Implemented incremental analyzer for Groovy

This commit is contained in:
Akash Yadav
2022-06-21 19:22:01 +05:30
parent cafec5ea87
commit 1dfa54bc17
2 changed files with 192 additions and 264 deletions
@@ -1,264 +0,0 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*
*/
package com.itsaky.androidide.language.groovy;
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.ANNOTATION;
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.LITERAL;
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.OPERATOR;
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.TEXT_NORMAL;
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.TYPE_NAME;
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forComment;
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forKeyword;
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forString;
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.get;
import com.itsaky.androidide.lexers.groovy.GroovyLexer;
import com.itsaky.androidide.utils.CharSequenceReader;
import com.itsaky.androidide.utils.ILogger;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CodePointCharStream;
import org.antlr.v4.runtime.Token;
import java.io.IOException;
import java.util.Stack;
import io.github.rosemoe.sora.lang.analysis.SimpleAnalyzeManager;
import io.github.rosemoe.sora.lang.styling.CodeBlock;
import io.github.rosemoe.sora.lang.styling.MappedSpans;
import io.github.rosemoe.sora.lang.styling.Styles;
public class GroovyAnalyzer extends SimpleAnalyzeManager<Void> {
private static final ILogger LOG = ILogger.newInstance("GroovyAnalyzer");
@Override
protected Styles analyze(StringBuilder text, Delegate<Void> delegate) {
final var styles = new Styles();
final var colors = new MappedSpans.Builder();
final CodePointCharStream stream;
try {
stream = CharStreams.fromReader(new CharSequenceReader(text));
} catch (IOException e) {
LOG.error("Unable to read text to analyze", e);
return styles;
}
final var lexer = new GroovyLexer(stream);
final var stack = new Stack<CodeBlock>();
Token token;
int line, column, lastLine = 1, currSwitch = 0, maxSwitch = 0, type, previous = 0;
boolean isFirst = true;
while (!delegate.isCancelled()) {
token = lexer.nextToken();
if (token == null) {
break;
}
line = token.getLine() - 1;
column = token.getCharPositionInLine();
type = token.getType();
if (type == GroovyLexer.EOF) {
lastLine = line;
break;
}
switch (type) {
case GroovyLexer.WS:
if (isFirst) {
colors.addNormalIfNull();
}
break;
case GroovyLexer.ABSTRACT:
case GroovyLexer.ASSERT:
case GroovyLexer.BREAK:
case GroovyLexer.CASE:
case GroovyLexer.CATCH:
case GroovyLexer.CLASS:
case GroovyLexer.CONST:
case GroovyLexer.CONTINUE:
case GroovyLexer.DEFAULT:
case GroovyLexer.DO:
case GroovyLexer.ELSE:
case GroovyLexer.EXTENDS:
case GroovyLexer.FINAL:
case GroovyLexer.FINALLY:
case GroovyLexer.FOR:
case GroovyLexer.IF:
case GroovyLexer.GOTO:
case GroovyLexer.IMPLEMENTS:
case GroovyLexer.IMPORT:
case GroovyLexer.INSTANCEOF:
case GroovyLexer.INTERFACE:
case GroovyLexer.NATIVE:
case GroovyLexer.NEW:
case GroovyLexer.PACKAGE:
case GroovyLexer.PRIVATE:
case GroovyLexer.PROTECTED:
case GroovyLexer.PUBLIC:
case GroovyLexer.RETURN:
case GroovyLexer.STATIC:
case GroovyLexer.STRICTFP:
case GroovyLexer.SUPER:
case GroovyLexer.SWITCH:
case GroovyLexer.SYNCHRONIZED:
case GroovyLexer.THIS:
case GroovyLexer.THROW:
case GroovyLexer.THROWS:
case GroovyLexer.TRANSIENT:
case GroovyLexer.TRY:
case GroovyLexer.VOID:
case GroovyLexer.VOLATILE:
case GroovyLexer.WHILE:
colors.addIfNeeded(line, column, forKeyword());
break;
case GroovyLexer.DECIMAL_LITERAL:
case GroovyLexer.HEX_LITERAL:
case GroovyLexer.OCT_LITERAL:
case GroovyLexer.BINARY_LITERAL:
case GroovyLexer.FLOAT_LITERAL:
case GroovyLexer.HEX_FLOAT_LITERAL:
case GroovyLexer.BOOL_LITERAL:
case GroovyLexer.CHAR_LITERAL:
case GroovyLexer.NULL_LITERAL:
colors.addIfNeeded(line, column, get(LITERAL));
break;
case GroovyLexer.STRING_LITERAL:
case GroovyLexer.SINGLE_QUOTE_STRING:
colors.addIfNeeded(line, column, forString());
break;
case GroovyLexer.LPAREN:
case GroovyLexer.RPAREN:
case GroovyLexer.LBRACK:
case GroovyLexer.RBRACK:
case GroovyLexer.SEMI:
case GroovyLexer.COMMA:
case GroovyLexer.ASSIGN:
case GroovyLexer.GT:
case GroovyLexer.LT:
case GroovyLexer.BANG:
case GroovyLexer.TILDE:
case GroovyLexer.QUESTION:
case GroovyLexer.COLON:
case GroovyLexer.EQUAL:
case GroovyLexer.GE:
case GroovyLexer.LE:
case GroovyLexer.NOTEQUAL:
case GroovyLexer.AND:
case GroovyLexer.OR:
case GroovyLexer.INC:
case GroovyLexer.DEC:
case GroovyLexer.ADD:
case GroovyLexer.SUB:
case GroovyLexer.MUL:
case GroovyLexer.DIV:
case GroovyLexer.BITAND:
case GroovyLexer.BITOR:
case GroovyLexer.CARET:
case GroovyLexer.MOD:
case GroovyLexer.ADD_ASSIGN:
case GroovyLexer.SUB_ASSIGN:
case GroovyLexer.MUL_ASSIGN:
case GroovyLexer.DIV_ASSIGN:
case GroovyLexer.AND_ASSIGN:
case GroovyLexer.OR_ASSIGN:
case GroovyLexer.XOR_ASSIGN:
case GroovyLexer.MOD_ASSIGN:
case GroovyLexer.LSHIFT_ASSIGN:
case GroovyLexer.RSHIFT_ASSIGN:
case GroovyLexer.URSHIFT_ASSIGN:
case GroovyLexer.ARROW:
case GroovyLexer.COLONCOLON:
case GroovyLexer.ELLIPSIS:
case GroovyLexer.DOT:
colors.addIfNeeded(line, column, get(OPERATOR));
break;
case GroovyLexer.BOOLEAN:
case GroovyLexer.BYTE:
case GroovyLexer.CHAR:
case GroovyLexer.DOUBLE:
case GroovyLexer.ENUM:
case GroovyLexer.FLOAT:
case GroovyLexer.INT:
case GroovyLexer.LONG:
case GroovyLexer.SHORT:
colors.addIfNeeded(line, column, get(TYPE_NAME));
break;
case GroovyLexer.COMMENT:
case GroovyLexer.LINE_COMMENT:
colors.addIfNeeded(line, column, forComment());
break;
case GroovyLexer.AT:
colors.addIfNeeded(line, column, get(ANNOTATION));
break;
case GroovyLexer.IDENTIFIER:
if (previous == GroovyLexer.AT) {
colors.addIfNeeded(line, column, get(ANNOTATION));
break;
}
colors.addIfNeeded(line, column, get(TEXT_NORMAL));
break;
case GroovyLexer.LBRACE:
colors.addIfNeeded(line, column, get(OPERATOR));
if (stack.isEmpty()) {
if (currSwitch > maxSwitch) maxSwitch = currSwitch;
currSwitch = 0;
}
currSwitch++;
var block = styles.obtainNewBlock();
block.startLine = line;
block.startColumn = column;
stack.push(block);
break;
case GroovyLexer.RBRACE:
colors.addIfNeeded(line, column, get(OPERATOR));
if (!stack.isEmpty()) {
var block2 = stack.pop();
block2.endLine = line;
block2.endColumn = column;
if (block2.startLine != block2.endLine) styles.addCodeBlock(block2);
}
break;
default:
colors.addIfNeeded(line, column, get(TEXT_NORMAL));
break;
}
isFirst = false;
if (type != GroovyLexer.WS) {
previous = type;
}
}
if (stack.isEmpty() && currSwitch > maxSwitch) {
maxSwitch = currSwitch;
}
colors.determine(lastLine);
styles.setSuppressSwitch(maxSwitch + 10);
styles.spans = colors.build();
return styles;
}
}
@@ -0,0 +1,192 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*/
package com.itsaky.androidide.language.groovy
import com.itsaky.androidide.language.incremental.BaseIncrementalAnalyzeManager
import com.itsaky.androidide.language.incremental.IncrementalToken
import com.itsaky.androidide.language.incremental.LineState
import com.itsaky.androidide.lexers.groovy.GroovyLexer
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE
import io.github.rosemoe.sora.lang.analysis.IncrementalAnalyzeManager.LineTokenizeResult
import io.github.rosemoe.sora.lang.styling.CodeBlock
import io.github.rosemoe.sora.lang.styling.Span
import io.github.rosemoe.sora.lang.styling.TextStyle
import io.github.rosemoe.sora.text.Content
/** @author Akash Yadav */
class GroovyAnalyzer : BaseIncrementalAnalyzeManager(GroovyLexer::class.java) {
override fun computeBlocks(text: Content, delegate: CodeBlockAnalyzeDelegate): List<CodeBlock> {
return emptyList()
}
override fun generateSpans(
tokens: LineTokenizeResult<LineState, IncrementalToken>,
): List<Span> {
val spans = mutableListOf<Span>()
var previous = GroovyLexer.WS
var first = true
for (token in tokens.tokens) {
val type = token.type
val offset = token.startIndex
when (type) {
GroovyLexer.WS -> {
if (first) {
spans.add(Span.obtain(offset, TextStyle.makeStyle(SchemeAndroidIDE.TEXT_NORMAL)))
first = false
}
}
GroovyLexer.ABSTRACT,
GroovyLexer.ASSERT,
GroovyLexer.BREAK,
GroovyLexer.CASE,
GroovyLexer.CATCH,
GroovyLexer.CLASS,
GroovyLexer.CONST,
GroovyLexer.CONTINUE,
GroovyLexer.DEFAULT,
GroovyLexer.DO,
GroovyLexer.ELSE,
GroovyLexer.EXTENDS,
GroovyLexer.FINAL,
GroovyLexer.FINALLY,
GroovyLexer.FOR,
GroovyLexer.IF,
GroovyLexer.GOTO,
GroovyLexer.IMPLEMENTS,
GroovyLexer.IMPORT,
GroovyLexer.INSTANCEOF,
GroovyLexer.INTERFACE,
GroovyLexer.NATIVE,
GroovyLexer.NEW,
GroovyLexer.PACKAGE,
GroovyLexer.PRIVATE,
GroovyLexer.PROTECTED,
GroovyLexer.PUBLIC,
GroovyLexer.RETURN,
GroovyLexer.STATIC,
GroovyLexer.STRICTFP,
GroovyLexer.SUPER,
GroovyLexer.SWITCH,
GroovyLexer.SYNCHRONIZED,
GroovyLexer.THIS,
GroovyLexer.THROW,
GroovyLexer.THROWS,
GroovyLexer.TRANSIENT,
GroovyLexer.TRY,
GroovyLexer.VOID,
GroovyLexer.VOLATILE,
GroovyLexer.WHILE -> spans.add(Span.obtain(offset, SchemeAndroidIDE.forKeyword()))
GroovyLexer.DECIMAL_LITERAL,
GroovyLexer.HEX_LITERAL,
GroovyLexer.OCT_LITERAL,
GroovyLexer.BINARY_LITERAL,
GroovyLexer.FLOAT_LITERAL,
GroovyLexer.HEX_FLOAT_LITERAL,
GroovyLexer.BOOL_LITERAL,
GroovyLexer.CHAR_LITERAL,
GroovyLexer.NULL_LITERAL, ->
spans.add(Span.obtain(offset, TextStyle.makeStyle(SchemeAndroidIDE.LITERAL)))
GroovyLexer.SINGLE_QUOTE_STRING,
GroovyLexer.STRING_LITERAL -> spans.add(Span.obtain(offset, SchemeAndroidIDE.forString()))
GroovyLexer.LPAREN,
GroovyLexer.RPAREN,
GroovyLexer.LBRACK,
GroovyLexer.RBRACK,
GroovyLexer.SEMI,
GroovyLexer.COMMA,
GroovyLexer.ASSIGN,
GroovyLexer.GT,
GroovyLexer.LT,
GroovyLexer.BANG,
GroovyLexer.TILDE,
GroovyLexer.QUESTION,
GroovyLexer.COLON,
GroovyLexer.EQUAL,
GroovyLexer.GE,
GroovyLexer.LE,
GroovyLexer.NOTEQUAL,
GroovyLexer.AND,
GroovyLexer.OR,
GroovyLexer.INC,
GroovyLexer.DEC,
GroovyLexer.ADD,
GroovyLexer.SUB,
GroovyLexer.MUL,
GroovyLexer.DIV,
GroovyLexer.BITAND,
GroovyLexer.BITOR,
GroovyLexer.CARET,
GroovyLexer.MOD,
GroovyLexer.ADD_ASSIGN,
GroovyLexer.SUB_ASSIGN,
GroovyLexer.MUL_ASSIGN,
GroovyLexer.DIV_ASSIGN,
GroovyLexer.AND_ASSIGN,
GroovyLexer.OR_ASSIGN,
GroovyLexer.XOR_ASSIGN,
GroovyLexer.MOD_ASSIGN,
GroovyLexer.LSHIFT_ASSIGN,
GroovyLexer.RSHIFT_ASSIGN,
GroovyLexer.URSHIFT_ASSIGN,
GroovyLexer.ARROW,
GroovyLexer.COLONCOLON,
GroovyLexer.ELLIPSIS,
GroovyLexer.LBRACE,
GroovyLexer.RBRACE,
GroovyLexer.DOT, ->
spans.add(Span.obtain(offset, TextStyle.makeStyle(SchemeAndroidIDE.OPERATOR)))
GroovyLexer.BOOLEAN,
GroovyLexer.BYTE,
GroovyLexer.CHAR,
GroovyLexer.DOUBLE,
GroovyLexer.ENUM,
GroovyLexer.FLOAT,
GroovyLexer.INT,
GroovyLexer.LONG,
GroovyLexer.SHORT, ->
spans.add(Span.obtain(offset, TextStyle.makeStyle(SchemeAndroidIDE.TYPE_NAME)))
GroovyLexer.COMMENT -> spans.add(Span.obtain(offset, SchemeAndroidIDE.forComment()))
GroovyLexer.LINE_COMMENT -> handleLineCommentSpan(token, spans, offset)
GroovyLexer.AT ->
spans.add(Span.obtain(offset, TextStyle.makeStyle(SchemeAndroidIDE.ANNOTATION)))
GroovyLexer.IDENTIFIER -> {
var colorId = SchemeAndroidIDE.TEXT_NORMAL
if (previous == GroovyLexer.AT) {
colorId = SchemeAndroidIDE.ANNOTATION
}
spans.add(Span.obtain(offset, TextStyle.makeStyle(colorId)))
}
else -> spans.add(Span.obtain(offset, TextStyle.makeStyle(SchemeAndroidIDE.TEXT_NORMAL)))
}
previous = type
}
return spans
}
override fun getMultilineTokenStartEndTypes(): Array<IntArray> {
val start = intArrayOf(GroovyLexer.DIV, GroovyLexer.MUL)
val end = intArrayOf(GroovyLexer.MUL, GroovyLexer.DIV)
return arrayOf(start, end)
}
override fun handleIncompleteToken(token: IncrementalToken) {
token.type = GroovyLexer.COMMENT
}
}