mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
feat: add support for more tree-sitter query predicates
#match? #not-match? #any-of?
This commit is contained in:
Generated
+15
@@ -549,6 +549,21 @@
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="kotlin">
|
||||
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
|
||||
<option name="BLANK_LINES_AFTER_CLASS_HEADER" value="1" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
<option name="CALL_PARAMETERS_WRAP" value="1" />
|
||||
<option name="CALL_PARAMETERS_LPAREN_ON_NEXT_LINE" value="false" />
|
||||
<option name="CALL_PARAMETERS_RPAREN_ON_NEXT_LINE" value="false" />
|
||||
<option name="METHOD_PARAMETERS_WRAP" value="1" />
|
||||
<option name="METHOD_PARAMETERS_LPAREN_ON_NEXT_LINE" value="false" />
|
||||
<option name="METHOD_PARAMETERS_RPAREN_ON_NEXT_LINE" value="false" />
|
||||
<option name="ASSIGNMENT_WRAP" value="0" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="protobuf">
|
||||
<option name="RIGHT_MARGIN" value="80" />
|
||||
|
||||
Generated
-1
@@ -1,6 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="GoogleStyle" />
|
||||
</state>
|
||||
</component>
|
||||
@@ -0,0 +1,412 @@
|
||||
;;; Identifiers
|
||||
|
||||
(simple_identifier) @variable
|
||||
|
||||
; `this` this keyword inside classes
|
||||
(this_expression) @variable.builtin
|
||||
|
||||
; `super` keyword inside classes
|
||||
(super_expression) @variable.builtin
|
||||
|
||||
(class_parameter
|
||||
(simple_identifier) @property)
|
||||
|
||||
(class_body
|
||||
(property_declaration
|
||||
(variable_declaration
|
||||
(simple_identifier) @property)))
|
||||
|
||||
; id_1.id_2.id_3: `id_2` and `id_3` are assumed as object properties
|
||||
(_
|
||||
(navigation_suffix
|
||||
(simple_identifier) @property))
|
||||
|
||||
; SCREAMING CASE identifiers are assumed to be constants
|
||||
((simple_identifier) @constant
|
||||
(#match? @constant "^[A-Z][A-Z0-9_]*$"))
|
||||
|
||||
(_
|
||||
(navigation_suffix
|
||||
(simple_identifier) @constant
|
||||
(#lua-match? @constant "^[A-Z][A-Z0-9_]*$")))
|
||||
|
||||
(enum_entry
|
||||
(simple_identifier) @constant)
|
||||
|
||||
(type_identifier) @type
|
||||
|
||||
; '?' operator, replacement for Java @Nullable
|
||||
(nullable_type) @punctuation.special
|
||||
|
||||
(type_alias
|
||||
(type_identifier) @type.definition)
|
||||
|
||||
((type_identifier) @type.builtin
|
||||
(#any-of? @type.builtin
|
||||
"Byte"
|
||||
"Short"
|
||||
"Int"
|
||||
"Long"
|
||||
"UByte"
|
||||
"UShort"
|
||||
"UInt"
|
||||
"ULong"
|
||||
"Float"
|
||||
"Double"
|
||||
"Boolean"
|
||||
"Char"
|
||||
"String"
|
||||
"Array"
|
||||
"ByteArray"
|
||||
"ShortArray"
|
||||
"IntArray"
|
||||
"LongArray"
|
||||
"UByteArray"
|
||||
"UShortArray"
|
||||
"UIntArray"
|
||||
"ULongArray"
|
||||
"FloatArray"
|
||||
"DoubleArray"
|
||||
"BooleanArray"
|
||||
"CharArray"
|
||||
"Map"
|
||||
"Set"
|
||||
"List"
|
||||
"EmptyMap"
|
||||
"EmptySet"
|
||||
"EmptyList"
|
||||
"MutableMap"
|
||||
"MutableSet"
|
||||
"MutableList"
|
||||
))
|
||||
|
||||
(package_header "package" @keyword
|
||||
. (identifier (simple_identifier) @namespace))
|
||||
|
||||
(import_header
|
||||
"import" @include)
|
||||
|
||||
; The last `simple_identifier` in a `import_header` will always either be a function
|
||||
; or a type. Classes can appear anywhere in the import path, unlike functions
|
||||
(import_header
|
||||
(identifier
|
||||
(simple_identifier) @type @_import)
|
||||
(import_alias
|
||||
(type_identifier) @type.definition)?
|
||||
(#match? @_import "^[A-Z]"))
|
||||
|
||||
(import_header
|
||||
(identifier
|
||||
(simple_identifier) @function @_import .)
|
||||
(import_alias
|
||||
(type_identifier) @function)?
|
||||
(#match? @_import "^[a-z]"))
|
||||
|
||||
; TODO: Separate labeled returns/breaks/continue/super/this
|
||||
; Must be implemented in the parser first
|
||||
(label) @label
|
||||
|
||||
;;; Function definitions
|
||||
|
||||
(function_declaration
|
||||
(simple_identifier) @function)
|
||||
|
||||
(getter
|
||||
("get") @function.builtin)
|
||||
(setter
|
||||
("set") @function.builtin)
|
||||
|
||||
(primary_constructor) @constructor
|
||||
(secondary_constructor
|
||||
("constructor") @constructor)
|
||||
|
||||
(constructor_invocation
|
||||
(user_type
|
||||
(type_identifier) @constructor))
|
||||
|
||||
(anonymous_initializer
|
||||
("init") @constructor)
|
||||
|
||||
(parameter
|
||||
(simple_identifier) @parameter)
|
||||
|
||||
(parameter_with_optional_type
|
||||
(simple_identifier) @parameter)
|
||||
|
||||
; lambda parameters
|
||||
(lambda_literal
|
||||
(lambda_parameters
|
||||
(variable_declaration
|
||||
(simple_identifier) @parameter)))
|
||||
|
||||
;;; Function calls
|
||||
|
||||
; function()
|
||||
(call_expression
|
||||
. (simple_identifier) @function.call)
|
||||
|
||||
; ::function
|
||||
(callable_reference
|
||||
. (simple_identifier) @function.call)
|
||||
|
||||
; object.function() or object.property.function()
|
||||
(call_expression
|
||||
(navigation_expression
|
||||
(navigation_suffix
|
||||
(simple_identifier) @function.call) . ))
|
||||
|
||||
(call_expression
|
||||
. (simple_identifier) @function.builtin
|
||||
(#any-of? @function.builtin
|
||||
"arrayOf"
|
||||
"arrayOfNulls"
|
||||
"byteArrayOf"
|
||||
"shortArrayOf"
|
||||
"intArrayOf"
|
||||
"longArrayOf"
|
||||
"ubyteArrayOf"
|
||||
"ushortArrayOf"
|
||||
"uintArrayOf"
|
||||
"ulongArrayOf"
|
||||
"floatArrayOf"
|
||||
"doubleArrayOf"
|
||||
"booleanArrayOf"
|
||||
"charArrayOf"
|
||||
"emptyArray"
|
||||
"mapOf"
|
||||
"setOf"
|
||||
"listOf"
|
||||
"emptyMap"
|
||||
"emptySet"
|
||||
"emptyList"
|
||||
"mutableMapOf"
|
||||
"mutableSetOf"
|
||||
"mutableListOf"
|
||||
"print"
|
||||
"println"
|
||||
"error"
|
||||
"TODO"
|
||||
"run"
|
||||
"runCatching"
|
||||
"repeat"
|
||||
"lazy"
|
||||
"lazyOf"
|
||||
"enumValues"
|
||||
"enumValueOf"
|
||||
"assert"
|
||||
"check"
|
||||
"checkNotNull"
|
||||
"require"
|
||||
"requireNotNull"
|
||||
"with"
|
||||
"suspend"
|
||||
"synchronized"
|
||||
))
|
||||
|
||||
;;; Literals
|
||||
|
||||
(comment) @comment
|
||||
|
||||
(shebang_line) @preproc
|
||||
|
||||
(comment) @spell
|
||||
|
||||
(real_literal) @float
|
||||
[
|
||||
(integer_literal)
|
||||
(long_literal)
|
||||
(hex_literal)
|
||||
(bin_literal)
|
||||
(unsigned_literal)
|
||||
] @number
|
||||
|
||||
[
|
||||
"null" ; should be highlighted the same as booleans
|
||||
(boolean_literal)
|
||||
] @boolean
|
||||
|
||||
(character_literal) @character
|
||||
|
||||
[
|
||||
(line_string_literal)
|
||||
(multi_line_string_literal)
|
||||
] @string
|
||||
|
||||
; NOTE: Escapes not allowed in multi-line strings
|
||||
(line_string_literal (character_escape_seq) @string.escape)
|
||||
|
||||
; There are 3 ways to define a regex
|
||||
; - "[abc]?".toRegex()
|
||||
(call_expression
|
||||
(navigation_expression
|
||||
([(line_string_literal) (multi_line_string_literal)] @string.regex)
|
||||
(navigation_suffix
|
||||
((simple_identifier) @_function
|
||||
(#eq? @_function "toRegex")))))
|
||||
|
||||
; - Regex("[abc]?")
|
||||
(call_expression
|
||||
((simple_identifier) @_function
|
||||
(#eq? @_function "Regex"))
|
||||
(call_suffix
|
||||
(value_arguments
|
||||
(value_argument
|
||||
[ (line_string_literal) (multi_line_string_literal) ] @string.regex))))
|
||||
|
||||
; - Regex.fromLiteral("[abc]?")
|
||||
(call_expression
|
||||
(navigation_expression
|
||||
((simple_identifier) @_class
|
||||
(#eq? @_class "Regex"))
|
||||
(navigation_suffix
|
||||
((simple_identifier) @_function
|
||||
(#eq? @_function "fromLiteral"))))
|
||||
(call_suffix
|
||||
(value_arguments
|
||||
(value_argument
|
||||
[ (line_string_literal) (multi_line_string_literal) ] @string.regex))))
|
||||
|
||||
;;; Keywords
|
||||
|
||||
(type_alias "typealias" @keyword)
|
||||
|
||||
(companion_object "companion" @keyword)
|
||||
|
||||
[
|
||||
(class_modifier)
|
||||
(member_modifier)
|
||||
(function_modifier)
|
||||
(property_modifier)
|
||||
(platform_modifier)
|
||||
(variance_modifier)
|
||||
(parameter_modifier)
|
||||
(visibility_modifier)
|
||||
(reification_modifier)
|
||||
(inheritance_modifier)
|
||||
] @type.qualifier
|
||||
|
||||
[
|
||||
"val"
|
||||
"var"
|
||||
"enum"
|
||||
"class"
|
||||
"object"
|
||||
"interface"
|
||||
"fun"
|
||||
; "typeof" ; NOTE: It is reserved for future use
|
||||
] @keyword
|
||||
|
||||
("fun") @keyword.function
|
||||
|
||||
(jump_expression) @keyword.return
|
||||
|
||||
[
|
||||
"if"
|
||||
"else"
|
||||
"when"
|
||||
] @conditional
|
||||
|
||||
[
|
||||
"for"
|
||||
"do"
|
||||
"while"
|
||||
] @repeat
|
||||
|
||||
[
|
||||
"try"
|
||||
"catch"
|
||||
"throw"
|
||||
"finally"
|
||||
] @exception
|
||||
|
||||
|
||||
(annotation
|
||||
"@" @attribute (use_site_target)? @attribute)
|
||||
(annotation
|
||||
(user_type
|
||||
(type_identifier) @attribute))
|
||||
(annotation
|
||||
(constructor_invocation
|
||||
(user_type
|
||||
(type_identifier) @attribute)))
|
||||
|
||||
(file_annotation
|
||||
"@" @attribute "file" @attribute ":" @attribute)
|
||||
(file_annotation
|
||||
(user_type
|
||||
(type_identifier) @attribute))
|
||||
(file_annotation
|
||||
(constructor_invocation
|
||||
(user_type
|
||||
(type_identifier) @attribute)))
|
||||
|
||||
;;; Operators & Punctuation
|
||||
|
||||
[
|
||||
"!"
|
||||
"!="
|
||||
"!=="
|
||||
"="
|
||||
"=="
|
||||
"==="
|
||||
">"
|
||||
">="
|
||||
"<"
|
||||
"<="
|
||||
"||"
|
||||
"&&"
|
||||
"+"
|
||||
"++"
|
||||
"+="
|
||||
"-"
|
||||
"--"
|
||||
"-="
|
||||
"*"
|
||||
"*="
|
||||
"/"
|
||||
"/="
|
||||
"%"
|
||||
"%="
|
||||
"?."
|
||||
"?:"
|
||||
"!!"
|
||||
"is"
|
||||
"!is"
|
||||
"in"
|
||||
"!in"
|
||||
"as"
|
||||
"as?"
|
||||
".."
|
||||
"->"
|
||||
] @operator
|
||||
|
||||
[
|
||||
"(" ")"
|
||||
"[" "]"
|
||||
"{" "}"
|
||||
] @punctuation.bracket
|
||||
|
||||
[
|
||||
"."
|
||||
","
|
||||
";"
|
||||
":"
|
||||
"::"
|
||||
] @punctuation.delimiter
|
||||
|
||||
; NOTE: `interpolated_identifier`s can be highlighted in any way
|
||||
(line_string_literal
|
||||
"$" @punctuation.special
|
||||
(interpolated_identifier) @none @variable)
|
||||
(line_string_literal
|
||||
"${" @punctuation.special
|
||||
(interpolated_expression) @none
|
||||
"}" @punctuation.special)
|
||||
|
||||
(multi_line_string_literal
|
||||
"$" @punctuation.special
|
||||
(interpolated_identifier) @none @variable)
|
||||
(multi_line_string_literal
|
||||
"${" @punctuation.special
|
||||
(interpolated_expression) @none
|
||||
"}" @punctuation.special)
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.editor.language.treesitter.predicates
|
||||
|
||||
import com.itsaky.androidide.treesitter.TSQuery
|
||||
import com.itsaky.androidide.treesitter.TSQueryMatch
|
||||
import com.itsaky.androidide.treesitter.TSQueryPredicateStep
|
||||
import io.github.rosemoe.sora.editor.ts.predicate.PredicateResult
|
||||
import io.github.rosemoe.sora.editor.ts.predicate.TsClientPredicateStep
|
||||
import io.github.rosemoe.sora.editor.ts.predicate.TsPredicate
|
||||
import io.github.rosemoe.sora.editor.ts.predicate.builtin.getCaptureContent
|
||||
|
||||
/**
|
||||
* [TsPredicate] implementation for '#any-of?' query predicates.
|
||||
*
|
||||
* Syntax : `"#any-of?" @capture "string" ["string", ... ] Done`
|
||||
*
|
||||
* Checks if the text of `@capture` matches (literally) any of the `"string"` defined.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
object AnyOfPredicate : TreeSitterPredicate() {
|
||||
|
||||
override val name: String
|
||||
get() = "any-of"
|
||||
|
||||
override fun canHandle(steps: List<TsClientPredicateStep>): Boolean {
|
||||
return steps.size > 4 &&
|
||||
steps.let {
|
||||
it[0].predicateType == TSQueryPredicateStep.Type.String &&
|
||||
it[1].predicateType == TSQueryPredicateStep.Type.Capture &&
|
||||
it[it.lastIndex].predicateType == TSQueryPredicateStep.Type.Done &&
|
||||
it.subList(2, it.lastIndex - 1).all { step ->
|
||||
step.predicateType == TSQueryPredicateStep.Type.String
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun doPredicateInternal(
|
||||
tsQuery: TSQuery,
|
||||
text: CharSequence,
|
||||
match: TSQueryMatch,
|
||||
predicate: List<TsClientPredicateStep>
|
||||
): PredicateResult {
|
||||
val captured = getCaptureContent(tsQuery, match, predicate[1].content, text)
|
||||
val toMatch = predicate.subList(2, predicate.lastIndex - 1).map { it.content }
|
||||
for (capture in captured) {
|
||||
if (capture !in toMatch) {
|
||||
return PredicateResult.REJECT
|
||||
}
|
||||
}
|
||||
return PredicateResult.ACCEPT
|
||||
}
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* 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.editor.language.treesitter.predicates
|
||||
|
||||
import com.itsaky.androidide.treesitter.TSQuery
|
||||
import com.itsaky.androidide.treesitter.TSQueryMatch
|
||||
import com.itsaky.androidide.treesitter.TSQueryPredicateStep
|
||||
import io.github.rosemoe.sora.editor.ts.predicate.PredicateResult
|
||||
import io.github.rosemoe.sora.editor.ts.predicate.TsClientPredicateStep
|
||||
import io.github.rosemoe.sora.editor.ts.predicate.TsPredicate
|
||||
import io.github.rosemoe.sora.editor.ts.predicate.builtin.getCaptureContent
|
||||
import io.github.rosemoe.sora.editor.ts.predicate.builtin.parametersMatch
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.regex.PatternSyntaxException
|
||||
|
||||
/**
|
||||
* [TsPredicate] implementation for '#match?' query predicates.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
open class MatchPredicate protected constructor() : TreeSitterPredicate() {
|
||||
|
||||
override val name: String
|
||||
get() = "match"
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmField
|
||||
val INSTANCE = MatchPredicate()
|
||||
|
||||
@JvmField
|
||||
val PARAMETERS =
|
||||
arrayOf(
|
||||
TSQueryPredicateStep.Type.String,
|
||||
TSQueryPredicateStep.Type.Capture,
|
||||
TSQueryPredicateStep.Type.String,
|
||||
TSQueryPredicateStep.Type.Done
|
||||
)
|
||||
}
|
||||
|
||||
private val cache = ConcurrentHashMap<String, Regex>()
|
||||
|
||||
override fun doPredicateInternal(
|
||||
tsQuery: TSQuery,
|
||||
text: CharSequence,
|
||||
match: TSQueryMatch,
|
||||
predicate: List<TsClientPredicateStep>
|
||||
): PredicateResult {
|
||||
val captured = getCaptureContent(tsQuery, match, predicate[1].content, text)
|
||||
try {
|
||||
var regex = cache[predicate[2].content]
|
||||
if (regex == null) {
|
||||
regex = Regex(predicate[2].content)
|
||||
cache[predicate[2].content] = regex
|
||||
}
|
||||
for (str in captured) {
|
||||
if (regex.find(str) == null) {
|
||||
return PredicateResult.REJECT
|
||||
}
|
||||
}
|
||||
return PredicateResult.ACCEPT
|
||||
} catch (e: PatternSyntaxException) {
|
||||
e.printStackTrace()
|
||||
return PredicateResult.UNHANDLED
|
||||
}
|
||||
}
|
||||
|
||||
override fun canHandle(steps: List<TsClientPredicateStep>): Boolean {
|
||||
return parametersMatch(steps, PARAMETERS)
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.editor.language.treesitter.predicates
|
||||
|
||||
import com.itsaky.androidide.treesitter.TSQuery
|
||||
import com.itsaky.androidide.treesitter.TSQueryMatch
|
||||
import io.github.rosemoe.sora.editor.ts.predicate.PredicateResult
|
||||
import io.github.rosemoe.sora.editor.ts.predicate.TsClientPredicateStep
|
||||
import io.github.rosemoe.sora.editor.ts.predicate.TsPredicate
|
||||
|
||||
/**
|
||||
* [TsPredicate] implementation for '#not-match?' query predicates.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
object NotMatchPredicate : MatchPredicate() {
|
||||
|
||||
override val name: String
|
||||
get() = "not-match"
|
||||
|
||||
override fun doPredicateInternal(
|
||||
tsQuery: TSQuery,
|
||||
text: CharSequence,
|
||||
match: TSQueryMatch,
|
||||
predicate: List<TsClientPredicateStep>
|
||||
): PredicateResult {
|
||||
|
||||
// Invert the result of the match predicate
|
||||
return when (val result = super.doPredicateInternal(tsQuery, text, match, predicate)) {
|
||||
PredicateResult.ACCEPT -> PredicateResult.REJECT
|
||||
PredicateResult.REJECT -> PredicateResult.ACCEPT
|
||||
else -> result
|
||||
}
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.editor.language.treesitter.predicates
|
||||
|
||||
import com.itsaky.androidide.treesitter.TSQuery
|
||||
import com.itsaky.androidide.treesitter.TSQueryMatch
|
||||
import io.github.rosemoe.sora.editor.ts.predicate.PredicateResult
|
||||
import io.github.rosemoe.sora.editor.ts.predicate.TsClientPredicateStep
|
||||
import io.github.rosemoe.sora.editor.ts.predicate.TsPredicate
|
||||
|
||||
/**
|
||||
* Base class for tree-sitter predicate implementations.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
abstract class TreeSitterPredicate : TsPredicate {
|
||||
|
||||
/** The name of the predicate that will be used to match. */
|
||||
abstract val name: String
|
||||
|
||||
/**
|
||||
* Whether the implmentation can handle the given predicate steps.
|
||||
*
|
||||
* @param steps The predicate steps.
|
||||
* @return `true` if and only if the implementatin can handle the given predicate steps, `false`
|
||||
* otherwise.
|
||||
*/
|
||||
abstract fun canHandle(steps: List<TsClientPredicateStep>): Boolean
|
||||
|
||||
/**
|
||||
* Performs the predicate check.
|
||||
*
|
||||
* @param tsQuery The [TSQuery] for the predicate.
|
||||
* @param text The editor text.
|
||||
* @param match The [TSQueryMatch] object.
|
||||
* @param predicate The predicate steps.
|
||||
* @return The result of the predicate check.
|
||||
*/
|
||||
abstract fun doPredicateInternal(
|
||||
tsQuery: TSQuery,
|
||||
text: CharSequence,
|
||||
match: TSQueryMatch,
|
||||
predicate: List<TsClientPredicateStep>
|
||||
): PredicateResult
|
||||
|
||||
override fun doPredicate(
|
||||
tsQuery: TSQuery,
|
||||
text: CharSequence,
|
||||
match: TSQueryMatch,
|
||||
predicate: List<TsClientPredicateStep>
|
||||
): PredicateResult {
|
||||
|
||||
if (predicate.isEmpty() || predicate[0].content != "${name}?" || !canHandle(predicate)) {
|
||||
return PredicateResult.UNHANDLED
|
||||
}
|
||||
|
||||
return doPredicateInternal(tsQuery, text, match, predicate)
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,9 @@ package com.itsaky.androidide.editor.schemes
|
||||
|
||||
import android.content.Context
|
||||
import com.itsaky.androidide.editor.language.treesitter.TreeSitterLanguageSpec
|
||||
import com.itsaky.androidide.editor.language.treesitter.predicates.AnyOfPredicate
|
||||
import com.itsaky.androidide.editor.language.treesitter.predicates.MatchPredicate
|
||||
import com.itsaky.androidide.editor.language.treesitter.predicates.NotMatchPredicate
|
||||
import com.itsaky.androidide.treesitter.TSLanguage
|
||||
import com.itsaky.androidide.utils.ILogger
|
||||
import io.github.rosemoe.sora.editor.ts.LocalsCaptureSpec
|
||||
@@ -33,7 +36,7 @@ object LanguageSpecProvider {
|
||||
|
||||
const val BASE_SPEC_PATH = "editor/treesitter"
|
||||
private val log = ILogger.newInstance("LanguageSpecProvider")
|
||||
|
||||
|
||||
@JvmStatic
|
||||
@JvmOverloads
|
||||
fun getLanguageSpec(
|
||||
@@ -42,15 +45,20 @@ object LanguageSpecProvider {
|
||||
lang: TSLanguage,
|
||||
localsCaptureSpec: LocalsCaptureSpec = LocalsCaptureSpec.DEFAULT
|
||||
): TreeSitterLanguageSpec {
|
||||
val editorLangSpec = TsLanguageSpec(
|
||||
language = lang,
|
||||
highlightScmSource = readScheme(context, type, "highlights"),
|
||||
localsScmSource = readScheme(context, type, "locals"),
|
||||
codeBlocksScmSource = readScheme(context, type, "blocks"),
|
||||
bracketsScmSource = readScheme(context, type, "brackets"),
|
||||
localsCaptureSpec = localsCaptureSpec
|
||||
val editorLangSpec =
|
||||
TsLanguageSpec(
|
||||
language = lang,
|
||||
highlightScmSource = readScheme(context, type, "highlights"),
|
||||
localsScmSource = readScheme(context, type, "locals"),
|
||||
codeBlocksScmSource = readScheme(context, type, "blocks"),
|
||||
bracketsScmSource = readScheme(context, type, "brackets"),
|
||||
localsCaptureSpec = localsCaptureSpec,
|
||||
predicates = listOf(MatchPredicate.INSTANCE, NotMatchPredicate, AnyOfPredicate)
|
||||
)
|
||||
return TreeSitterLanguageSpec(
|
||||
spec = editorLangSpec,
|
||||
indentsQueryScm = readScheme(context, type, "indents")
|
||||
)
|
||||
return TreeSitterLanguageSpec(spec = editorLangSpec, indentsQueryScm = readScheme(context, type, "indents"))
|
||||
}
|
||||
|
||||
private fun readScheme(context: Context, type: String, name: String): String {
|
||||
|
||||
Reference in New Issue
Block a user