From 9ebe916fac395caf8c9fce4866084947053d612f Mon Sep 17 00:00:00 2001 From: Federico Fissore Date: Mon, 27 Apr 2015 17:23:44 +0200 Subject: [PATCH] Restoring keyword.txt loading and parsing. Added token type identifiers and related style in theme.txt --- app/src/processing/app/Base.java | 10 + app/src/processing/app/Editor.java | 8 +- app/src/processing/app/Theme.java | 62 +++-- .../app/syntax/ArduinoTokenMakerFactory.java | 25 ++ .../processing/app/syntax/PdeKeywords.java | 199 +++++++------- .../processing/app/syntax/SketchTextArea.java | 254 ++++++++++-------- .../app/syntax/SketchTokenMaker.java | 113 +------- .../app/AbstractWithPreferencesTest.java | 2 + .../app/syntax/PdeKeywordsTest.java | 30 +++ build/shared/lib/keywords.txt | 158 +++++------ build/shared/lib/theme/syntax/default.xml | 16 +- build/shared/lib/theme/theme.txt | 7 + 12 files changed, 474 insertions(+), 410 deletions(-) create mode 100644 app/src/processing/app/syntax/ArduinoTokenMakerFactory.java create mode 100644 app/test/processing/app/syntax/PdeKeywordsTest.java diff --git a/app/src/processing/app/Base.java b/app/src/processing/app/Base.java index a0b76d4dd..fecf58c83 100644 --- a/app/src/processing/app/Base.java +++ b/app/src/processing/app/Base.java @@ -53,6 +53,7 @@ import processing.app.legacy.PApplet; import processing.app.macosx.ThinkDifferent; import processing.app.packages.LibraryList; import processing.app.packages.UserLibrary; +import processing.app.syntax.PdeKeywords; import processing.app.tools.MenuScroller; import processing.app.tools.ZipDeflater; @@ -118,6 +119,8 @@ public class Base { private List boardsCustomMenus; private volatile Action openBoardsManager; + private final PdeKeywords pdeKeywords; + static public void main(String args[]) throws Exception { System.setProperty("awt.useSystemAAFontSettings", "on"); System.setProperty("swing.aatext", "true"); @@ -345,6 +348,9 @@ public class Base { // them. PreferencesData.save(); + this.pdeKeywords = new PdeKeywords(); + this.pdeKeywords.reload(); + if (parser.isInstallBoard()) { ContributionsIndexer indexer = new ContributionsIndexer(BaseNoGui.getSettingsFolder()); ContributionInstaller installer = new ContributionInstaller(indexer) { @@ -2771,4 +2777,8 @@ public class Base { public Action getOpenBoardsManager() { return openBoardsManager; } + + public PdeKeywords getPdeKeywords() { + return pdeKeywords; + } } diff --git a/app/src/processing/app/Editor.java b/app/src/processing/app/Editor.java index 71e7fe4d4..3c9d5d59a 100644 --- a/app/src/processing/app/Editor.java +++ b/app/src/processing/app/Editor.java @@ -958,7 +958,7 @@ public class Editor extends JFrame implements RunnerListener { protected SketchTextArea createTextArea() throws IOException { - SketchTextArea textArea = new SketchTextArea(); + SketchTextArea textArea = new SketchTextArea(base.getPdeKeywords()); textArea.requestFocusInWindow(); textArea.setMarkOccurrences(true); textArea.setMarginLineEnabled(false); @@ -1757,7 +1757,7 @@ public class Editor extends JFrame implements RunnerListener { RSyntaxDocument document = (RSyntaxDocument) codeDoc.getDocument(); if (document == null) { // this document not yet inited - document = new RSyntaxDocument(RSyntaxDocument.SYNTAX_STYLE_CPLUSPLUS); + document = new RSyntaxDocument(new ArduinoTokenMakerFactory(base.getPdeKeywords()), RSyntaxDocument.SYNTAX_STYLE_CPLUSPLUS); document.putProperty(PlainDocument.tabSizeAttribute, Preferences.getInteger("editor.tabs.size")); // insert the program text into the document object @@ -1953,7 +1953,7 @@ public class Editor extends JFrame implements RunnerListener { protected void handleFindReference() { String text = getCurrentKeyword(); - String referenceFile = PdeKeywords.getReference(text); + String referenceFile = base.getPdeKeywords().getReference(text); if (referenceFile == null) { statusNotice(I18n.format(_("No reference available for \"{0}\""), text)); } else { @@ -2897,7 +2897,7 @@ public class Editor extends JFrame implements RunnerListener { @Override public void popupMenuWillBecomeVisible(PopupMenuEvent e) { - String referenceFile = PdeKeywords.getReference(getCurrentKeyword()); + String referenceFile = base.getPdeKeywords().getReference(getCurrentKeyword()); referenceItem.setEnabled(referenceFile != null); int offset = textarea.getCaretPosition(); diff --git a/app/src/processing/app/Theme.java b/app/src/processing/app/Theme.java index 9ad4f2aef..841880d9b 100644 --- a/app/src/processing/app/Theme.java +++ b/app/src/processing/app/Theme.java @@ -21,19 +21,20 @@ package processing.app; -import static processing.app.I18n._; - -import java.awt.Color; -import java.awt.Font; -import java.awt.SystemColor; -import java.io.File; - -import javax.swing.text.StyleContext; - import processing.app.helpers.OSUtils; import processing.app.helpers.PreferencesHelper; import processing.app.helpers.PreferencesMap; +import javax.swing.text.StyleContext; +import java.awt.*; +import java.awt.font.TextAttribute; +import java.io.File; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Map; + +import static processing.app.I18n._; + /** * Storage class for theme settings. This was separated from the Preferences * class for 1.0 so that the coloring wouldn't conflict with previous releases @@ -41,9 +42,13 @@ import processing.app.helpers.PreferencesMap; */ public class Theme { - /** Copy of the defaults in case the user mangles a preference. */ + /** + * Copy of the defaults in case the user mangles a preference. + */ static PreferencesMap defaults; - /** Table of attributes/values for the theme. */ + /** + * Table of attributes/values for the theme. + */ static PreferencesMap table = new PreferencesMap(); static protected void init() { @@ -51,7 +56,7 @@ public class Theme { table.load(new File(BaseNoGui.getContentFile("lib"), "theme/theme.txt")); } catch (Exception te) { Base.showError(null, _("Could not read color theme settings.\n" + - "You'll need to reinstall Arduino."), te); + "You'll need to reinstall Arduino."), te); } // other things that have to be set explicitly for the defaults @@ -106,8 +111,8 @@ public class Theme { } return font; } - - /** + + /** * Returns the default font for text areas. * * @return The default font. @@ -129,8 +134,7 @@ public class Theme { font = sc.getFont("Monospaced", Font.PLAIN, 13); } } - } - else { + } else { // Consolas added in Vista, used by VS2010+. font = sc.getFont("Consolas", Font.PLAIN, 13); if (!"Consolas".equals(font.getFamily())) { @@ -140,6 +144,30 @@ public class Theme { //System.out.println(font.getFamily() + ", " + font.getName()); return font; - } + + public static Map getStyledFont(String what, Font font) { + String split[] = get("editor." + what + ".style").split(","); + + Color color = PreferencesHelper.parseColor(split[0]); + + String style = split[1]; + boolean bold = style.contains("bold"); + boolean italic = style.contains("italic"); + boolean underlined = style.contains("underlined"); + + Font styledFont = new Font(font.getFamily(), (bold ? Font.BOLD : 0) | (italic ? Font.ITALIC : 0), font.getSize()); + if (underlined) { + Map attr = new Hashtable(); + attr.put(TextAttribute.UNDERLINE, TextAttribute.UNDERLINE_ON); + styledFont = styledFont.deriveFont(attr); + } + + Map result = new HashMap(); + result.put("color", color); + result.put("font", styledFont); + + return result; + } + } diff --git a/app/src/processing/app/syntax/ArduinoTokenMakerFactory.java b/app/src/processing/app/syntax/ArduinoTokenMakerFactory.java new file mode 100644 index 000000000..e1728cb62 --- /dev/null +++ b/app/src/processing/app/syntax/ArduinoTokenMakerFactory.java @@ -0,0 +1,25 @@ +package processing.app.syntax; + +import org.fife.ui.rsyntaxtextarea.AbstractTokenMakerFactory; +import org.fife.ui.rsyntaxtextarea.RSyntaxDocument; +import org.fife.ui.rsyntaxtextarea.TokenMaker; + +public class ArduinoTokenMakerFactory extends AbstractTokenMakerFactory { + + private final PdeKeywords pdeKeywords; + + public ArduinoTokenMakerFactory(PdeKeywords pdeKeywords) { + this.pdeKeywords = pdeKeywords; + } + + @Override + protected TokenMaker getTokenMakerImpl(String key) { + return new SketchTokenMaker(pdeKeywords); + } + + @Override + protected void initTokenMakerMap() { + putMapping(RSyntaxDocument.SYNTAX_STYLE_CPLUSPLUS, SketchTokenMaker.class.getName()); + } + +} diff --git a/app/src/processing/app/syntax/PdeKeywords.java b/app/src/processing/app/syntax/PdeKeywords.java index d3823802a..3b5a575ce 100644 --- a/app/src/processing/app/syntax/PdeKeywords.java +++ b/app/src/processing/app/syntax/PdeKeywords.java @@ -25,6 +25,7 @@ package processing.app.syntax; import cc.arduino.contributions.libraries.ContributedLibrary; +import org.fife.ui.rsyntaxtextarea.TokenMap; import org.fife.ui.rsyntaxtextarea.TokenTypes; import processing.app.Base; import processing.app.BaseNoGui; @@ -35,130 +36,146 @@ import java.io.File; import java.io.FileInputStream; import java.io.InputStreamReader; import java.util.HashMap; +import java.util.Map; +import java.util.regex.Pattern; public class PdeKeywords { - // Value is org.fife.ui.rsyntaxtextarea.TokenTypes - private static HashMap keywords = new HashMap(); + private static final Map KNOWN_TOKEN_TYPES = new HashMap(); + private static final Pattern ALPHA = Pattern.compile("\\w"); - private static HashMap keywordToReference = new HashMap(); - - public static HashMap reload() { - keywords.clear(); - keywordToReference.clear(); - return get(); + static { + KNOWN_TOKEN_TYPES.put("RESERVED_WORD", TokenTypes.RESERVED_WORD); + KNOWN_TOKEN_TYPES.put("RESERVED_WORD_2", TokenTypes.RESERVED_WORD_2); + KNOWN_TOKEN_TYPES.put("VARIABLE", TokenTypes.VARIABLE); + KNOWN_TOKEN_TYPES.put("OPERATOR", TokenTypes.OPERATOR); + KNOWN_TOKEN_TYPES.put("DATA_TYPE", TokenTypes.DATA_TYPE); + KNOWN_TOKEN_TYPES.put("LITERAL_BOOLEAN", TokenTypes.LITERAL_BOOLEAN); + KNOWN_TOKEN_TYPES.put("LITERAL_CHAR", TokenTypes.LITERAL_CHAR); } - public static HashMap get() { + // lookup table for the TokenMarker subclass, handles coloring + private final TokenMap keywordTokenType; + private final Map keywordOldToken; + private final Map keywordTokenTypeAsString; - if (keywords.isEmpty()) { - try { - load(new File(BaseNoGui.getContentFile("lib"), "keywords.txt")); - if (Base.getLibraries() != null) { - for (ContributedLibrary lib : Base.getLibraries()) { - File keywords = new File(lib.getInstalledFolder(), "keywords.txt"); - if (keywords.exists()) load(keywords); - } + // lookup table that maps keywords to their html reference pages + private final Map keywordToReference; + + public PdeKeywords() { + this.keywordTokenType = new TokenMap(); + this.keywordOldToken = new HashMap(); + this.keywordTokenTypeAsString = new HashMap(); + this.keywordToReference = new HashMap(); + } + + /** + * Handles loading of keywords file. + *

+ * Uses getKeywords() method because that's part of the + * TokenMarker classes. + *

+ * It is recommended that a # sign be used for comments + * inside keywords.txt. + */ + public void reload() { + try { + parseKeywordsTxt(new File(BaseNoGui.getContentFile("lib"), "keywords.txt")); + for (ContributedLibrary lib : Base.getLibraries()) { + File keywords = new File(lib.getInstalledFolder(), "keywords.txt"); + if (keywords.exists()) { + parseKeywordsTxt(keywords); } - } catch (Exception e) { - Base.showError("Problem loading keywords", - "Could not load keywords.txt,\n" + - "please re-install Arduino.", e); - System.exit(1); } + } catch (Exception e) { + Base.showError("Problem loading keywords", "Could not load keywords.txt,\nplease re-install Arduino.", e); + System.exit(1); } - - return keywords; } - static private void load(File input) throws Exception { + private void parseKeywordsTxt(File input) throws Exception { BufferedReader reader = null; try { reader = new BufferedReader(new InputStreamReader(new FileInputStream(input))); - String line = null; + String line; while ((line = reader.readLine()) != null) { //System.out.println("line is " + line); // in case there's any garbage on the line - //if (line.trim().length() == 0) continue; + line = line.trim(); + if (line.length() == 0 || line.startsWith("#")) { + continue; + } String pieces[] = PApplet.split(line, '\t'); + + String keyword = pieces[0].trim(); + if (pieces.length >= 2) { - //int tab = line.indexOf('\t'); - // any line with no tab is ignored - // meaning that a comment is any line without a tab - //if (tab == -1) continue; + keywordOldToken.put(keyword, pieces[1]); + } - String keyword = pieces[0].trim(); - //String keyword = line.substring(0, tab).trim(); - //String second = line.substring(tab + 1); - //tab = second.indexOf('\t'); - //String coloring = second.substring(0, tab).trim(); - //String htmlFilename = second.substring(tab + 1).trim(); - String coloring = pieces[1].trim(); - - if (coloring.length() > 0 && Character.isDigit(coloring.charAt(coloring.length() - 1))) { - // text will be KEYWORD or LITERAL - boolean isKey = (coloring.charAt(0) == 'K'); - // KEYWORD1 -> 0, KEYWORD2 -> 1, etc - int num = coloring.charAt(coloring.length() - 1) - '1'; - byte id = (byte) - ((isKey ? Token.KEYWORD1 : Token.LITERAL1) + num); - //System.out.println("got " + (isKey ? "keyword" : "literal") + - // (num+1) + " for " + keyword); - - int tokenType = TokenTypes.IDENTIFIER; - - // KEYWORD1 Classes, datatypes, and C++ keywords - // KEYWORD2 Methods and functions - // KEYWORD3 setup and loop functions, as well as the Serial keywords - // LITERAL1 Constants - // LITERAL2 Built-in variables (INPUT,OUTPUT,CHANGE,FALLING) - - switch (id) { - case Token.KEYWORD1: - tokenType = TokenTypes.VARIABLE; - break; - case Token.KEYWORD2: - tokenType = TokenTypes.FUNCTION; - break; - case Token.KEYWORD3: - tokenType = TokenTypes.RESERVED_WORD; - break; - case Token.LITERAL1: - tokenType = TokenTypes.PREPROCESSOR; - break; - case Token.LITERAL2: - tokenType = TokenTypes.RESERVED_WORD_2; - break; - default: - break; - } - - if ("true".equals(keyword) || "false".equals(keyword)) { - tokenType = TokenTypes.LITERAL_BOOLEAN; - } - - keywords.put(keyword, tokenType); - } - if (pieces.length >= 3) { - String htmlFilename = pieces[2].trim(); - if (htmlFilename.length() > 0) { - keywordToReference.put(keyword, htmlFilename); - } - } + if (pieces.length >= 3) { + parseHTMLReferenceFileName(pieces[2], keyword); + } + if (pieces.length >= 4) { + parseRSyntaxTextAreaTokenType(pieces[3], keyword); } } + + fillMissingTokenType(); } finally { if (reader != null) { reader.close(); } } + } - public static String getReference(String keyword) { - if (keywordToReference == null) return null; + private void fillMissingTokenType() { + for (Map.Entry oldTokenEntry : keywordOldToken.entrySet()) { + String keyword = oldTokenEntry.getKey(); + if (!keywordTokenTypeAsString.containsKey(keyword)) { + if ("KEYWORD1".equals(oldTokenEntry.getValue())) { + parseRSyntaxTextAreaTokenType("DATA_TYPE", keyword); + } else { + parseRSyntaxTextAreaTokenType("FUNCTION", keyword); + } + } + } + } + + private void parseRSyntaxTextAreaTokenType(String tokenTypeAsString, String keyword) { + if (!ALPHA.matcher(keyword).find()) { + return; + } + + if (KNOWN_TOKEN_TYPES.containsKey(tokenTypeAsString)) { + keywordTokenType.put(keyword, KNOWN_TOKEN_TYPES.get(tokenTypeAsString)); + keywordTokenTypeAsString.put(keyword, tokenTypeAsString); + } else { + keywordTokenType.put(keyword, TokenTypes.FUNCTION); + keywordTokenTypeAsString.put(keyword, "FUNCTION"); + } + } + + private void parseHTMLReferenceFileName(String piece, String keyword) { + String htmlFilename = piece.trim(); + if (htmlFilename.length() > 0) { + keywordToReference.put(keyword, htmlFilename); + } + } + + public String getReference(String keyword) { return keywordToReference.get(keyword); } + + public String getTokenTypeAsString(String keyword) { + return keywordTokenTypeAsString.get(keyword); + } + + public int getTokenType(char[] array, int start, int end) { + return keywordTokenType.get(array, start, end); + } } diff --git a/app/src/processing/app/syntax/SketchTextArea.java b/app/src/processing/app/syntax/SketchTextArea.java index c2243c25c..8d5381cbe 100644 --- a/app/src/processing/app/syntax/SketchTextArea.java +++ b/app/src/processing/app/syntax/SketchTextArea.java @@ -29,66 +29,67 @@ package processing.app.syntax; -import java.awt.AWTKeyStroke; -import java.awt.KeyboardFocusManager; -import java.awt.event.KeyEvent; -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.util.HashSet; -import java.util.Set; -import java.util.logging.Logger; - -import javax.swing.JPopupMenu; -import javax.swing.KeyStroke; -import javax.swing.event.HyperlinkEvent; -import javax.swing.text.BadLocationException; -import javax.swing.text.Document; -import javax.swing.text.Segment; -import javax.swing.undo.UndoManager; - import org.fife.ui.rsyntaxtextarea.*; import org.fife.ui.rsyntaxtextarea.Theme; import org.fife.ui.rsyntaxtextarea.Token; import org.fife.ui.rsyntaxtextarea.focusabletip.FocusableTip; import org.fife.ui.rtextarea.RUndoManager; - import processing.app.*; +import javax.swing.*; +import javax.swing.event.HyperlinkEvent; +import javax.swing.text.BadLocationException; +import javax.swing.text.Document; +import javax.swing.text.Segment; +import javax.swing.undo.UndoManager; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.logging.Logger; + /** * Arduino Sketch code editor based on RSyntaxTextArea (http://fifesoft.com/rsyntaxtextarea) - * @author Ricardo JL Rufino (ricardo@criativasoft.com.br) + * + * @author Ricardo JL Rufino (ricardo@criativasoft.com.br) * @date 20/04/2015 * @since 1.6.4 */ public class SketchTextArea extends RSyntaxTextArea { - + private final static Logger LOG = Logger.getLogger(SketchTextArea.class.getName()); - - /** The last docTooltip displayed. */ + + /** + * The last docTooltip displayed. + */ private FocusableTip docTooltip; - + /** * The component that tracks the current line number. */ protected EditorLineStatus editorLineStatus; private EditorListener editorListener; - - public SketchTextArea() throws IOException { - super(); + + private final PdeKeywords pdeKeywords; + + public SketchTextArea(PdeKeywords pdeKeywords) throws IOException { + this.pdeKeywords = pdeKeywords; installFeatures(); } - - - protected void installFeatures() throws IOException { + protected void installFeatures() throws IOException { setTheme(PreferencesData.get("editor.syntax_theme", "default")); - setLinkGenerator(new DocLinkGenerator()); - + setLinkGenerator(new DocLinkGenerator(pdeKeywords)); + fixControlTab(); - installTokenMaker(); + + setSyntaxEditingStyle(SYNTAX_STYLE_CPLUSPLUS); } public void setTheme(String name) throws IOException { @@ -102,93 +103,118 @@ public class SketchTextArea extends RSyntaxTextArea { defaultXmlInputStream.close(); } } + + setForeground(processing.app.Theme.getColor("editor.fgcolor")); + setBackground(processing.app.Theme.getColor("editor.bgcolor")); + setCurrentLineHighlightColor(processing.app.Theme.getColor("editor.linehighlight.color")); + setCaretColor(processing.app.Theme.getColor("editor.caret.color")); + setSelectedTextColor(null); + setUseSelectedTextColor(false); + setSelectionColor(processing.app.Theme.getColor("editor.selection.color")); + setMatchedBracketBorderColor(processing.app.Theme.getColor("editor.brackethighlight.color")); + + setSyntaxTheme(TokenTypes.DATA_TYPE, "data_type"); + setSyntaxTheme(TokenTypes.FUNCTION, "function"); + setSyntaxTheme(TokenTypes.RESERVED_WORD, "reserved_word"); + setSyntaxTheme(TokenTypes.RESERVED_WORD_2, "reserved_word_2"); + setSyntaxTheme(TokenTypes.VARIABLE, "variable"); + setSyntaxTheme(TokenTypes.OPERATOR, "operator"); + setSyntaxTheme(TokenTypes.COMMENT_DOCUMENTATION, "comment1"); + setSyntaxTheme(TokenTypes.COMMENT_EOL, "comment1"); + setSyntaxTheme(TokenTypes.COMMENT_KEYWORD, "comment1"); + setSyntaxTheme(TokenTypes.COMMENT_MARKUP, "comment1"); + setSyntaxTheme(TokenTypes.LITERAL_CHAR, "literal_char"); + setSyntaxTheme(TokenTypes.LITERAL_STRING_DOUBLE_QUOTE, "literal_string_double_quote"); + } + + private void setSyntaxTheme(int tokenType, String id) { + Style style = getSyntaxScheme().getStyle(tokenType); + + Map styledFont = processing.app.Theme.getStyledFont(id, style.font); + style.foreground = (Color) styledFont.get("color"); + style.font = (Font) styledFont.get("font"); + + getSyntaxScheme().setStyle(tokenType, style); } // Removing the default focus traversal keys // This is because the DefaultKeyboardFocusManager handles the keypress and consumes the event - protected void fixControlTab(){ + protected void fixControlTab() { KeyStroke ctrlTab = KeyStroke.getKeyStroke("ctrl TAB"); KeyStroke ctrlShiftTab = KeyStroke.getKeyStroke("ctrl shift TAB"); - + // Remove ctrl-tab from normal focus traversal Set forwardKeys = new HashSet(this.getFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS)); forwardKeys.remove(ctrlTab); this.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, forwardKeys); - + // Remove ctrl-shift-tab from normal focus traversal Set backwardKeys = new HashSet(this.getFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS)); backwardKeys.remove(ctrlShiftTab); } - public void setEditorLineStatus(EditorLineStatus editorLineStatus) { this.editorLineStatus = editorLineStatus; } - + @Override public void select(int selectionStart, int selectionEnd) { super.select(selectionStart, selectionEnd); - if(editorLineStatus != null) editorLineStatus.set(selectionStart, selectionEnd); + if (editorLineStatus != null) editorLineStatus.set(selectionStart, selectionEnd); } public boolean isSelectionActive() { return this.getSelectedText() != null; } - - public void setSelectedText(String text){ - + + public void setSelectedText(String text) { + int old = getTextMode(); setTextMode(OVERWRITE_MODE); replaceSelection(text); setTextMode(old); - + } - protected void installTokenMaker(){ - AbstractTokenMakerFactory atmf = (AbstractTokenMakerFactory)TokenMakerFactory.getDefaultInstance(); - atmf.putMapping(SYNTAX_STYLE_CPLUSPLUS, "processing.app.syntax.SketchTokenMaker"); - setSyntaxEditingStyle(SYNTAX_STYLE_CPLUSPLUS); - } - public void processKeyEvent(KeyEvent evt) { - + // this had to be added because the menu key events weren't making it up to the frame. - - switch(evt.getID()) { - case KeyEvent.KEY_TYPED: - if (editorListener != null) editorListener.keyTyped(evt); - break; - case KeyEvent.KEY_PRESSED: - if (editorListener != null) editorListener.keyPressed(evt); - break; - case KeyEvent.KEY_RELEASED: - // inputHandler.keyReleased(evt); - break; + + switch (evt.getID()) { + case KeyEvent.KEY_TYPED: + if (editorListener != null) editorListener.keyTyped(evt); + break; + case KeyEvent.KEY_PRESSED: + if (editorListener != null) editorListener.keyPressed(evt); + break; + case KeyEvent.KEY_RELEASED: + // inputHandler.keyReleased(evt); + break; } - - if(!evt.isConsumed()){ - super.processKeyEvent(evt); + + if (!evt.isConsumed()) { + super.processKeyEvent(evt); } } - + public void switchDocument(Document document, UndoManager newUndo) { - + // HACK: Dont discard changes on curret UndoManager. // BUG: https://github.com/bobbylight/RSyntaxTextArea/issues/84 setUndoManager(null); // bypass reset current undo manager... - + super.setDocument(document); - + setUndoManager((RUndoManager) newUndo); - + // HACK: Complement previous hack (hide code folding on switch) | Drawback: Lose folding state // if(sketch.getCodeCount() > 1 && textarea.isCodeFoldingEnabled()){ // textarea.setCodeFoldingEnabled(false); // textarea.setCodeFoldingEnabled(true); // } - - + + } @Override @@ -196,12 +222,12 @@ public class SketchTextArea extends RSyntaxTextArea { JPopupMenu menu = super.createPopupMenu(); return menu; } - + @Override protected void configurePopupMenu(JPopupMenu popupMenu) { super.configurePopupMenu(popupMenu); } - + public void getTextLine(int line, Segment segment) { try { int offset = getLineStartOffset(line); @@ -210,7 +236,7 @@ public class SketchTextArea extends RSyntaxTextArea { } catch (BadLocationException e) { } } - + public String getTextLine(int line) { try { int offset = getLineStartOffset(line); @@ -220,48 +246,54 @@ public class SketchTextArea extends RSyntaxTextArea { return null; } } - + public void setEditorListener(EditorListener editorListener) { this.editorListener = editorListener; } - - private static class DocLinkGenerator implements LinkGenerator{ - - @Override - public LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, final int offs) { - - final Token token = textArea.modelToToken(offs); - - final String reference = PdeKeywords.getReference(token.getLexeme()); - // LOG.fine("reference: " + reference + ", match: " + (token.getType() == TokenTypes.DATA_TYPE || token.getType() == TokenTypes.VARIABLE || token.getType() == TokenTypes.FUNCTION)); - - if(token != null && (reference != null || (token.getType() == TokenTypes.DATA_TYPE || token.getType() == TokenTypes.VARIABLE || token.getType() == TokenTypes.FUNCTION) )){ - - LinkGeneratorResult generatorResult = new LinkGeneratorResult() { - - @Override - public int getSourceOffset() { - return offs; - } - - @Override - public HyperlinkEvent execute() { - - LOG.fine("Open Reference: " + reference); - - Base.showReference("Reference/" + reference); - - return null; - } - }; - - return generatorResult; - } - - return null; + private static class DocLinkGenerator implements LinkGenerator { + + private final PdeKeywords pdeKeywords; + + public DocLinkGenerator(PdeKeywords pdeKeywords) { + this.pdeKeywords = pdeKeywords; + } + + @Override + public LinkGeneratorResult isLinkAtOffset(RSyntaxTextArea textArea, final int offs) { + + final Token token = textArea.modelToToken(offs); + + final String reference = pdeKeywords.getReference(token.getLexeme()); + + // LOG.fine("reference: " + reference + ", match: " + (token.getType() == TokenTypes.DATA_TYPE || token.getType() == TokenTypes.VARIABLE || token.getType() == TokenTypes.FUNCTION)); + + if (token != null && (reference != null || (token.getType() == TokenTypes.DATA_TYPE || token.getType() == TokenTypes.VARIABLE || token.getType() == TokenTypes.FUNCTION))) { + + LinkGeneratorResult generatorResult = new LinkGeneratorResult() { + + @Override + public int getSourceOffset() { + return offs; + } + + @Override + public HyperlinkEvent execute() { + + LOG.fine("Open Reference: " + reference); + + Base.showReference("Reference/" + reference); + + return null; + } + }; + + return generatorResult; } - }; + + return null; + } + } } diff --git a/app/src/processing/app/syntax/SketchTokenMaker.java b/app/src/processing/app/syntax/SketchTokenMaker.java index f04c8bac4..6e46f9a68 100644 --- a/app/src/processing/app/syntax/SketchTokenMaker.java +++ b/app/src/processing/app/syntax/SketchTokenMaker.java @@ -29,120 +29,33 @@ package processing.app.syntax; -import java.util.HashMap; -import java.util.Set; - -import org.fife.ui.rsyntaxtextarea.TokenMap; -import org.fife.ui.rsyntaxtextarea.TokenTypes; import org.fife.ui.rsyntaxtextarea.modes.CPlusPlusTokenMaker; -import processing.app.Base; +import java.util.Arrays; /** * Controls the syntax highlighting of {@link SketchTextArea} based on the {@link PdeKeywords} + * * @author Ricardo JL Rufino (ricardo@criativasoft.com.br) * @date 20/04/2015 - * @since 1.6.4 + * @since 1.6.4 */ public class SketchTokenMaker extends CPlusPlusTokenMaker { - - static TokenMap extraTokens; - public SketchTokenMaker() { - extraTokens = getKeywords(); + private final PdeKeywords pdeKeywords; + + public SketchTokenMaker(PdeKeywords pdeKeywords) { + this.pdeKeywords = pdeKeywords; } @Override public void addToken(char[] array, int start, int end, int tokenType, int startOffset, boolean hyperlink) { - // This assumes all of your extra tokens would normally be scanned as IDENTIFIER. - if (tokenType == TokenTypes.IDENTIFIER || tokenType == TokenTypes.DATA_TYPE) { - int newType = extraTokens.get(array, start, end); - if (newType>-1) { - tokenType = newType; - } - } - super.addToken(array, start, end, tokenType, startOffset, hyperlink); - } - - public static void addKeyword(String keyword, int type) { - extraTokens.put(keyword, type); - } - - public void clear() { - extraTokens = new TokenMap(); - } - - - /** - * Handles loading of keywords file. - *

- * It is recommended that a # sign be used for comments - * inside keywords.txt. - */ - static public TokenMap getKeywords() { - if (extraTokens == null) { - try { - extraTokens = new TokenMap(false); - - extraTokens.put("setup", TokenTypes.RESERVED_WORD); - extraTokens.put("loop", TokenTypes.RESERVED_WORD); - - extraTokens.put("HIGH", TokenTypes.RESERVED_WORD_2); - extraTokens.put("LOW", TokenTypes.RESERVED_WORD_2); - extraTokens.put("OUTPUT", TokenTypes.RESERVED_WORD_2); - extraTokens.put("INPUT", TokenTypes.RESERVED_WORD_2); - extraTokens.put("INPUT_PULLUP", TokenTypes.RESERVED_WORD_2); - - extraTokens.put("CHANGE", TokenTypes.RESERVED_WORD_2); - extraTokens.put("FALLING", TokenTypes.RESERVED_WORD_2); - extraTokens.put("RISING", TokenTypes.RESERVED_WORD_2); - - extraTokens.put("PI", TokenTypes.LITERAL_NUMBER_FLOAT); - extraTokens.put("HALF_PI", TokenTypes.LITERAL_NUMBER_FLOAT); - extraTokens.put("TWO_PI", TokenTypes.LITERAL_NUMBER_FLOAT); - extraTokens.put("DEG_TO_RAD", TokenTypes.LITERAL_NUMBER_FLOAT); - extraTokens.put("RAD_TO_DEG", TokenTypes.LITERAL_NUMBER_FLOAT); - extraTokens.put("EULER", TokenTypes.LITERAL_NUMBER_FLOAT); - - // Print. - extraTokens.put("DEC", TokenTypes.RESERVED_WORD_2); - extraTokens.put("HEX", TokenTypes.RESERVED_WORD_2); - extraTokens.put("OCT", TokenTypes.RESERVED_WORD_2); - extraTokens.put("BIN", TokenTypes.RESERVED_WORD_2); - - extraTokens.put("true", TokenTypes.LITERAL_BOOLEAN); - extraTokens.put("false", TokenTypes.LITERAL_BOOLEAN); - - // Related IO - extraTokens.put("pinMode", TokenTypes.FUNCTION); - extraTokens.put("digitalWrite", TokenTypes.FUNCTION); - extraTokens.put("digitalRead", TokenTypes.FUNCTION); - extraTokens.put("analogRead", TokenTypes.FUNCTION); - extraTokens.put("analogReference", TokenTypes.FUNCTION); - extraTokens.put("analogWrite", TokenTypes.FUNCTION); - - // Others. - extraTokens.put("DIGITAL", TokenTypes.RESERVED_WORD_2); - extraTokens.put("ANALOG", TokenTypes.RESERVED_WORD_2); - - // force load references. - PdeKeywords.reload(); - - - HashMap keywords = PdeKeywords.get(); - Set keys = keywords.keySet(); - for (String key : keys) { - extraTokens.put(key, keywords.get(key)); - } - - } catch (Exception e) { - Base.showError("Problem loading keywords", - "Could not load keywords.txt,\n" + - "please re-install Arduino.", e); - System.exit(1); - } + // This assumes all of your extra tokens would normally be scanned as IDENTIFIER. + int newType = pdeKeywords.getTokenType(array, start, end); + if (newType > -1) { + tokenType = newType; } - return extraTokens; + super.addToken(array, start, end, tokenType, startOffset, hyperlink); } - + } diff --git a/app/test/processing/app/AbstractWithPreferencesTest.java b/app/test/processing/app/AbstractWithPreferencesTest.java index b3124929c..4f176471d 100644 --- a/app/test/processing/app/AbstractWithPreferencesTest.java +++ b/app/test/processing/app/AbstractWithPreferencesTest.java @@ -41,6 +41,8 @@ public abstract class AbstractWithPreferencesTest { Preferences.init(null); Theme.init(); + BaseNoGui.initPackages(); + Base.untitledFolder = Base.createTempFolder("untitled"); DeleteFilesOnShutdown.add(Base.untitledFolder); } diff --git a/app/test/processing/app/syntax/PdeKeywordsTest.java b/app/test/processing/app/syntax/PdeKeywordsTest.java new file mode 100644 index 000000000..7942b595e --- /dev/null +++ b/app/test/processing/app/syntax/PdeKeywordsTest.java @@ -0,0 +1,30 @@ +package processing.app.syntax; + +import org.fife.ui.rsyntaxtextarea.TokenTypes; +import org.junit.Test; +import processing.app.AbstractWithPreferencesTest; +import processing.app.BaseNoGui; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +public class PdeKeywordsTest extends AbstractWithPreferencesTest { + + @Test + public void testKeywordsTxtParsing() throws Exception { + PdeKeywords pdeKeywords = new PdeKeywords(); + pdeKeywords.reload(); + + assertEquals("Constants", pdeKeywords.getReference("HIGH")); + assertEquals("IDENTIFIER", pdeKeywords.getTokenTypeAsString("HIGH")); + assertEquals(TokenTypes.IDENTIFIER, pdeKeywords.getTokenType("HIGH".toCharArray(), 0, 3)); + + assertEquals("IncrementCompound", pdeKeywords.getReference("+=")); + assertNull(pdeKeywords.getTokenTypeAsString("+=")); + + assertNull(pdeKeywords.getReference("Mouse")); + assertEquals("VARIABLE", pdeKeywords.getTokenTypeAsString("Mouse")); + assertEquals(TokenTypes.VARIABLE, pdeKeywords.getTokenType("Mouse".toCharArray(), 0, 4)); + } + +} diff --git a/build/shared/lib/keywords.txt b/build/shared/lib/keywords.txt index 5fe836878..9ac844ede 100644 --- a/build/shared/lib/keywords.txt +++ b/build/shared/lib/keywords.txt @@ -5,57 +5,57 @@ # LITERAL2 specifies constants -HIGH LITERAL2 Constants -LOW LITERAL2 Constants -INPUT LITERAL2 Constants -INPUT_PULLUP LITERAL2 Constants -OUTPUT LITERAL2 Constants -DEC LITERAL2 Serial_Print -BIN LITERAL2 Serial_Print -HEX LITERAL2 Serial_Print -OCT LITERAL2 Serial_Print -PI LITERAL2 -HALF_PI LITERAL2 -TWO_PI LITERAL2 -LSBFIRST LITERAL2 ShiftOut -MSBFIRST LITERAL2 ShiftOut -CHANGE LITERAL2 AttachInterrupt -FALLING LITERAL2 AttachInterrupt -RISING LITERAL2 AttachInterrupt -DEFAULT LITERAL2 AnalogReference -EXTERNAL LITERAL2 AnalogReference -INTERNAL LITERAL2 AnalogReference -INTERNAL1V1 LITERAL2 AnalogReference -INTERNAL2V56 LITERAL2 AnalogReference +HIGH LITERAL2 Constants RESERVED_WORD_2 +LOW LITERAL2 Constants RESERVED_WORD_2 +INPUT LITERAL2 Constants RESERVED_WORD_2 +INPUT_PULLUP LITERAL2 Constants RESERVED_WORD_2 +OUTPUT LITERAL2 Constants RESERVED_WORD_2 +DEC LITERAL2 Serial_Print RESERVED_WORD_2 +BIN LITERAL2 Serial_Print RESERVED_WORD_2 +HEX LITERAL2 Serial_Print RESERVED_WORD_2 +OCT LITERAL2 Serial_Print RESERVED_WORD_2 +PI LITERAL2 RESERVED_WORD_2 +HALF_PI LITERAL2 RESERVED_WORD_2 +TWO_PI LITERAL2 RESERVED_WORD_2 +LSBFIRST LITERAL2 ShiftOut RESERVED_WORD_2 +MSBFIRST LITERAL2 ShiftOut RESERVED_WORD_2 +CHANGE LITERAL2 AttachInterrupt RESERVED_WORD_2 +FALLING LITERAL2 AttachInterrupt RESERVED_WORD_2 +RISING LITERAL2 AttachInterrupt RESERVED_WORD_2 +DEFAULT LITERAL2 AnalogReference RESERVED_WORD_2 +EXTERNAL LITERAL2 AnalogReference RESERVED_WORD_2 +INTERNAL LITERAL2 AnalogReference RESERVED_WORD_2 +INTERNAL1V1 LITERAL2 AnalogReference RESERVED_WORD_2 +INTERNAL2V56 LITERAL2 AnalogReference RESERVED_WORD_2 -boolean LITERAL2 BooleanVariables -byte LITERAL2 Byte -char LITERAL2 Char -const LITERAL2 Const -false LITERAL2 Constants -float LITERAL2 Float -null LITERAL2 -int LITERAL2 Int -long LITERAL2 Long -new LITERAL2 -private LITERAL2 -protected LITERAL2 -public LITERAL2 -short LITERAL2 -signed LITERAL2 -static LITERAL2 Static -String LITERAL2 String -void LITERAL2 Void -true LITERAL2 -unsigned LITERAL2 +boolean LITERAL2 BooleanVariables RESERVED_WORD_2 +byte LITERAL2 Byte RESERVED_WORD_2 +char LITERAL2 Char RESERVED_WORD_2 +const LITERAL2 Const RESERVED_WORD_2 +false LITERAL2 Constants LITERAL_BOOLEAN +float LITERAL2 Float RESERVED_WORD_2 +null LITERAL2 RESERVED_WORD_2 +int LITERAL2 Int RESERVED_WORD_2 +long LITERAL2 Long RESERVED_WORD_2 +new LITERAL2 RESERVED_WORD_2 +private LITERAL2 RESERVED_WORD_2 +protected LITERAL2 RESERVED_WORD_2 +public LITERAL2 RESERVED_WORD_2 +short LITERAL2 RESERVED_WORD_2 +signed LITERAL2 RESERVED_WORD_2 +static LITERAL2 Static RESERVED_WORD_2 +String LITERAL2 String RESERVED_WORD_2 +void LITERAL2 Void RESERVED_WORD_2 +true LITERAL2 LITERAL_BOOLEAN +unsigned LITERAL2 RESERVED_WORD_2 -boolean LITERAL2 boolean_ -byte LITERAL2 byte_ -char LITERAL2 char_ -float LITERAL2 float_ -int LITERAL2 int_ -long LITERAL2 long_ -word LITERAL2 word_ +boolean LITERAL2 boolean_ RESERVED_WORD_2 +byte LITERAL2 byte_ RESERVED_WORD_2 +char LITERAL2 char_ RESERVED_WORD_2 +float LITERAL2 float_ RESERVED_WORD_2 +int LITERAL2 int_ RESERVED_WORD_2 +long LITERAL2 long_ RESERVED_WORD_2 +word LITERAL2 word_ RESERVED_WORD_2 # KEYWORD2 specifies methods and functions @@ -113,11 +113,11 @@ shiftOut KEYWORD2 ShiftOut tone KEYWORD2 Tone yield KEYWORD2 Yield -Serial KEYWORD1 Serial -Serial1 KEYWORD1 Serial -Serial2 KEYWORD1 Serial -Serial3 KEYWORD1 Serial -SerialUSB KEYWORD1 Serial +Serial KEYWORD1 Serial DATA_TYPE +Serial1 KEYWORD1 Serial DATA_TYPE +Serial2 KEYWORD1 Serial DATA_TYPE +Serial3 KEYWORD1 Serial DATA_TYPE +SerialUSB KEYWORD1 Serial DATA_TYPE begin KEYWORD2 Serial_Begin end KEYWORD2 Serial_End peek KEYWORD2 Serial_Peek @@ -156,8 +156,8 @@ substring KEYWORD2 toCharArray KEYWORD2 toInt KEYWORD2 -Keyboard KEYWORD1 -Mouse KEYWORD1 +Keyboard KEYWORD1 DATA_TYPE +Mouse KEYWORD1 DATA_TYPE press KEYWORD2 release KEYWORD2 releaseAll KEYWORD2 @@ -168,28 +168,28 @@ isPressed KEYWORD2 # KEYWORD3 specifies structures -break KEYWORD3 Break -case KEYWORD3 SwitchCase -class KEYWORD3 -continue KEYWORD3 Continue -default KEYWORD3 SwitchCase -do KEYWORD3 DoWhile -double KEYWORD3 Double -else KEYWORD3 Else -for KEYWORD3 For -if KEYWORD3 If -register KEYWORD3 -return KEYWORD3 Return +break KEYWORD3 Break RESERVED_WORD +case KEYWORD3 SwitchCase RESERVED_WORD +class KEYWORD3 RESERVED_WORD +continue KEYWORD3 Continue RESERVED_WORD +default KEYWORD3 SwitchCase RESERVED_WORD +do KEYWORD3 DoWhile RESERVED_WORD +double KEYWORD3 Double RESERVED_WORD +else KEYWORD3 Else RESERVED_WORD +for KEYWORD3 For RESERVED_WORD +if KEYWORD3 If RESERVED_WORD +register KEYWORD3 RESERVED_WORD +return KEYWORD3 Return RESERVED_WORD -switch KEYWORD3 SwitchCase -this KEYWORD3 -throw KEYWORD3 -try KEYWORD3 -while KEYWORD3 While -word KEYWORD3 Word +switch KEYWORD3 SwitchCase RESERVED_WORD +this KEYWORD3 RESERVED_WORD +throw KEYWORD3 RESERVED_WORD +try KEYWORD3 RESERVED_WORD +while KEYWORD3 While RESERVED_WORD +word KEYWORD3 Word RESERVED_WORD -setup KEYWORD3 Setup -loop KEYWORD3 Loop +setup KEYWORD3 Setup RESERVED_WORD +loop KEYWORD3 Loop RESERVED_WORD # operators aren't highlighted, but may have documentation @@ -199,15 +199,15 @@ loop KEYWORD3 Loop = assign & BitwiseAnd | BitwiseAnd -, +, // Comments -?: +?: {} Braces -- Increment / Arithmetic /* Comments . dot -== +== < greaterthan <= greaterthanorequalto ++ Increment diff --git a/build/shared/lib/theme/syntax/default.xml b/build/shared/lib/theme/syntax/default.xml index 59cac7813..f3553a4da 100644 --- a/build/shared/lib/theme/syntax/default.xml +++ b/build/shared/lib/theme/syntax/default.xml @@ -18,7 +18,7 @@ - + @@ -40,7 +40,7 @@