/* * $Id$ * * SARL is an general-purpose agent programming language. * More details on http://www.sarl.io * * Copyright (C) 2014-2017 the original authors or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package io.sarl.lang.mwe2.externalspec.latex; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.Set; import com.google.common.base.Joiner; import com.google.common.io.Files; import com.google.inject.Injector; import com.ibm.icu.text.SimpleDateFormat; import org.eclipse.xtext.generator.IGeneratorFragment; import org.eclipse.xtext.util.Strings; import org.eclipse.xtext.xtext.generator.CodeConfig; import io.sarl.lang.mwe2.externalspec.AbstractExternalHighlightingFragment2; import io.sarl.lang.mwe2.externalspec.ExternalHighlightingConfig.Color; import io.sarl.lang.mwe2.externalspec.ExternalHighlightingConfig.ColorConfig; import io.sarl.lang.mwe2.externalspec.IStyleAppendable; /** * A {@link IGeneratorFragment} that create the language specification for * the LaTeX listings. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ public class LaTeXListingsGenerator2 extends AbstractExternalHighlightingFragment2<IStyleAppendable> { /** The default basename pattern for {@link MessageFormat}. */ public static final String BASENAME_PATTERN = "{0}-listing.sty"; //$NON-NLS-1$ /** Default definition for the basic style for floating algorithms (without colors). */ public static final String DEFAULT_FLOAT_BASIC_STYLE = "\\normalcolor\\smaller\\smaller"; //$NON-NLS-1$ /** Default definition for the basic style for floating algorithms (with colors). */ public static final String DEFAULT_COLORIZED_FLOAT_BASIC_STYLE = DEFAULT_FLOAT_BASIC_STYLE; /** Default definition for the basic style for inline code (without color). */ public static final String DEFAULT_INLINE_BASIC_STYLE = "\\normalcolor"; //$NON-NLS-1$ /** Default definition for the basic style for inline code (with colors). */ public static final String DEFAULT_COLORIZED_INLINE_BASIC_STYLE = DEFAULT_INLINE_BASIC_STYLE; /** Default definition for the identifier style (without color). */ public static final String DEFAULT_IDENTIFIER_STYLE = "\\ttfamily"; //$NON-NLS-1$ /** Default definition for the identifier style (with color). */ public static final String DEFAULT_COLORIZED_IDENTIFIER_STYLE = "\\color{SARLidentifier}" //$NON-NLS-1$ + DEFAULT_IDENTIFIER_STYLE; /** Default definition for the comment style (without color). */ public static final String DEFAULT_COMMENT_STYLE = "\\smaller\\bfseries"; //$NON-NLS-1$ /** Default definition for the identifier style (with color). */ public static final String DEFAULT_COLORIZED_COMMENT_STYLE = "\\color{SARLcomment}" //$NON-NLS-1$ + DEFAULT_COMMENT_STYLE; /** Default definition for the string style (without color). */ public static final String DEFAULT_STRING_STYLE = "\\ttfamily"; //$NON-NLS-1$ /** Default definition for the string style (with color). */ public static final String DEFAULT_COLORIZED_STRING_STYLE = "\\color{SARLstring}" //$NON-NLS-1$ + DEFAULT_STRING_STYLE; /** Default definition for the keyword style (without color). */ public static final String DEFAULT_KEYWORD_STYLE = "\\bfseries"; //$NON-NLS-1$ /** Default definition for the keyword style (with color). */ public static final String DEFAULT_COLORIZED_KEYWORD_STYLE = "\\color{SARLkeyword}" //$NON-NLS-1$ + DEFAULT_KEYWORD_STYLE; /** Default definition for the overridable TeX requirements. */ public static final String[] DEFAULT_REQUIREMENTS = new String[] { "relsize", //$NON-NLS-1$ }; private String floatBasicStyle; private String identifierStyle; private String commentStyle; private String stringStyle; private String keywordStyle; private String inlineBasicStyle; private boolean showLines = true; private int lineStep = 2; private int tabSize = 2; private boolean showSpecialCharacters; private Collection<String> requirements; @Override protected IStyleAppendable newStyleAppendable() { return new TeXAppendable(getCodeConfig(), getLanguageSimpleName(), getLanguageVersion()); } @Override public String toString() { return "LaTeX listings"; //$NON-NLS-1$ } @Override public void initialize(Injector injector) { super.initialize(injector); setBasenameTemplate(BASENAME_PATTERN); } /** Set the TeX style of the standard code in floats. * * @param style the TeX code that describes the style. If <code>null</code> * or empty, the default style is applied. */ public void setFloatBasicStyle(String style) { this.floatBasicStyle = style; } /** Set the TeX style of the inline standard code. * * @param style the TeX code that describes the style. If <code>null</code> * or empty, the default style is applied. */ public void setInlineBasicStyle(String style) { this.inlineBasicStyle = style; } /** Set the TeX style of the identifiers. * * @param style the TeX code that describes the style. If <code>null</code> * or empty, the default style is applied. */ public void setIdentifierStyle(String style) { this.identifierStyle = style; } /** Set the TeX style of the comments. * * @param style the TeX code that describes the style. If <code>null</code> * or empty, the default style is applied. */ public void setCommentStyle(String style) { this.commentStyle = style; } /** Set the TeX style of the strings of characters. * * @param style the TeX code that describes the style. If <code>null</code> * or empty, the default style is applied. */ public void setStringStyle(String style) { this.stringStyle = style; } /** Set the TeX style of the strings of keywords. * * @param style the TeX code that describes the style. If <code>null</code> * or empty, the default style is applied. */ public void setKeywordStyle(String style) { this.keywordStyle = style; } /** Set if the TeX style shows the line numbers. * * @param showLineNumbers <code>true</code> for showing the line numbers. */ public void setLineNumbers(boolean showLineNumbers) { this.showLines = showLineNumbers; } /** Set the step between two line numbers. * * @param step the step between two line numbers. */ public void setLineStep(int step) { this.lineStep = step; } /** Set the size of the tabs. * * @param size the size of one tab character. */ public void setTabSize(int size) { this.tabSize = size; } /** Set if the TeX style shows the special characters (including spaces). * * @param showSpecialChars <code>true</code> for showing the special characters. */ public void setShowSpecialChars(boolean showSpecialChars) { this.showSpecialCharacters = showSpecialChars; } /** Clear the list of the overridable requirements. */ public void clearRequirements() { if (this.requirements == null) { this.requirements = new ArrayList<>(); } else { this.requirements.clear(); } } /** Add a TeX requirement. * * @param requirement the name of the TeX package. */ public void addRequirement(String requirement) { if (!Strings.isEmpty(requirement)) { if (this.requirements == null) { this.requirements = new ArrayList<>(); } this.requirements.add(requirement); } } @SuppressWarnings({"checkstyle:parameternumber", "checkstyle:npathcomplexity", "checkstyle:cyclomaticcomplexity"}) @Override protected void generate(IStyleAppendable it, Set<String> literals, Set<String> expressionKeywords, Set<String> modifiers, Set<String> primitiveTypes, Set<String> punctuation, Set<String> ignored, Set<String> specialKeywords, Set<String> typeDeclarationKeywords) { final ColorConfig colors = getHighlightingConfig().getColors(); final Set<String> texKeywords = sortedConcat(expressionKeywords, modifiers, primitiveTypes, specialKeywords, typeDeclarationKeywords, literals); it.appendHeader(); final String basename = getBasename( MessageFormat.format(getBasenameTemplate(), getLanguageSimpleName().toLowerCase())); final String simpleBasename = Files.getNameWithoutExtension(basename); it.appendNl("\\NeedsTeXFormat{LaTeX2e}[1995/12/01]"); //$NON-NLS-1$ final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd"); //$NON-NLS-1$ it.appendNl("\\ProvidesPackage'{'{0}'}'[{1}]", simpleBasename, dateFormat.format(new Date())); //$NON-NLS-1$ it.appendNl("\\RequirePackage{algpseudocode}"); //$NON-NLS-1$ it.appendNl("\\RequirePackage{listings}"); //$NON-NLS-1$ it.appendNl("\\RequirePackage{xspace}"); //$NON-NLS-1$ Collection<String> requirements = this.requirements; if (requirements == null) { requirements = Arrays.asList(DEFAULT_REQUIREMENTS); } for (final String requirement : requirements) { it.appendNl("\\RequirePackage'{'{0}'}'", requirement); //$NON-NLS-1$ } if (getEnableColors()) { it.appendNl("\\RequirePackage{xcolor}"); //$NON-NLS-1$ } if (getEnableColors()) { for (final Color color : colors.getColors().values()) { it.appendNl("\\definecolor'{'{0}'}{'RGB'}{'{1},{2},{3}'}'", //$NON-NLS-1$ color.getName(), color.getRed(), color.getGreen(), color.getBlue()); } it.appendNl("\\colorlet'{'SARLcomment'}{'{0}'}'", colors.getCommentColor()); //$NON-NLS-1$ it.appendNl("\\colorlet'{'SARLstring'}{'{0}'}'", colors.getStringColor()); //$NON-NLS-1$ it.appendNl("\\colorlet'{'SARLkeyword'}{'{0}'}'", colors.getKeywordColor()); //$NON-NLS-1$ it.appendNl("\\colorlet'{'SARLidentifier'}{'{0}'}'", colors.getIdentifierColor()); //$NON-NLS-1$ } final String langName = getLanguageSimpleName().toUpperCase(); it.appendNl("\\lstdefinelanguage'{'{0}'}{'%", langName); //$NON-NLS-1$ it.appendNl(" morecomment=[l]{//},"); //$NON-NLS-1$ it.appendNl(" morecomment=[s]{/*}{*/},"); //$NON-NLS-1$ it.appendNl(" morestring=[b]\","); //$NON-NLS-1$ it.appendNl(" morekeywords='{'{0}'}',", Joiner.on(",").join(texKeywords)); //$NON-NLS-1$ //$NON-NLS-2$ it.appendNl("}"); //$NON-NLS-1$ it.appendNl("\\lstset{%"); //$NON-NLS-1$ String floatBasicStyle = this.floatBasicStyle; if (floatBasicStyle == null) { floatBasicStyle = (getEnableColors()) ? DEFAULT_COLORIZED_FLOAT_BASIC_STYLE : DEFAULT_FLOAT_BASIC_STYLE; } floatBasicStyle = Strings.emptyIfNull(floatBasicStyle); it.appendNl(" basicstyle={0}, % the size of the fonts that are used for the code", floatBasicStyle); //$NON-NLS-1$ it.appendNl(" breakatwhitespace=false, % sets if automatic breaks should only happen at whitespace"); //$NON-NLS-1$ it.appendNl(" breaklines=true, % sets automatic line breaking"); //$NON-NLS-1$ it.appendNl(" captionpos=b, % sets the caption-position to bottom"); //$NON-NLS-1$ it.appendNl(" deletekeywords={filter}, % if you want to delete keywords from the given language"); //$NON-NLS-1$ it.appendNl(" escapeinside={(*@}{@*)}, % if you want to add LaTeX within your code"); //$NON-NLS-1$ it.append(" extendedchars=true, % lets you use non-ASCII characters; for 8-bits "); //$NON-NLS-1$ it.appendNl("encodings only, does not work with UTF-8"); //$NON-NLS-1$ it.appendNl(" frame=none, % no frame around the code"); //$NON-NLS-1$ it.append(" keepspaces=true, % keeps spaces in text, useful for keeping "); //$NON-NLS-1$ it.appendNl("indentation of code (possibly needs columns=flexible)"); //$NON-NLS-1$ String identifierStyle = this.identifierStyle; if (identifierStyle == null) { identifierStyle = (getEnableColors()) ? DEFAULT_COLORIZED_IDENTIFIER_STYLE : DEFAULT_IDENTIFIER_STYLE; } identifierStyle = Strings.emptyIfNull(identifierStyle); it.appendNl(" identifierstyle={0},", identifierStyle); //$NON-NLS-1$ String commentStyle = this.commentStyle; if (commentStyle == null) { commentStyle = (getEnableColors()) ? DEFAULT_COLORIZED_COMMENT_STYLE : DEFAULT_COMMENT_STYLE; } commentStyle = Strings.emptyIfNull(commentStyle); it.appendNl(" commentstyle={0},", commentStyle); //$NON-NLS-1$ String stringStyle = this.stringStyle; if (stringStyle == null) { stringStyle = (getEnableColors()) ? DEFAULT_COLORIZED_STRING_STYLE : DEFAULT_STRING_STYLE; } stringStyle = Strings.emptyIfNull(stringStyle); it.appendNl(" stringstyle={0},", stringStyle); //$NON-NLS-1$ String keywordStyle = this.keywordStyle; if (keywordStyle == null) { keywordStyle = (getEnableColors()) ? DEFAULT_COLORIZED_KEYWORD_STYLE : DEFAULT_KEYWORD_STYLE; } keywordStyle = Strings.emptyIfNull(keywordStyle); it.appendNl(" keywordstyle={0}, % keyword style", keywordStyle); //$NON-NLS-1$ it.appendNl(" language={0}, % the default language of the code", langName); //$NON-NLS-1$ it.append(" showspaces={0}, % show spaces everywhere adding particular ", this.showSpecialCharacters); //$NON-NLS-1$ it.appendNl("underscores; it overrides 'showstringspaces'"); //$NON-NLS-1$ it.appendNl(" showstringspaces={0}, % underline spaces within strings only", //$NON-NLS-1$ this.showSpecialCharacters); it.appendNl(" showtabs={0}, % show tabs within strings adding particular underscores", //$NON-NLS-1$ this.showSpecialCharacters); if (this.showLines) { it.appendNl(" numbers=left,% Numbers on left"); //$NON-NLS-1$ it.appendNl(" firstnumber=1, % First line number"); //$NON-NLS-1$ it.appendNl(" numberfirstline=false, %Start numbers at first line"); //$NON-NLS-1$ it.append(" stepnumber={0}, % the step between two line-numbers. ", this.lineStep); //$NON-NLS-1$ it.appendNl("If it's 1, each line will be numbered"); //$NON-NLS-1$ } it.appendNl(" tabsize={0}, % sets default tabsize to 2 spaces", this.tabSize); //$NON-NLS-1$ it.append(" title=\\lstname, % show the filename of files included with "); //$NON-NLS-1$ it.appendNl("\\lstinputlisting; also try caption instead of title"); //$NON-NLS-1$ it.appendNl(" frameround=fttt, % If framed, use this rounded corner style"); //$NON-NLS-1$ it.appendNl("}"); //$NON-NLS-1$ String inlineBasicStyle = this.inlineBasicStyle; if (inlineBasicStyle == null) { inlineBasicStyle = (getEnableColors()) ? DEFAULT_COLORIZED_INLINE_BASIC_STYLE : DEFAULT_INLINE_BASIC_STYLE; } inlineBasicStyle = Strings.emptyIfNull(inlineBasicStyle); it.append("\\newcommand{\\code}[1]{"); //$NON-NLS-1$ it.append("\\ifmmode\\text'{'\\lstinline[basicstyle={0}]'{'#1'}}'", inlineBasicStyle); //$NON-NLS-1$ it.append("\\else\\lstinline[basicstyle={0}]'{'#1'}'", inlineBasicStyle); //$NON-NLS-1$ it.appendNl("\\fi}"); //$NON-NLS-1$ it.appendNl("\\newcommand{\\sarl}{\\mbox{SARL}\\xspace}"); //$NON-NLS-1$ it.appendNl("\\newcommand'{'\\sarlversion'}{'{0}'}'", getLanguageVersion()); //$NON-NLS-1$ it.appendNl("\\endinput"); //$NON-NLS-1$ } /** Appendable for tex-based styles. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @since 0.6 */ protected static class TeXAppendable extends AbstractAppendable { /** Constructor. * * @param codeConfig the code configuration. * @param languageName the language name. * @param languageVersion the language version. */ protected TeXAppendable(CodeConfig codeConfig, String languageName, String languageVersion) { super(codeConfig, languageName, languageVersion); } @Override public void appendComment(String text, Object... parameters) { final String comment = applyFormat(text, parameters); for (final String line : comment.split("[\n\r]")) { //$NON-NLS-1$ appendNl("% " + line.trim()); //$NON-NLS-1$ } } @Override public void appendHeader() { final String[] header = Strings.emptyIfNull(getCodeConfig().getFileHeader()).split("[\n\r]+"); //$NON-NLS-1$ for (final String headerLine : header) { appendNl(headerLine.replaceFirst("^\\s*[/]?[*][/]?", "%")); //$NON-NLS-1$//$NON-NLS-2$ } } } }