diff --git a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAnalyzer.java b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAnalyzer.java
deleted file mode 100755
index 054189269..000000000
--- a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAnalyzer.java
+++ /dev/null
@@ -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 .
- *
- */
-
-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 {
-
- private static final ILogger LOG = ILogger.newInstance("GroovyAnalyzer");
-
- @Override
- protected Styles analyze(StringBuilder text, Delegate 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();
-
- 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;
- }
-}
diff --git a/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAnalyzer.kt b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAnalyzer.kt
new file mode 100644
index 000000000..1fb89053a
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/language/groovy/GroovyAnalyzer.kt
@@ -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 .
+ */
+
+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 {
+ return emptyList()
+ }
+
+ override fun generateSpans(
+ tokens: LineTokenizeResult,
+ ): List {
+ val spans = mutableListOf()
+ 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 {
+ 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
+ }
+}