diff --git a/app/src/main/java/com/itsaky/androidide/app/IDEApplication.kt b/app/src/main/java/com/itsaky/androidide/app/IDEApplication.kt index 2a345a775..b30aed5a5 100755 --- a/app/src/main/java/com/itsaky/androidide/app/IDEApplication.kt +++ b/app/src/main/java/com/itsaky/androidide/app/IDEApplication.kt @@ -42,6 +42,7 @@ class IDEApplication : BaseApplication() { init { System.loadLibrary("android-tree-sitter") System.loadLibrary("tree-sitter-java") + System.loadLibrary("tree-sitter-xml") } override fun onCreate() { diff --git a/app/src/main/java/com/itsaky/androidide/ui/editor/CodeEditorView.java b/app/src/main/java/com/itsaky/androidide/ui/editor/CodeEditorView.java index e7d0e27a2..671140af2 100644 --- a/app/src/main/java/com/itsaky/androidide/ui/editor/CodeEditorView.java +++ b/app/src/main/java/com/itsaky/androidide/ui/editor/CodeEditorView.java @@ -234,7 +234,7 @@ public class CodeEditorView extends LinearLayout { case "java": return new JavaLanguage(getContext()); case "xml": - return new XMLLanguage(); + return new XMLLanguage(getContext()); case "gradle": return new GroovyLanguage(); case "kt": diff --git a/dependencies.gradle b/dependencies.gradle index 547854b90..a5aa282e7 100644 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -5,7 +5,7 @@ ext { oss_licenses_version = '0.10.6' nav_safeargs_version = '2.5.3' - ats_version = '1.0.9' + ats_version = '1.0.10' editor_version = '0.20.1' classpaths = [ @@ -19,7 +19,8 @@ ext { androidide = [ javac : 'io.github.itsaky:nb-javac-android:17.0.0.4-SNAPSHOT', ats : "io.github.itsaky:android-tree-sitter:$ats_version", - ts_java: "io.github.itsaky:tree-sitter-java:$ats_version" + ts_java: "io.github.itsaky:tree-sitter-java:$ats_version", + ts_xml: "io.github.itsaky:tree-sitter-xml:$ats_version" ] common = [ diff --git a/editor/build.gradle b/editor/build.gradle index f45f3e033..b4fb5f13d 100644 --- a/editor/build.gradle +++ b/editor/build.gradle @@ -41,6 +41,7 @@ dependencies { api androidide.ats api androidide.ts_java + api androidide.ts_xml api common.editor api common.editor_ts diff --git a/editor/src/main/assets/editor/schemes/default/default.json b/editor/src/main/assets/editor/schemes/default/default.json index 408cd4cb2..a8a7e82b0 100644 --- a/editor/src/main/assets/editor/schemes/default/default.json +++ b/editor/src/main/assets/editor/schemes/default/default.json @@ -12,6 +12,9 @@ "onSurfaceVariant": "#4a4644", "outline": "#7a7572", "text.selected": "#ff5252", + "black": "#000000", + "white": "#ffffff", + "type": "#2196f3", "keyword": "#1e88e5", "operator": "#2196f3", @@ -24,12 +27,14 @@ "comment": "#a8a8a8", "func.decl": "#2962ff", "func.call": "#2962ff", - "black": "#000000", - "white": "#ffffff" + + "xml.tag": "#1e88e5", + "xml.ref": "#2196f3", + "ns_prefix": "#9c27b0" }, "editor": "@editor.json", "languages": [ - "@log.json", - "@java.json" + "@java.json", + "@xml.json" ] } \ No newline at end of file diff --git a/editor/src/main/assets/editor/schemes/default/log.json b/editor/src/main/assets/editor/schemes/default/log.json deleted file mode 100644 index 576ff7e10..000000000 --- a/editor/src/main/assets/editor/schemes/default/log.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "types": [ - "@log" - ], - "styles": { - "text.error": "#f44336", - "text.warning": "#ffeb3b", - "text.info": "#4caf50", - "text.debug": "#f5f5f5", - "text.verbose": "#4fc3f7", - "priority.error": { - "fg": "@black", - "bg": "#f44336", - "completion": false - }, - "priority.warning": { - "fg": "@black", - "bg": "#ffeb3b" - }, - "priority.info": { - "fg": "@white", - "bg": "#1f65c0" - }, - "priority.debug": { - "fg": "@white", - "bg": "#9e9d24" - }, - "priority.verbose": { - "fg": "@black", - "bg": "@white" - } - } -} \ No newline at end of file diff --git a/editor/src/main/assets/editor/schemes/default/scheme.prop b/editor/src/main/assets/editor/schemes/default/scheme.prop index 2ac0edde1..64364fbfe 100644 --- a/editor/src/main/assets/editor/schemes/default/scheme.prop +++ b/editor/src/main/assets/editor/schemes/default/scheme.prop @@ -2,13 +2,13 @@ scheme.name=AndroidIDE Default # The version code of the color scheme -scheme.version=1 +scheme.version=2 # Whether the scheme is dark or light scheme.isDark=false # Languages that scheme supports -scheme.langs=java,@log +scheme.langs=java,xml # The JSON color scheme definition file scheme.file=default.json \ No newline at end of file diff --git a/editor/src/main/assets/editor/schemes/default/xml.json b/editor/src/main/assets/editor/schemes/default/xml.json new file mode 100644 index 000000000..f9bc4aef3 --- /dev/null +++ b/editor/src/main/assets/editor/schemes/default/xml.json @@ -0,0 +1,33 @@ +{ + "types": ["xml"], + "styles": { + "xml_decl": { + "fg": "@operator", + "bold": true + }, + "element.tag": { + "fg": "@xml.tag", + "bold": true + }, + "ns_declarator": { + "fg": "@keyword", + "bold": true + }, + "xml.ref": { + "fg": "@xml.ref", + "italic": true + }, + "comment": { + "fg": "@comment", + "italic": true + }, + "operator": "@operator", + "cdata.start": "@xml.tag", + "cdata.end": "@xml.tag", + "cdata": "@onSurface", + "xmlns.prefix": "@ns_prefix", + "attr.prefix" : "@ns_prefix", + "attr.name": "@onSurface", + "attr.value": "@string" + } +} \ No newline at end of file diff --git a/editor/src/main/assets/editor/treesitter/xml/brackets.scm b/editor/src/main/assets/editor/treesitter/xml/brackets.scm new file mode 100644 index 000000000..b97564d1d --- /dev/null +++ b/editor/src/main/assets/editor/treesitter/xml/brackets.scm @@ -0,0 +1,23 @@ +; bracket pair highlights query for sora-editor + +; for xml declaration, mark the opening '' as brackets close +(xml_decl + "" @editor.brackets.close) + +; for empty element tag, mark opening '<' and closing '/>' as brackets open and close +(empty_element + "<" @editor.brackets.open + "/>" @editor.brackets.close) + +; in case of an element which has an end tag +; mark the tag name in tag_start as bracket open +; and the tag name in tag_end as bracket close +(tag_start + tag_name: (name) @editor.brackets.open) +(tag_end + tag_name: (name) @editor.brackets.close) + +; bracket pairs for CDATA sections +(cdata_start) @editor.brackets.open +(cdata_end) @editor.brackets.close \ No newline at end of file diff --git a/editor/src/main/assets/editor/treesitter/xml/highlights.scm b/editor/src/main/assets/editor/treesitter/xml/highlights.scm new file mode 100644 index 000000000..034793219 --- /dev/null +++ b/editor/src/main/assets/editor/treesitter/xml/highlights.scm @@ -0,0 +1,45 @@ +(xml_decl + decl: "xml" @xml_decl) + +(xml_version + version_attr: "version" @attr.name) + +(xml_encoding + encoding_attr: "encoding" @attr.name) + +(xml_version_value) @attr.value +(xml_encoding_value) @attr.value + +(empty_element + tag_name: (name) @element.tag) + +(tag_start + tag_name: (name) @element.tag) + +(tag_end + tag_name: (name) @element.tag) + +"xmlns" @ns_declarator +(ns_decl + xmlns_prefix: (name) @xmlns.prefix) + +(xml_attr + ns_prefix: (name) @attr.prefix + attr_name: (name) @attr.name) + +(attr_value) @attr.value + +(comment) @comment + +(entity_ref) @xml.ref +(char_ref) @xml.ref + +(comment) @comment + +(cdata_start) @cdata.start +(cdata_end) @cdata.end +(cdata) @cdata.data + +(eq) @operator + +["<" "" "" "/>"] @operator \ No newline at end of file diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/TreeSitterLanguage.kt b/editor/src/main/java/com/itsaky/androidide/editor/language/TreeSitterLanguage.kt index 1da63ffeb..d6d62a7dc 100644 --- a/editor/src/main/java/com/itsaky/androidide/editor/language/TreeSitterLanguage.kt +++ b/editor/src/main/java/com/itsaky/androidide/editor/language/TreeSitterLanguage.kt @@ -28,6 +28,7 @@ import io.github.rosemoe.sora.editor.ts.TsAnalyzeManager import io.github.rosemoe.sora.editor.ts.TsLanguageSpec import io.github.rosemoe.sora.editor.ts.TsTheme import io.github.rosemoe.sora.lang.analysis.AnalyzeManager +import io.github.rosemoe.sora.widget.SymbolPairMatch /** * Tree Sitter language implementation. @@ -66,6 +67,10 @@ abstract class TreeSitterLanguage(context: Context, lang: TSLanguage, type: Stri override fun getAnalyzeManager(): AnalyzeManager { return this.analyzer } + + override fun getSymbolPairs(): SymbolPairMatch { + return CommonSymbolPairs() + } override fun destroy() { languageSpec.close() diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/xml/XMLLanguage.java b/editor/src/main/java/com/itsaky/androidide/editor/language/xml/XMLLanguage.java deleted file mode 100755 index a4e49749d..000000000 --- a/editor/src/main/java/com/itsaky/androidide/editor/language/xml/XMLLanguage.java +++ /dev/null @@ -1,119 +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.editor.language.xml; - -import androidx.annotation.NonNull; - -import com.itsaky.androidide.editor.language.CommonSymbolPairs; -import com.itsaky.androidide.editor.language.IDELanguage; -import com.itsaky.androidide.lexers.xml.XMLLexer; -import com.itsaky.androidide.lsp.api.ILanguageServer; -import com.itsaky.androidide.lsp.api.ILanguageServerRegistry; -import com.itsaky.androidide.lsp.xml.XMLLanguageServer; -import com.itsaky.androidide.utils.ILogger; - -import org.antlr.v4.runtime.CharStreams; -import org.antlr.v4.runtime.Token; - -import java.io.StringReader; - -import io.github.rosemoe.sora.lang.analysis.AnalyzeManager; -import io.github.rosemoe.sora.lang.smartEnter.NewlineHandler; -import io.github.rosemoe.sora.text.ContentReference; -import io.github.rosemoe.sora.util.MyCharacter; -import io.github.rosemoe.sora.widget.SymbolPairMatch; - -public class XMLLanguage extends IDELanguage { - - private static final ILogger LOG = ILogger.newInstance("XMLLanguage"); - private final NewlineHandler[] newlineHandlers; - private XMLAnalyzer analyzer; - - public XMLLanguage() { - this.analyzer = new XMLAnalyzer(); - this.newlineHandlers = new NewlineHandler[0]; - } - - @Override - protected ILanguageServer getLanguageServer() { - return ILanguageServerRegistry.getDefault().getServer(XMLLanguageServer.SERVER_ID); - } - - @Override - protected boolean checkIsCompletionChar(final char c) { - return MyCharacter.isJavaIdentifierPart(c) || c == '<' || c == '/'; - } - - @Override - public int getIndentAdvance(@NonNull String content) { - try { - XMLLexer lexer = new XMLLexer(CharStreams.fromReader(new StringReader(content))); - Token token; - int advance = 0; - while (((token = lexer.nextToken()) != null && token.getType() != token.EOF)) { - switch (token.getType()) { - case XMLLexer.OPEN: - advance++; - break; - case XMLLexer.CLOSE: - case XMLLexer.SLASH_CLOSE: - advance--; - break; - default: - break; - } - } - advance = Math.max(0, advance); - return advance * getTabSize(); - } catch (Throwable e) { - LOG.error("Failed to compute indent advance", e); - } - return 0; - } - - @NonNull - @Override - public AnalyzeManager getAnalyzeManager() { - return analyzer; - } - - @Override - public int getInterruptionLevel() { - return INTERRUPTION_LEVEL_STRONG; - } - - @Override - public int getIndentAdvance(@NonNull ContentReference content, int line, int column) { - final var text = content.getLine(line).substring(0, column); - return getIndentAdvance(text.trim()); - } - - @Override - public SymbolPairMatch getSymbolPairs() { - return new CommonSymbolPairs(); - } - - @Override - public NewlineHandler[] getNewlineHandlers() { - return newlineHandlers; - } - - @Override - public void destroy() { - analyzer = null; - } -} diff --git a/editor/src/main/java/com/itsaky/androidide/editor/language/xml/XMLLanguage.kt b/editor/src/main/java/com/itsaky/androidide/editor/language/xml/XMLLanguage.kt new file mode 100644 index 000000000..a965177d4 --- /dev/null +++ b/editor/src/main/java/com/itsaky/androidide/editor/language/xml/XMLLanguage.kt @@ -0,0 +1,61 @@ +/* + * 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.editor.language.xml + +import android.content.Context +import com.itsaky.androidide.editor.language.TreeSitterLanguage +import com.itsaky.androidide.lsp.api.ILanguageServer +import com.itsaky.androidide.lsp.api.ILanguageServerRegistry +import com.itsaky.androidide.lsp.xml.XMLLanguageServer +import com.itsaky.androidide.treesitter.xml.TSLanguageXml +import io.github.rosemoe.sora.lang.smartEnter.NewlineHandler +import io.github.rosemoe.sora.text.ContentReference +import io.github.rosemoe.sora.util.MyCharacter + +/** + * Tree Sitter language XML language. + * + * @author Akash Yadav + */ +class XMLLanguage(context: Context) : + TreeSitterLanguage(context, lang = TSLanguageXml.newInstance(), type = TS_TYPE) { + + companion object { + const val TS_TYPE = "xml" + } + + override fun getLanguageServer(): ILanguageServer? { + return ILanguageServerRegistry.getDefault().getServer(XMLLanguageServer.SERVER_ID) + } + + override fun checkIsCompletionChar(c: Char): Boolean { + return MyCharacter.isJavaIdentifierPart(c) || c == '<' || c == '/' + } + + override fun getInterruptionLevel(): Int { + return INTERRUPTION_LEVEL_STRONG + } + + override fun getIndentAdvance(content: ContentReference, line: Int, column: Int): Int { + return 0 + } + + override fun getNewlineHandlers(): Array { + return emptyArray() + } +} diff --git a/tests/test-home/.androidide/init/model.jar b/tests/test-home/.androidide/init/model.jar index 2ee5a1531..fb153043c 100644 Binary files a/tests/test-home/.androidide/init/model.jar and b/tests/test-home/.androidide/init/model.jar differ diff --git a/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/ShowXmlActivity.kt b/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/ShowXmlActivity.kt index ff698e49a..40c487adc 100644 --- a/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/ShowXmlActivity.kt +++ b/uidesigner/src/main/java/com/itsaky/androidide/uidesigner/ShowXmlActivity.kt @@ -61,7 +61,7 @@ class ShowXmlActivity : BaseIDEActivity() { editor.editable = false editor.typefaceText = jetbrainsMono() editor.typefaceLineNumber = jetbrainsMono() - editor.setEditorLanguage(XMLLanguage()) + editor.setEditorLanguage(XMLLanguage(this)) editor.setText(intent?.getStringExtra(KEY_XML) ?: "") editor.setTextSize(fontSize)