You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2020/02/04 16:00:55 UTC
[groovy] 02/02: Fix occasional syntax highlighting issues of groovy
console
This is an automated email from the ASF dual-hosted git repository.
sunlan pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 7a423de2dcc2ff96bb35606b1481ba6ed46258b8
Author: Daniel Sun <su...@apache.org>
AuthorDate: Tue Feb 4 23:08:24 2020 +0800
Fix occasional syntax highlighting issues of groovy console
For example:
1) input the following code in groovy console
```
def a =
123
```
2) select all text and press the <TAB> key
3) `123` is not rendered properly
(cherry picked from commit 00b46e9212e1dd2cedbe18ef4b20f41188f2cdda)
---
.../console/ui/text/MatchingHighlighter.java | 12 ++--
.../console/ui/text/SmartDocumentFilter.java | 78 +++++++++++++++++++---
.../groovy/groovy/console/ui/text/TextEditor.java | 40 +++++++++--
3 files changed, 109 insertions(+), 21 deletions(-)
diff --git a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/MatchingHighlighter.java b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/MatchingHighlighter.java
index 95a594f..62f2b8c 100644
--- a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/MatchingHighlighter.java
+++ b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/MatchingHighlighter.java
@@ -144,11 +144,13 @@ public class MatchingHighlighter implements CaretListener {
stack.push(tuple(token, triggerFlag));
} else if (tokenType == matchedTokenType) {
- Tuple2<Token, Boolean> tokenAndTriggerFlagTuple = stack.pop();
- if (tokenAndTriggerFlagTuple.getV2()) {
- triggerToken = tokenAndTriggerFlagTuple.getV1();
- matchedToken = token;
- break;
+ if (!stack.isEmpty()) {
+ Tuple2<Token, Boolean> tokenAndTriggerFlagTuple = stack.pop();
+ if (tokenAndTriggerFlagTuple.getV2()) {
+ triggerToken = tokenAndTriggerFlagTuple.getV1();
+ matchedToken = token;
+ break;
+ }
}
}
}
diff --git a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/SmartDocumentFilter.java b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/SmartDocumentFilter.java
index 5e49ca6..9b103e6 100644
--- a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/SmartDocumentFilter.java
+++ b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/SmartDocumentFilter.java
@@ -18,6 +18,7 @@
*/
package groovy.console.ui.text;
+import groovy.lang.Tuple2;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
@@ -26,6 +27,8 @@ import org.antlr.v4.runtime.LexerNoViableAltException;
import org.antlr.v4.runtime.Token;
import org.apache.groovy.parser.antlr4.GroovyLangLexer;
import org.apache.groovy.parser.antlr4.GroovySyntaxError;
+import org.apache.groovy.parser.antlr4.util.PositionConfigureUtils;
+import org.apache.groovy.parser.antlr4.util.StringUtils;
import javax.swing.text.AttributeSet;
import javax.swing.text.BadLocationException;
@@ -41,6 +44,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import java.util.stream.Collectors;
import static org.apache.groovy.parser.antlr4.GroovyLexer.ABSTRACT;
import static org.apache.groovy.parser.antlr4.GroovyLexer.AS;
@@ -163,7 +167,7 @@ public class SmartDocumentFilter extends DocumentFilter {
private String replaceMetaCharacters(String string) {
// just in case remove carriage returns
- string = string.replace("\\t", TAB_REPLACEMENT);
+ string = string.replace("\r\n", "\n");
return string;
}
@@ -192,7 +196,12 @@ public class SmartDocumentFilter extends DocumentFilter {
}
List<Token> tokenList = tokenStream.getTokens();
- List<Token> tokenListToRender = findTokensToRender(tokenList);
+ List<Token> tokenListToRender;
+ try {
+ tokenListToRender = findTokensToRender(tokenList);
+ } finally {
+ this.setRenderRange(null);
+ }
for (Token token : tokenListToRender) {
int tokenType = token.getType();
@@ -228,18 +237,35 @@ public class SmartDocumentFilter extends DocumentFilter {
}
private List<Token> findTokensToRender(List<Token> tokenList) {
- int tokenListSize = tokenList.size();
- int latestTokenListSize = latestTokenList.size();
+ final Tuple2<Integer, Integer> renderRange = getRenderRange();
+ if (null != renderRange) {
+ long startLine = lineNumber(renderRange.getV1());
+ long stopLine = lineNumber(renderRange.getV2());
+
+ if (startLine < 0 || stopLine < 0) return tokenList; // should never happen
+
+ return tokenList.stream()
+ .filter(e -> e.getLine() >= startLine && PositionConfigureUtils.endPosition(e).getV1() <= stopLine)
+ .collect(Collectors.toList());
+ }
- if (0 == tokenListSize || 0 == latestTokenListSize) {
+ List<Token> tmpLatestTokenList = filterNewlines(this.latestTokenList);
+ int latestTokenListSize = tmpLatestTokenList.size();
+ if (0 == latestTokenListSize) {
+ return tokenList;
+ }
+
+ List<Token> tmpTokenList = filterNewlines(tokenList);
+ int tokenListSize = tmpTokenList.size();
+ if (0 == tokenListSize) {
return tokenList;
}
int startTokenIndex = 0;
int minSize = Math.min(tokenListSize, latestTokenListSize);
for (int i = 0; i < minSize; i++) {
- Token token = tokenList.get(i);
- Token latestToken = latestTokenList.get(i);
+ Token token = tmpTokenList.get(i);
+ Token latestToken = tmpLatestTokenList.get(i);
if (token.getType() == latestToken.getType()
&& token.getStartIndex() == latestToken.getStartIndex()
@@ -251,8 +277,8 @@ public class SmartDocumentFilter extends DocumentFilter {
break;
}
- List<Token> newTokenList = new ArrayList<>(tokenList);
- List<Token> newLatestTokenList = new ArrayList<>(latestTokenList);
+ List<Token> newTokenList = new ArrayList<>(tmpTokenList);
+ List<Token> newLatestTokenList = new ArrayList<>(tmpLatestTokenList);
Collections.reverse(newTokenList);
Collections.reverse(newLatestTokenList);
@@ -277,12 +303,36 @@ public class SmartDocumentFilter extends DocumentFilter {
}
if (startTokenIndex <= stopTokenIndex) {
- return tokenList.subList(startTokenIndex, stopTokenIndex);
+ return tmpTokenList.subList(startTokenIndex, stopTokenIndex);
}
return tokenList; // should never reach here. If unexpected error occurred, it's better to render all tokens
}
+ /**
+ * Returns line number starting with 1
+ */
+ private long lineNumber(int position) {
+ long result = -1;
+
+ if (position < 0) return result;
+
+ try {
+ String text = this.styledDocument.getText(0, position + 1);
+ result = StringUtils.countChar(text, '\n') + 1;
+ } catch (BadLocationException e) {
+ e.printStackTrace(System.err);
+ }
+
+ return result;
+ }
+
+ private List<Token> filterNewlines(List<Token> tokenList) {
+ return tokenList.stream()
+ .filter(e -> !(NL == e.getType() && e.getText().trim().isEmpty()))
+ .collect(Collectors.toList());
+ }
+
private Style findStyleByTokenType(int tokenType) {
Style style = styleContext.getStyle(String.valueOf(tokenType));
@@ -347,7 +397,7 @@ public class SmartDocumentFilter extends DocumentFilter {
private volatile boolean latest = false;
private volatile List<Token> latestTokenList = Collections.emptyList();
- private static final String TAB_REPLACEMENT = " ";
+ private volatile Tuple2<Integer, Integer> renderRange;
public boolean isLatest() {
return latest;
@@ -357,4 +407,10 @@ public class SmartDocumentFilter extends DocumentFilter {
return latestTokenList;
}
+ public void setRenderRange(Tuple2<Integer, Integer> renderRange) {
+ this.renderRange = renderRange;
+ }
+ public Tuple2<Integer, Integer> getRenderRange() {
+ return renderRange;
+ }
}
diff --git a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/TextEditor.java b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/TextEditor.java
index 5be4773..5c62211 100644
--- a/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/TextEditor.java
+++ b/subprojects/groovy-console/src/main/groovy/groovy/console/ui/text/TextEditor.java
@@ -18,17 +18,35 @@
*/
package groovy.console.ui.text;
-import javax.swing.*;
+import groovy.lang.Tuple;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.ActionMap;
+import javax.swing.InputMap;
+import javax.swing.JTextPane;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
import javax.swing.plaf.ComponentUI;
import javax.swing.text.BadLocationException;
import javax.swing.text.Caret;
import javax.swing.text.DefaultCaret;
import javax.swing.text.DefaultEditorKit;
+import javax.swing.text.DefaultStyledDocument;
import javax.swing.text.Document;
+import javax.swing.text.DocumentFilter;
import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
import javax.swing.text.Utilities;
-import java.awt.*;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Cursor;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
@@ -416,9 +434,10 @@ public class TextEditor extends JTextPane implements Pageable, Printable {
TAB_BACK_PATTERN.matcher(getSelectedText()).replaceAll("") :
getSelectedText().replaceAll("^\t", "");
+ int stop = start + text.length();
+ setRenderRange(start, stop);
TextEditor.this.replaceSelection(text);
-
- TextEditor.this.select(start, start + text.length());
+ TextEditor.this.select(start, stop);
}
} catch (Exception e) {
e.printStackTrace();
@@ -441,8 +460,11 @@ public class TextEditor extends JTextPane implements Pageable, Printable {
String toReplace = TextEditor.this.getSelectedText();
toReplace = LINE_START.matcher(toReplace).replaceAll(text);
+
+ int stop = start + toReplace.length();
+ setRenderRange(start, stop);
TextEditor.this.replaceSelection(toReplace);
- TextEditor.this.select(start, start + toReplace.length());
+ TextEditor.this.select(start, stop);
} else {
int pos = TextEditor.this.getCaretPosition();
doc.insertString(pos, text, null);
@@ -453,6 +475,14 @@ public class TextEditor extends JTextPane implements Pageable, Printable {
}
}
+ private void setRenderRange(int start, int stop) {
+ DocumentFilter documentFilter = ((DefaultStyledDocument) TextEditor.this.getDocument()).getDocumentFilter();
+ if (documentFilter instanceof SmartDocumentFilter) {
+ SmartDocumentFilter smartDocumentFilter = (SmartDocumentFilter) documentFilter;
+ smartDocumentFilter.setRenderRange(Tuple.tuple(start, stop));
+ }
+ }
+
/**
* Paint a horizontal line the width of a column and 1 pixel high
*/