Implemented incremental analyzer for Kotlin

This commit is contained in:
Akash Yadav
2022-06-21 20:31:03 +05:30
parent 1dfa54bc17
commit c6c49ea981
3 changed files with 236 additions and 278 deletions
@@ -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<Void> {
private static final ILogger LOG = ILogger.newInstance("KotlinAnalyzer");
@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 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;
}
}
@@ -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 <https://www.gnu.org/licenses/>.
*/
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<CodeBlock> {
return mutableListOf()
}
override fun getMultilineTokenStartEndTypes(): Array<IntArray> {
val start = intArrayOf(KotlinLexer.DIV, KotlinLexer.MULT)
val end = intArrayOf(KotlinLexer.MULT, KotlinLexer.DIV)
return arrayOf(start, end)
}
override fun generateSpans(
tokens: LineTokenizeResult<LineState, IncrementalToken>,
): MutableList<Span> {
val spans = mutableListOf<Span>()
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
}
}
@@ -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;