From c6c49ea981115ff963eb3083ccfac16db99222b7 Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Tue, 21 Jun 2022 20:31:03 +0530 Subject: [PATCH] Implemented incremental analyzer for Kotlin --- .../language/kotlin/KotlinAnalyzer.java | 276 ------------------ .../language/kotlin/KotlinAnalyzer.kt | 236 +++++++++++++++ .../language/kotlin/KotlinLanguage.java | 2 - 3 files changed, 236 insertions(+), 278 deletions(-) delete mode 100644 app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinAnalyzer.java create mode 100644 app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinAnalyzer.kt diff --git a/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinAnalyzer.java b/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinAnalyzer.java deleted file mode 100644 index 031b81a60..000000000 --- a/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinAnalyzer.java +++ /dev/null @@ -1,276 +0,0 @@ -package com.itsaky.androidide.language.kotlin; - -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.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 static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.withoutCompletion; - -import com.itsaky.androidide.lexers.kotlin.KotlinLexer; -import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE; -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 io.github.rosemoe.sora.lang.analysis.SimpleAnalyzeManager; -import io.github.rosemoe.sora.lang.styling.MappedSpans; -import io.github.rosemoe.sora.lang.styling.Styles; - -public class KotlinAnalyzer extends SimpleAnalyzeManager { - - private static final ILogger LOG = ILogger.newInstance("KotlinAnalyzer"); - - @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 KotlinLexer(stream); - // final var parser = new KotlinParser(new CommonTokenStream(lexer)); - Token token; - int line, column, lastLine = 1, type; - 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 == KotlinLexer.EOF) { - lastLine = line; - break; - } - - switch (type) { - case KotlinLexer.WS: - if (isFirst) { - colors.addNormalIfNull(); - } - break; - // annotations - case KotlinLexer.AT_NO_WS: - case KotlinLexer.AT_POST_WS: - case KotlinLexer.AT_PRE_WS: - case KotlinLexer.AT_BOTH_WS: - colors.addIfNeeded(line, column, get(ANNOTATION)); - break; - // lexicalModifiers - case KotlinLexer.ANNOTATION: - case KotlinLexer.ABSTRACT: - case KotlinLexer.BY: - case KotlinLexer.CATCH: - case KotlinLexer.COMPANION: - case KotlinLexer.CONSTRUCTOR: - case KotlinLexer.CROSSINLINE: - case KotlinLexer.DATA: - case KotlinLexer.DYNAMIC: - case KotlinLexer.ENUM: - case KotlinLexer.EXTERNAL: - case KotlinLexer.FINAL: - case KotlinLexer.FINALLY: - case KotlinLexer.IMPORT: - case KotlinLexer.INFIX: - case KotlinLexer.INIT: - case KotlinLexer.INLINE: - case KotlinLexer.INNER: - case KotlinLexer.INTERNAL: - case KotlinLexer.LATEINIT: - case KotlinLexer.NOINLINE: - case KotlinLexer.OPEN: - case KotlinLexer.OPERATOR: - case KotlinLexer.OUT: - case KotlinLexer.OVERRIDE: - case KotlinLexer.PRIVATE: - case KotlinLexer.PROTECTED: - case KotlinLexer.PUBLIC: - case KotlinLexer.REIFIED: - case KotlinLexer.SEALED: - case KotlinLexer.TAILREC: - case KotlinLexer.VARARG: - case KotlinLexer.WHERE: - case KotlinLexer.GET: - case KotlinLexer.SET: - case KotlinLexer.FIELD: - case KotlinLexer.PROPERTY: - case KotlinLexer.RECEIVER: - case KotlinLexer.PARAM: - case KotlinLexer.SETPARAM: - case KotlinLexer.DELEGATE: - case KotlinLexer.FILE: - case KotlinLexer.EXPECT: - case KotlinLexer.ACTUAL: - case KotlinLexer.VALUE: - case KotlinLexer.CONST: - case KotlinLexer.SUSPEND: - // SECTION: keywords - case KotlinLexer.RETURN_AT: - case KotlinLexer.CONTINUE_AT: - case KotlinLexer.BREAK_AT: - case KotlinLexer.THIS_AT: - case KotlinLexer.SUPER_AT: - case KotlinLexer.PACKAGE: - case KotlinLexer.CLASS: - case KotlinLexer.INTERFACE: - case KotlinLexer.FUN: - case KotlinLexer.OBJECT: - case KotlinLexer.VAL: - case KotlinLexer.VAR: - case KotlinLexer.TYPE_ALIAS: - case KotlinLexer.THIS: - case KotlinLexer.SUPER: - case KotlinLexer.TYPEOF: - case KotlinLexer.IF: - case KotlinLexer.ELSE: - case KotlinLexer.WHEN: - case KotlinLexer.TRY: - case KotlinLexer.FOR: - case KotlinLexer.DO: - case KotlinLexer.WHILE: - case KotlinLexer.THROW: - case KotlinLexer.RETURN: - case KotlinLexer.CONTINUE: - case KotlinLexer.BREAK: - case KotlinLexer.AS: - case KotlinLexer.IS: - case KotlinLexer.IN: - case KotlinLexer.NOT_IS: - case KotlinLexer.NOT_IN: - colors.addIfNeeded(line, column, forKeyword()); - break; - // SECTION: literals - case KotlinLexer.RealLiteral: - case KotlinLexer.FloatLiteral: - case KotlinLexer.DoubleLiteral: - case KotlinLexer.IntegerLiteral: - case KotlinLexer.HexLiteral: - case KotlinLexer.BinLiteral: - case KotlinLexer.UnsignedLiteral: - case KotlinLexer.LongLiteral: - case KotlinLexer.BooleanLiteral: - case KotlinLexer.NullLiteral: - case KotlinLexer.CharacterLiteral: - colors.addIfNeeded(line, column, get(LITERAL)); - break; - // strings - case KotlinLexer.QUOTE_OPEN: - case KotlinLexer.TRIPLE_QUOTE_OPEN: - case KotlinLexer.QUOTE_CLOSE: - case KotlinLexer.LineStrRef: - case KotlinLexer.LineStrText: - case KotlinLexer.LineStrEscapedChar: - case KotlinLexer.LineStrExprStart: - case KotlinLexer.TRIPLE_QUOTE_CLOSE: - case KotlinLexer.MultiLineStringQuote: - case KotlinLexer.MultiLineStrRef: - case KotlinLexer.MultiLineStrText: - case KotlinLexer.MultiLineStrExprStart: - colors.addIfNeeded(line, column, forString()); - break; - // SECTION: separatorsAndOperations - case KotlinLexer.RESERVED: - case KotlinLexer.DOT: - case KotlinLexer.COMMA: - case KotlinLexer.LPAREN: - case KotlinLexer.RPAREN: - case KotlinLexer.LSQUARE: - case KotlinLexer.RSQUARE: - case KotlinLexer.LCURL: - case KotlinLexer.RCURL: - case KotlinLexer.MULT: - case KotlinLexer.MOD: - case KotlinLexer.DIV: - case KotlinLexer.ADD: - case KotlinLexer.SUB: - case KotlinLexer.INCR: - case KotlinLexer.DECR: - case KotlinLexer.CONJ: - case KotlinLexer.DISJ: - case KotlinLexer.EXCL_WS: - case KotlinLexer.EXCL_NO_WS: - case KotlinLexer.COLON: - case KotlinLexer.SEMICOLON: - case KotlinLexer.ASSIGNMENT: - case KotlinLexer.ADD_ASSIGNMENT: - case KotlinLexer.SUB_ASSIGNMENT: - case KotlinLexer.MULT_ASSIGNMENT: - case KotlinLexer.DIV_ASSIGNMENT: - case KotlinLexer.MOD_ASSIGNMENT: - case KotlinLexer.ARROW: - case KotlinLexer.DOUBLE_ARROW: - case KotlinLexer.RANGE: - case KotlinLexer.COLONCOLON: - case KotlinLexer.DOUBLE_SEMICOLON: - case KotlinLexer.QUEST_WS: - case KotlinLexer.QUEST_NO_WS: - case KotlinLexer.LANGLE: - case KotlinLexer.RANGLE: - case KotlinLexer.LE: - case KotlinLexer.GE: - case KotlinLexer.EXCL_EQ: - case KotlinLexer.EXCL_EQEQ: - case KotlinLexer.AS_SAFE: - case KotlinLexer.EQEQ: - case KotlinLexer.EQEQEQ: - case KotlinLexer.SINGLE_QUOTE: - colors.addIfNeeded(line, column, get(OPERATOR)); - break; - // comments - case KotlinLexer.LineComment: - var commentType = SchemeAndroidIDE.COMMENT; - - // highlight special line comments - var commentText = token.getText(); - if (commentText.length() > 2) { - commentText = commentText.substring(2); - commentText = commentText.trim(); - - if ("todo".equalsIgnoreCase(commentText.substring(0, 4))) { - commentType = SchemeAndroidIDE.TODO_COMMENT; - } else if ("fixme".equalsIgnoreCase(commentText.substring(0, 5))) { - commentType = SchemeAndroidIDE.FIXME_COMMENT; - } - } - - colors.addIfNeeded(line, column, withoutCompletion(commentType)); - break; - case KotlinLexer.ShebangLine: - case KotlinLexer.DelimitedComment: - case KotlinLexer.Inside_Comment: - colors.addIfNeeded(line, column, forComment()); - break; - - default: - colors.addIfNeeded(line, column, get(TEXT_NORMAL)); - break; - } - - isFirst = false; - } - - colors.determine(lastLine); - styles.spans = colors.build(); - - return styles; - } -} diff --git a/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinAnalyzer.kt b/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinAnalyzer.kt new file mode 100644 index 000000000..74d46cfa4 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinAnalyzer.kt @@ -0,0 +1,236 @@ +/* + * 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.kotlin + +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.kotlin.KotlinLexer +import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.ANNOTATION +import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.LITERAL +import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.OPERATOR +import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.TEXT_NORMAL +import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forComment +import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forKeyword +import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forString +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.makeStyle +import io.github.rosemoe.sora.text.Content + +/** + * Incremental syntax analyzer for Kotlin. + * + * @author Akash Yadav + */ +class KotlinAnalyzer : BaseIncrementalAnalyzeManager(KotlinLexer::class.java) { + + override fun computeBlocks( + text: Content?, + delegate: CodeBlockAnalyzeDelegate, + ): MutableList { + return mutableListOf() + } + + override fun getMultilineTokenStartEndTypes(): Array { + val start = intArrayOf(KotlinLexer.DIV, KotlinLexer.MULT) + val end = intArrayOf(KotlinLexer.MULT, KotlinLexer.DIV) + return arrayOf(start, end) + } + + override fun generateSpans( + tokens: LineTokenizeResult, + ): MutableList { + val spans = mutableListOf() + var first = true + for (token in tokens.tokens) { + val type = token.type + val offset = token.startIndex + + when (type) { + KotlinLexer.WS -> + if (first) { + spans.add(Span.obtain(0, makeStyle(TEXT_NORMAL))) + first = false + } + KotlinLexer.AT_NO_WS, + KotlinLexer.AT_POST_WS, + KotlinLexer.AT_PRE_WS, + KotlinLexer.AT_BOTH_WS -> spans.add(Span.obtain(offset, makeStyle(ANNOTATION))) + KotlinLexer.ANNOTATION, + KotlinLexer.ABSTRACT, + KotlinLexer.BY, + KotlinLexer.CATCH, + KotlinLexer.COMPANION, + KotlinLexer.CONSTRUCTOR, + KotlinLexer.CROSSINLINE, + KotlinLexer.DATA, + KotlinLexer.DYNAMIC, + KotlinLexer.ENUM, + KotlinLexer.EXTERNAL, + KotlinLexer.FINAL, + KotlinLexer.FINALLY, + KotlinLexer.IMPORT, + KotlinLexer.INFIX, + KotlinLexer.INIT, + KotlinLexer.INLINE, + KotlinLexer.INNER, + KotlinLexer.INTERNAL, + KotlinLexer.LATEINIT, + KotlinLexer.NOINLINE, + KotlinLexer.OPEN, + KotlinLexer.OPERATOR, + KotlinLexer.OUT, + KotlinLexer.OVERRIDE, + KotlinLexer.PRIVATE, + KotlinLexer.PROTECTED, + KotlinLexer.PUBLIC, + KotlinLexer.REIFIED, + KotlinLexer.SEALED, + KotlinLexer.TAILREC, + KotlinLexer.VARARG, + KotlinLexer.WHERE, + KotlinLexer.GET, + KotlinLexer.SET, + KotlinLexer.FIELD, + KotlinLexer.PROPERTY, + KotlinLexer.RECEIVER, + KotlinLexer.PARAM, + KotlinLexer.SETPARAM, + KotlinLexer.DELEGATE, + KotlinLexer.FILE, + KotlinLexer.EXPECT, + KotlinLexer.ACTUAL, + KotlinLexer.VALUE, + KotlinLexer.CONST, + KotlinLexer.SUSPEND, + KotlinLexer.RETURN_AT, + KotlinLexer.CONTINUE_AT, + KotlinLexer.BREAK_AT, + KotlinLexer.THIS_AT, + KotlinLexer.SUPER_AT, + KotlinLexer.PACKAGE, + KotlinLexer.CLASS, + KotlinLexer.INTERFACE, + KotlinLexer.FUN, + KotlinLexer.OBJECT, + KotlinLexer.VAL, + KotlinLexer.VAR, + KotlinLexer.TYPE_ALIAS, + KotlinLexer.THIS, + KotlinLexer.SUPER, + KotlinLexer.TYPEOF, + KotlinLexer.IF, + KotlinLexer.ELSE, + KotlinLexer.WHEN, + KotlinLexer.TRY, + KotlinLexer.FOR, + KotlinLexer.DO, + KotlinLexer.WHILE, + KotlinLexer.THROW, + KotlinLexer.RETURN, + KotlinLexer.CONTINUE, + KotlinLexer.BREAK, + KotlinLexer.AS, + KotlinLexer.IS, + KotlinLexer.IN, + KotlinLexer.NOT_IS, + KotlinLexer.NOT_IN -> spans.add(Span.obtain(offset, forKeyword())) + KotlinLexer.RealLiteral, + KotlinLexer.FloatLiteral, + KotlinLexer.DoubleLiteral, + KotlinLexer.IntegerLiteral, + KotlinLexer.HexLiteral, + KotlinLexer.BinLiteral, + KotlinLexer.UnsignedLiteral, + KotlinLexer.LongLiteral, + KotlinLexer.BooleanLiteral, + KotlinLexer.NullLiteral, + KotlinLexer.CharacterLiteral -> spans.add(Span.obtain(offset, makeStyle(LITERAL))) + KotlinLexer.QUOTE_OPEN, + KotlinLexer.TRIPLE_QUOTE_OPEN, + KotlinLexer.QUOTE_CLOSE, + KotlinLexer.LineStrRef, + KotlinLexer.LineStrText, + KotlinLexer.LineStrEscapedChar, + KotlinLexer.LineStrExprStart, + KotlinLexer.TRIPLE_QUOTE_CLOSE, + KotlinLexer.MultiLineStringQuote, + KotlinLexer.MultiLineStrRef, + KotlinLexer.MultiLineStrText, + KotlinLexer.MultiLineStrExprStart -> spans.add(Span.obtain(offset, forString())) + KotlinLexer.RESERVED, + KotlinLexer.DOT, + KotlinLexer.COMMA, + KotlinLexer.LPAREN, + KotlinLexer.RPAREN, + KotlinLexer.LSQUARE, + KotlinLexer.RSQUARE, + KotlinLexer.LCURL, + KotlinLexer.RCURL, + KotlinLexer.MULT, + KotlinLexer.MOD, + KotlinLexer.DIV, + KotlinLexer.ADD, + KotlinLexer.SUB, + KotlinLexer.INCR, + KotlinLexer.DECR, + KotlinLexer.CONJ, + KotlinLexer.DISJ, + KotlinLexer.EXCL_WS, + KotlinLexer.EXCL_NO_WS, + KotlinLexer.COLON, + KotlinLexer.SEMICOLON, + KotlinLexer.ASSIGNMENT, + KotlinLexer.ADD_ASSIGNMENT, + KotlinLexer.SUB_ASSIGNMENT, + KotlinLexer.MULT_ASSIGNMENT, + KotlinLexer.DIV_ASSIGNMENT, + KotlinLexer.MOD_ASSIGNMENT, + KotlinLexer.ARROW, + KotlinLexer.DOUBLE_ARROW, + KotlinLexer.RANGE, + KotlinLexer.COLONCOLON, + KotlinLexer.DOUBLE_SEMICOLON, + KotlinLexer.QUEST_WS, + KotlinLexer.QUEST_NO_WS, + KotlinLexer.LANGLE, + KotlinLexer.RANGLE, + KotlinLexer.LE, + KotlinLexer.GE, + KotlinLexer.EXCL_EQ, + KotlinLexer.EXCL_EQEQ, + KotlinLexer.`AS_SAFE`, + KotlinLexer.EQEQ, + KotlinLexer.EQEQEQ, + KotlinLexer.SINGLE_QUOTE -> spans.add(Span.obtain(offset, makeStyle(OPERATOR))) + KotlinLexer.LineComment -> handleLineCommentSpan(token, spans, offset) + KotlinLexer.ShebangLine, + KotlinLexer.DelimitedComment, + KotlinLexer.Inside_Comment -> spans.add(Span.obtain(offset, forComment())) + else -> spans.add(Span.obtain(offset, makeStyle(TEXT_NORMAL))) + } + } + return spans + } + + override fun handleIncompleteToken(token: IncrementalToken) { + token.type = KotlinLexer.DelimitedComment + } +} diff --git a/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinLanguage.java b/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinLanguage.java index 59267e9a8..36a606143 100644 --- a/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinLanguage.java +++ b/app/src/main/java/com/itsaky/androidide/language/kotlin/KotlinLanguage.java @@ -23,8 +23,6 @@ import androidx.annotation.NonNull; import com.itsaky.androidide.language.IDELanguage; -import com.itsaky.androidide.language.java.JavaLanguage; -import com.itsaky.androidide.utils.CharSequenceReader; import com.itsaky.androidide.utils.ILogger; import org.antlr.v4.runtime.CharStreams;