feat: add indentation support for XML language

This commit is contained in:
Akash Yadav
2023-01-04 11:59:34 +05:30
parent 3db99be599
commit 88db25767a
4 changed files with 80 additions and 20 deletions
@@ -0,0 +1,16 @@
; Indents query for AndroidIDE
(xml_decl
"<?" @indent
"?>" @outdent)
(empty_element
tag_name: (name) @indent
"/>" @outdent)
(end_tag_element
(tag_start
tag_name: (name) @indent)
(tag_end
"</" @outdent)
)
@@ -26,6 +26,7 @@ import com.itsaky.androidide.editor.schemes.LanguageSpecProvider.getLanguageSpec
import com.itsaky.androidide.editor.schemes.LocalCaptureSpecProvider.newLocalCaptureSpec
import com.itsaky.androidide.treesitter.TSLanguage
import com.itsaky.androidide.treesitter.TSParser
import com.itsaky.androidide.treesitter.TSQueryCapture
import com.itsaky.androidide.treesitter.TSQueryCursor
import com.itsaky.androidide.treesitter.TSQueryMatch
import com.itsaky.androidide.utils.ILogger
@@ -69,10 +70,14 @@ abstract class TreeSitterLanguage(context: Context, lang: TSLanguage, type: Stri
}
}
fun finalizeIndent(indent: Int) : Int {
open fun finalizeIndent(indent: Int): Int {
return max(0, indent) * tabSize
}
open fun validateIndentCapture(match: TSQueryMatch, capture: TSQueryCapture): Boolean {
return true
}
override fun getAnalyzeManager(): AnalyzeManager {
return this.analyzer
}
@@ -80,35 +85,51 @@ abstract class TreeSitterLanguage(context: Context, lang: TSLanguage, type: Stri
override fun getSymbolPairs(): SymbolPairMatch {
return CommonSymbolPairs()
}
override fun getIndentAdvance(line: String): Int {
return computeIndent(line, 0, line.length)
}
protected fun computeIndent(content: String, line: Int, column: Int, decrementBy: Int = 0): Int {
return TSParser().use { parser ->
parser.language = languageSpec.language
val tree = parser.parseString(line)
val tree = parser.parseString(content)
return@use TSQueryCursor().use { cursor ->
cursor.exec(languageSpec.indentsQuery, tree.rootNode)
var indent = 0
var match: TSQueryMatch? = cursor.nextMatch()
val captures = mutableListOf<TSQueryCapture>()
while (match != null) {
for (capture in match.captures) {
val captureName =
languageSpec.indentsQuery.getCaptureNameForId(capture.index)
if (captureName == "indent") {
++indent
} else if (captureName == "outdent") {
--indent
}
}
captures.addAll(match.captures)
match = cursor.nextMatch()
}
if (indent > 1) {
captures.sortBy { it.node.startByte }
for (capture in captures) {
val capLine = capture.node.startPoint.row
val capCol = capture.node.endPoint.column / 2
if (capLine > line || (capLine == line && capCol > column)) {
break
}
val captureName =
languageSpec.indentsQuery.getCaptureNameForId(capture.index)
if (captureName == "indent") {
++indent
} else if (captureName == "outdent") {
--indent
}
}
if (decrementBy == 0 && indent > 1) {
indent = 1
}
finalizeIndent(indent)
finalizeIndent(indent) - decrementBy
}
}
}
@@ -22,9 +22,12 @@ import com.itsaky.androidide.editor.language.treesitter.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.TSQueryCapture
import com.itsaky.androidide.treesitter.TSQueryMatch
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.text.TextUtils.countLeadingSpaceCount
import io.github.rosemoe.sora.util.MyCharacter
/**
@@ -54,4 +57,24 @@ class XMLLanguage(context: Context) :
override fun getNewlineHandlers(): Array<NewlineHandler> {
return emptyArray()
}
override fun getIndentAdvance(content: ContentReference, line: Int, column: Int): Int {
return computeIndent(
content.toString(),
line,
column,
decrementBy = countLeadingSpaceCount(content.getLine(line), tabSize)
)
}
override fun finalizeIndent(indent: Int): Int {
// Indentation can be negative in XML
return indent * tabSize
}
override fun validateIndentCapture(match: TSQueryMatch, capture: TSQueryCapture): Boolean {
// sometimes there are captures whose start and end bytes are the same
// those capture must not be considered
return capture.node.startByte < capture.node.endByte
}
}
@@ -33,7 +33,7 @@ object LanguageSpecProvider {
private const val BASE_SPEC_PATH = "editor/treesitter"
private val log = ILogger.newInstance("LanguageSpecProvider")
@JvmStatic
@JvmOverloads
fun getLanguageSpec(