mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-10-27 03:32:44 +03:00
I am in hell
This commit is contained in:
parent
e1cce9ac9a
commit
917fdf551b
@ -7,7 +7,7 @@ module.exports = {
|
|||||||
extends: [
|
extends: [
|
||||||
"eslint:recommended",
|
"eslint:recommended",
|
||||||
"plugin:node/recommended",
|
"plugin:node/recommended",
|
||||||
"plugin:jsdoc/recommended"
|
// "plugin:jsdoc/recommended"
|
||||||
],
|
],
|
||||||
overrides: [],
|
overrides: [],
|
||||||
parserOptions: {
|
parserOptions: {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
name: 'Ruby'
|
name: 'Ruby (TS)'
|
||||||
scopeName: 'source.ruby'
|
scopeName: 'source.ruby.ts'
|
||||||
type: 'tree-sitter'
|
type: 'tree-sitter'
|
||||||
parser: 'tree-sitter-ruby'
|
parser: 'tree-sitter-ruby'
|
||||||
|
|
||||||
|
@ -1,5 +1,342 @@
|
|||||||
; Keywords
|
; NOTES:
|
||||||
|
;
|
||||||
|
; (#set! final "true") means that any later rule that matches this exact range
|
||||||
|
; will be ignored.
|
||||||
|
;
|
||||||
|
; (#set! shy "true") means that this rule will be ignored if any previous rule
|
||||||
|
; has marked this exact range, whether or not it was marked as final.
|
||||||
|
|
||||||
|
|
||||||
|
(superclass
|
||||||
|
(constant) @entity.name.type.class.ruby
|
||||||
|
.
|
||||||
|
)
|
||||||
|
|
||||||
|
(superclass
|
||||||
|
"<" @punctuation.separator.inheritance.ruby
|
||||||
|
(constant) @entity.other.inherited-class.ruby
|
||||||
|
(#set! final "true")
|
||||||
|
)
|
||||||
|
|
||||||
|
; module [Foo]
|
||||||
|
(module
|
||||||
|
name: (constant) @entity.name.type.module.ruby
|
||||||
|
(#set! final "true")
|
||||||
|
)
|
||||||
|
|
||||||
|
(singleton_class
|
||||||
|
"<<" @keyword.operator.assigment.ruby
|
||||||
|
)
|
||||||
|
|
||||||
|
(call
|
||||||
|
method: (identifier) @keyword.other.special-method (#match? @keyword.other.special-method "^(raise|loop)$")
|
||||||
|
)
|
||||||
|
|
||||||
|
; Mark `new` as a special method in all contexts, from `Foo.new` to
|
||||||
|
; `Foo::Bar::Baz.new` and so on.
|
||||||
|
(call
|
||||||
|
receiver: (_)
|
||||||
|
method: (identifier) @function.method.builtin.ruby
|
||||||
|
(#eq? @function.method.builtin.ruby "new")
|
||||||
|
)
|
||||||
|
|
||||||
|
(superclass
|
||||||
|
(scope_resolution
|
||||||
|
scope: (constant) @entity.other.inherited-class.ruby
|
||||||
|
name: (constant) @entity.other.inherited-class.ruby
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
(scope_resolution
|
||||||
|
scope: (constant) @support.class.ruby
|
||||||
|
(#set! final "true")
|
||||||
|
)
|
||||||
|
|
||||||
|
(scope_resolution
|
||||||
|
"::" @keyword.operator.namespace.ruby
|
||||||
|
(#set! final "true")
|
||||||
|
)
|
||||||
|
|
||||||
|
(scope_resolution
|
||||||
|
name: (constant) @support.class.ruby
|
||||||
|
(#set! final "true")
|
||||||
|
)
|
||||||
|
|
||||||
|
; ((variable) @keyword.other.special-method
|
||||||
|
; (#match? @keyword.other.special-method "^(extend)$"))
|
||||||
|
|
||||||
|
((identifier) @keyword.other.special-method
|
||||||
|
(#match? @keyword.other.special-method "^(private|protected|public)$"))
|
||||||
|
|
||||||
|
|
||||||
|
; Highlight the interpolation inside of a string, plus the strings that delimit
|
||||||
|
; the interpolation.
|
||||||
|
(
|
||||||
|
(interpolation
|
||||||
|
"#{" @punctuation.section.embedded.begin.ruby
|
||||||
|
"}" @punctuation.section.embedded.end.ruby
|
||||||
|
) @meta.embedded
|
||||||
|
(#set! final "true")
|
||||||
|
)
|
||||||
|
|
||||||
|
; Function calls
|
||||||
|
|
||||||
|
; TODO: The TM grammar scopes this as `keyword.control.pseudo-method.ruby`; decide on
|
||||||
|
; the best name for it.
|
||||||
|
(
|
||||||
|
(identifier) @function.method.builtin.ruby
|
||||||
|
(#eq? @function.method.builtin.ruby "require")
|
||||||
|
)
|
||||||
|
|
||||||
|
(unary
|
||||||
|
"defined?" @function.method.builtin.ruby
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
(class name: [(constant)]
|
||||||
|
@entity.name.type.class.ruby
|
||||||
|
(#set! final "true"))
|
||||||
|
|
||||||
|
; Scope the entire inside of a class body to `meta.class.ruby`; it's
|
||||||
|
; semantically useful even though it probably won't get highlighted.
|
||||||
|
(class) @meta.class.ruby
|
||||||
|
|
||||||
|
(method
|
||||||
|
name: [(identifier) (constant)] @entity.name.function.ruby
|
||||||
|
(#set! final "true")
|
||||||
|
)
|
||||||
|
; (singleton_method name: [(identifier) (constant)] @function.method)
|
||||||
|
|
||||||
|
; Identifiers
|
||||||
|
|
||||||
|
(global_variable) @variable.other.readwrite.global.ruby
|
||||||
|
|
||||||
|
(class_variable) @variable.other.readwrite.class.ruby
|
||||||
|
|
||||||
|
(instance_variable) @variable.other.readwrite.instance.ruby
|
||||||
|
|
||||||
|
(exception_variable (identifier) @variable.parameter.ruby)
|
||||||
|
(call receiver: (identifier) @variable.other.ruby)
|
||||||
|
|
||||||
|
; (call
|
||||||
|
; receiver: (constant) @support.class.ruby
|
||||||
|
; method: (identifier) @function.method.builtin.ruby
|
||||||
|
; (#eq? @function.method.builtin.ruby "new")
|
||||||
|
; )
|
||||||
|
|
||||||
|
(call
|
||||||
|
method: [(identifier) (constant)] @keyword.other.special-method (#match? @keyword.other.special-method "^(extend)$"))
|
||||||
|
|
||||||
|
|
||||||
|
; (call
|
||||||
|
; method: [(identifier) (constant)] @function.method)
|
||||||
|
|
||||||
|
; (call
|
||||||
|
; method: (scope_resolution
|
||||||
|
; scope: [(constant) (scope_resolution)] @support.class.ruby
|
||||||
|
; "::" @keyword.operator.namespace.ruby
|
||||||
|
; name: [(constant)] @support.class.ruby
|
||||||
|
; )
|
||||||
|
; )
|
||||||
|
|
||||||
|
|
||||||
|
(scope_resolution
|
||||||
|
scope: [(constant) (scope_resolution)]
|
||||||
|
"::" @keyword.operator.namespace.ruby
|
||||||
|
name: [(constant)] @support.class.ruby
|
||||||
|
(#set! final "true")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
; (call
|
||||||
|
; receiver: (constant) @constant.ruby (#match? @constant.ruby "^[A-Z\\d_]+$")
|
||||||
|
; )
|
||||||
|
(call receiver: (constant)
|
||||||
|
@support.class.ruby
|
||||||
|
(#set! final "true")
|
||||||
|
)
|
||||||
|
|
||||||
|
((identifier) @constant.builtin.ruby
|
||||||
|
(#match? @constant.builtin.ruby "^__(FILE|LINE|ENCODING)__$"))
|
||||||
|
|
||||||
|
; Anything that hasn't been highlighted yet is probably a bare identifier. Mark
|
||||||
|
; it as `constant` if it's all uppercase…
|
||||||
|
((constant) @constant.ruby
|
||||||
|
(#match? @constant.ruby "^[A-Z\\d_]+$")
|
||||||
|
(#set! final "true"))
|
||||||
|
|
||||||
|
; …otherwise treat it as a variable.
|
||||||
|
((constant) @variable.other.constant.ruby)
|
||||||
|
|
||||||
|
(self) @variable.language.self.ruby
|
||||||
|
(super) @keyword.control.pseudo-method.ruby
|
||||||
|
|
||||||
|
(block_parameter (identifier) @variable.parameter.function.block.ruby)
|
||||||
|
(block_parameters (identifier) @variable.parameter.function.block.ruby)
|
||||||
|
(destructured_parameter (identifier) @variable.parameter.function.ruby)
|
||||||
|
(hash_splat_parameter (identifier) @variable.parameter.function.splat.ruby)
|
||||||
|
(lambda_parameters (identifier) @variable.parameter.function.lambda.ruby)
|
||||||
|
(method_parameters (identifier) @variable.parameter.function.ruby)
|
||||||
|
(splat_parameter (identifier) @variable.parameter.function.splat.ruby)
|
||||||
|
|
||||||
|
; TODO: We might want to combine the name and the colon so they can get
|
||||||
|
; highlighted together as one scope. Pretty sure there's a way to do that.
|
||||||
|
|
||||||
|
; A keyword-style parameter when defining a method.
|
||||||
|
(keyword_parameter
|
||||||
|
":" @constant.other.symbol.parameter.ruby
|
||||||
|
(#set! final "true")
|
||||||
|
)
|
||||||
|
|
||||||
|
; A keyword-style argument when calling a method.
|
||||||
|
(pair
|
||||||
|
key: (hash_key_symbol) @constant.other.symbol.hashkey.ruby
|
||||||
|
":" @constant.other.symbol.hashkey.ruby
|
||||||
|
(#set! final "true")
|
||||||
|
)
|
||||||
|
|
||||||
|
(optional_parameter
|
||||||
|
name: (identifier) @variable.parameter.function.optional.ruby
|
||||||
|
)
|
||||||
|
|
||||||
|
(
|
||||||
|
(identifier) @support.function.kernel.ruby
|
||||||
|
(#match? @support.function.kernel.ruby "^(abort|at_exit|autoload|binding|callcc|caller|caller_locations|chomp|chop|eval|exec|exit|fork|format|gets|global_variables|gsub|lambda|load|local_variables|open|p|print|printf|proc|putc|puts|rand|readline|readlines|select|set_trace_func|sleep|spawn|sprintf|srand|sub|syscall|system|test|trace_var|trap|untrace_var|warn)$")
|
||||||
|
(#set! final "true")
|
||||||
|
)
|
||||||
|
|
||||||
|
;((constant) @constant.ruby
|
||||||
|
; (#match? @constant.ruby "^[A-Z\\d_]+$"))
|
||||||
|
|
||||||
|
; (identifier) @variable
|
||||||
|
|
||||||
|
; Literals
|
||||||
|
|
||||||
|
; TODO: I can't mark these as @string.quoted.double.ruby yet because the "s
|
||||||
|
; match _any_ delimiter, including single quotes and %Qs. This is probably a
|
||||||
|
; bug in tree-sitter-ruby.
|
||||||
|
(
|
||||||
|
(string
|
||||||
|
"\"" @punctuation.definition.string.begin.ruby
|
||||||
|
(string_content)
|
||||||
|
"\"" @punctuation.definition.string.end.ruby
|
||||||
|
) @string.quoted.ruby
|
||||||
|
(#set! final "true")
|
||||||
|
)
|
||||||
|
|
||||||
|
; (will match empty strings)
|
||||||
|
(string) @string.quoted.ruby
|
||||||
|
|
||||||
|
[
|
||||||
|
(bare_string)
|
||||||
|
(subshell)
|
||||||
|
(heredoc_body)
|
||||||
|
(heredoc_beginning)
|
||||||
|
] @string.unquoted.ruby
|
||||||
|
|
||||||
|
[
|
||||||
|
(simple_symbol)
|
||||||
|
(delimited_symbol)
|
||||||
|
(hash_key_symbol)
|
||||||
|
(bare_symbol)
|
||||||
|
] @constant.other.symbol.ruby
|
||||||
|
|
||||||
|
(regex) @string.special.regex
|
||||||
|
(escape_sequence) @escape
|
||||||
|
|
||||||
|
[
|
||||||
|
(integer)
|
||||||
|
(float)
|
||||||
|
] @constant.numeric.ruby
|
||||||
|
|
||||||
|
(nil) @constant.language.nil.ruby
|
||||||
|
|
||||||
|
[
|
||||||
|
(true)
|
||||||
|
(false)
|
||||||
|
] @constant.language.boolean.ruby
|
||||||
|
|
||||||
|
; TODO: tree-sitter-ruby doesn't currently let us distinguish line comments
|
||||||
|
; from block comments (the =begin/=end syntax). Until it does, the latter will
|
||||||
|
; be scoped as `comment.line` and we just have to live with it — or invent a
|
||||||
|
; way to hack around it in the language mode file once we can inspect the text
|
||||||
|
; itself.
|
||||||
|
;
|
||||||
|
; Likewise, we can't grab the leading `#` and scope it as punctuation the way
|
||||||
|
; the TM grammar does.
|
||||||
|
(comment) @comment.line.number-sign.ruby
|
||||||
|
|
||||||
|
; To distinguish them from the bitwise "|" operator.
|
||||||
|
(block_parameters
|
||||||
|
"|" @punctuation.separator.variable.ruby
|
||||||
|
)
|
||||||
|
|
||||||
|
(binary
|
||||||
|
"|" @keyword.operator.other.ruby
|
||||||
|
)
|
||||||
|
|
||||||
|
; Operators
|
||||||
|
|
||||||
|
"(" @punctuation.brace.round.begin.ruby
|
||||||
|
")" @punctuation.brace.round.end.ruby
|
||||||
|
"[" @punctuation.brace.square.begin.ruby
|
||||||
|
"]" @punctuation.brace.square.end.ruby
|
||||||
|
"{" @punctuation.brace.curly.begin.ruby
|
||||||
|
"}" @punctuation.brace.curly.end.ruby
|
||||||
|
|
||||||
|
(conditional
|
||||||
|
["?" ":"] @keyword.operator.conditional.ruby
|
||||||
|
(#set! final "true")
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
[
|
||||||
|
"="
|
||||||
|
"||="
|
||||||
|
"+="
|
||||||
|
"-="
|
||||||
|
"<<"
|
||||||
|
] @keyword.operator.assigment.ruby
|
||||||
|
|
||||||
|
[
|
||||||
|
"||"
|
||||||
|
"&&"
|
||||||
|
] @keyword.operator.logical.ruby
|
||||||
|
|
||||||
|
[
|
||||||
|
"&"
|
||||||
|
] @keyword.operator.other.ruby
|
||||||
|
|
||||||
|
[
|
||||||
|
"=="
|
||||||
|
">="
|
||||||
|
"<="
|
||||||
|
">"
|
||||||
|
"<"
|
||||||
|
] @keyword.operator.comparison.ruby
|
||||||
|
|
||||||
|
[
|
||||||
|
"+"
|
||||||
|
"-"
|
||||||
|
"*"
|
||||||
|
"/"
|
||||||
|
"**"
|
||||||
|
] @keyword.operator.arithmetic.ruby
|
||||||
|
|
||||||
|
[
|
||||||
|
"=>"
|
||||||
|
"->"
|
||||||
|
] @keyword.operator.ruby
|
||||||
|
|
||||||
|
[
|
||||||
|
","
|
||||||
|
";"
|
||||||
|
"."
|
||||||
|
":"
|
||||||
|
] @punctuation.separator.ruby
|
||||||
|
|
||||||
|
; Keywords
|
||||||
[
|
[
|
||||||
"alias"
|
"alias"
|
||||||
"and"
|
"and"
|
||||||
@ -28,119 +365,10 @@
|
|||||||
"when"
|
"when"
|
||||||
"while"
|
"while"
|
||||||
"yield"
|
"yield"
|
||||||
] @keyword
|
] @keyword.control.ruby
|
||||||
|
|
||||||
((identifier) @keyword
|
; Any identifiers we haven't caught yet can be given a generic scope.
|
||||||
(#match? @keyword "^(private|protected|public)$"))
|
((identifier) @function.method.ruby
|
||||||
|
(#is-not? local)
|
||||||
; Function calls
|
(#set! shy "true")
|
||||||
|
)
|
||||||
((identifier) @function.method.builtin
|
|
||||||
(#eq? @function.method.builtin "require"))
|
|
||||||
|
|
||||||
"defined?" @function.method.builtin
|
|
||||||
|
|
||||||
(call
|
|
||||||
method: [(identifier) (constant)] @function.method)
|
|
||||||
|
|
||||||
; Function definitions
|
|
||||||
|
|
||||||
(alias (identifier) @function.method)
|
|
||||||
(setter (identifier) @function.method)
|
|
||||||
(method name: [(identifier) (constant)] @function.method)
|
|
||||||
(singleton_method name: [(identifier) (constant)] @function.method)
|
|
||||||
|
|
||||||
; Identifiers
|
|
||||||
|
|
||||||
[
|
|
||||||
(class_variable)
|
|
||||||
(instance_variable)
|
|
||||||
] @property
|
|
||||||
|
|
||||||
((identifier) @constant.builtin
|
|
||||||
(#match? @constant.builtin "^__(FILE|LINE|ENCODING)__$"))
|
|
||||||
|
|
||||||
((constant) @constant
|
|
||||||
(#match? @constant "^[A-Z\\d_]+$"))
|
|
||||||
|
|
||||||
(constant) @constructor
|
|
||||||
|
|
||||||
(self) @variable.builtin
|
|
||||||
(super) @variable.builtin
|
|
||||||
|
|
||||||
(block_parameter (identifier) @variable.parameter)
|
|
||||||
(block_parameters (identifier) @variable.parameter)
|
|
||||||
(destructured_parameter (identifier) @variable.parameter)
|
|
||||||
(hash_splat_parameter (identifier) @variable.parameter)
|
|
||||||
(lambda_parameters (identifier) @variable.parameter)
|
|
||||||
(method_parameters (identifier) @variable.parameter)
|
|
||||||
(splat_parameter (identifier) @variable.parameter)
|
|
||||||
|
|
||||||
(keyword_parameter name: (identifier) @variable.parameter)
|
|
||||||
(optional_parameter name: (identifier) @variable.parameter)
|
|
||||||
|
|
||||||
((identifier) @function.method
|
|
||||||
(#is-not? local))
|
|
||||||
(identifier) @variable
|
|
||||||
|
|
||||||
; Literals
|
|
||||||
|
|
||||||
[
|
|
||||||
(string)
|
|
||||||
(bare_string)
|
|
||||||
(subshell)
|
|
||||||
(heredoc_body)
|
|
||||||
(heredoc_beginning)
|
|
||||||
] @string
|
|
||||||
|
|
||||||
[
|
|
||||||
(simple_symbol)
|
|
||||||
(delimited_symbol)
|
|
||||||
(hash_key_symbol)
|
|
||||||
(bare_symbol)
|
|
||||||
] @string.special.symbol
|
|
||||||
|
|
||||||
(regex) @string.special.regex
|
|
||||||
(escape_sequence) @escape
|
|
||||||
|
|
||||||
[
|
|
||||||
(integer)
|
|
||||||
(float)
|
|
||||||
] @number
|
|
||||||
|
|
||||||
[
|
|
||||||
(nil)
|
|
||||||
(true)
|
|
||||||
(false)
|
|
||||||
]@constant.builtin
|
|
||||||
|
|
||||||
(interpolation
|
|
||||||
"#{" @punctuation.special
|
|
||||||
"}" @punctuation.special) @embedded
|
|
||||||
|
|
||||||
(comment) @comment
|
|
||||||
|
|
||||||
; Operators
|
|
||||||
|
|
||||||
[
|
|
||||||
"="
|
|
||||||
"=>"
|
|
||||||
"->"
|
|
||||||
] @operator
|
|
||||||
|
|
||||||
[
|
|
||||||
","
|
|
||||||
";"
|
|
||||||
"."
|
|
||||||
] @punctuation.delimiter
|
|
||||||
|
|
||||||
[
|
|
||||||
"("
|
|
||||||
")"
|
|
||||||
"["
|
|
||||||
"]"
|
|
||||||
"{"
|
|
||||||
"}"
|
|
||||||
"%w("
|
|
||||||
"%i("
|
|
||||||
] @punctuation.bracket
|
|
||||||
|
21
packages/language-ruby/grammars/ts/indents-mine.scm
Normal file
21
packages/language-ruby/grammars/ts/indents-mine.scm
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
|
||||||
|
[
|
||||||
|
"do"
|
||||||
|
"module"
|
||||||
|
"class"
|
||||||
|
] @indent
|
||||||
|
|
||||||
|
; If the user has typed `if foo` but hasn't typed `end` yet, the only way to
|
||||||
|
; recognize that we should indent is via these anonymous nodes… TO
|
||||||
|
|
||||||
|
"if" @indent
|
||||||
|
"unless" @indent
|
||||||
|
|
||||||
|
; …but that also improperly catches postfix conditionals like `exit if foo`. So
|
||||||
|
; we dedent those to balance it out.
|
||||||
|
(if_modifier) @dedent
|
||||||
|
(unless_modifier) @dedent
|
||||||
|
|
||||||
|
[
|
||||||
|
"end"
|
||||||
|
] @dedent
|
@ -1,24 +1,29 @@
|
|||||||
[
|
[
|
||||||
(class)
|
"class"
|
||||||
(singleton_class)
|
(singleton_class)
|
||||||
(method)
|
"def"
|
||||||
(singleton_method)
|
(singleton_method)
|
||||||
(module)
|
"module"
|
||||||
(if)
|
"if"
|
||||||
(block)
|
"else"
|
||||||
(do_block)
|
"unless"
|
||||||
(argument_list)
|
; (block)
|
||||||
|
; (argument_list)
|
||||||
(case)
|
(case)
|
||||||
(while)
|
(while)
|
||||||
(until)
|
(until)
|
||||||
(for)
|
(for)
|
||||||
(begin)
|
"begin"
|
||||||
(unless)
|
"do"
|
||||||
|
"rescue"
|
||||||
|
; (unless)
|
||||||
"("
|
"("
|
||||||
"{"
|
"{"
|
||||||
"["
|
"["
|
||||||
] @indent
|
] @indent
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
[
|
[
|
||||||
"end"
|
"end"
|
||||||
")"
|
")"
|
||||||
@ -34,7 +39,7 @@
|
|||||||
(when)
|
(when)
|
||||||
(elsif)
|
(elsif)
|
||||||
(else)
|
(else)
|
||||||
(rescue)
|
"rescue"
|
||||||
(ensure)
|
(ensure)
|
||||||
] @branch
|
] @branch
|
||||||
|
|
||||||
|
@ -1,11 +1,132 @@
|
|||||||
const Parser = require('web-tree-sitter');
|
const Parser = require('web-tree-sitter');
|
||||||
const ScopeDescriptor = require('./scope-descriptor')
|
const ScopeDescriptor = require('./scope-descriptor')
|
||||||
const fs = require('fs');
|
// const fs = require('fs');
|
||||||
const { Point, Range } = require('text-buffer');
|
const { Point, Range } = require('text-buffer');
|
||||||
const { Emitter } = require('event-kit');
|
const { Emitter } = require('event-kit');
|
||||||
|
|
||||||
const initPromise = Parser.init()
|
const initPromise = Parser.init()
|
||||||
createTree = require("./rb-tree")
|
const createTree = require("./rb-tree")
|
||||||
|
|
||||||
|
class PositionIndex {
|
||||||
|
constructor () {
|
||||||
|
this.map = new Map
|
||||||
|
// TODO: It probably doesn't actually matter what order these are visited
|
||||||
|
// in.
|
||||||
|
this.order = []
|
||||||
|
this.rangeData = new Map
|
||||||
|
}
|
||||||
|
|
||||||
|
_normalizePoint (point) {
|
||||||
|
return `${point.row},${point.column}`
|
||||||
|
}
|
||||||
|
|
||||||
|
_normalizeRange (syntax) {
|
||||||
|
let { startPosition, endPosition } = syntax.node;
|
||||||
|
return `${this._normalizePoint(startPosition)}/${this._normalizePoint(endPosition)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
_keyToObject (key) {
|
||||||
|
let [row, column] = key.split(',');
|
||||||
|
return { row: Number(row), column: Number(column) }
|
||||||
|
}
|
||||||
|
|
||||||
|
setDataForRange (syntax, props) {
|
||||||
|
let key = this._normalizeRange(syntax);
|
||||||
|
return this.rangeData.set(key, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
getDataForRange (syntax) {
|
||||||
|
let key = this._normalizeRange(syntax);
|
||||||
|
return this.rangeData.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
store (syntax, id) {
|
||||||
|
let {
|
||||||
|
node,
|
||||||
|
setProperties: props = {}
|
||||||
|
} = syntax;
|
||||||
|
|
||||||
|
let {
|
||||||
|
startPosition: start,
|
||||||
|
endPosition: end
|
||||||
|
} = node;
|
||||||
|
|
||||||
|
let data = this.getDataForRange(syntax);
|
||||||
|
if (data && data.final) {
|
||||||
|
// A previous rule covering this exact range marked itself as "final." We
|
||||||
|
// should not add an additional scope.
|
||||||
|
return;
|
||||||
|
} else if (data && props.shy) {
|
||||||
|
// This node will only apply if we haven't yet marked this range with
|
||||||
|
// anything.
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// TODO: We may want to handle the case where more than one token will
|
||||||
|
// want to set data for a given range. Do we merge objects? Store each
|
||||||
|
// dataset separately?
|
||||||
|
this.setDataForRange(syntax, props);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should open this scope at `start`.
|
||||||
|
this.set(node, start, id, 'open');
|
||||||
|
|
||||||
|
// We should close this scope at `end`.
|
||||||
|
this.set(node, end, id, 'close');
|
||||||
|
}
|
||||||
|
|
||||||
|
set (node, point, id, which) {
|
||||||
|
let key = this._normalizePoint(point)
|
||||||
|
if (!this.order.includes(key)) {
|
||||||
|
this.order.push(key);
|
||||||
|
}
|
||||||
|
if (!this.map.has(key)) {
|
||||||
|
this.map.set(key, {
|
||||||
|
open: [],
|
||||||
|
close: [],
|
||||||
|
openNodes: [],
|
||||||
|
closeNodes: []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
let bundle = this.map.get(key);
|
||||||
|
let idBundle = bundle[which];
|
||||||
|
let nodeBundle = bundle[`${which}Nodes`];
|
||||||
|
|
||||||
|
if (which === 'open') {
|
||||||
|
// TODO: For now, assume that if two tokens both open at (X, Y), the one
|
||||||
|
// that spans a greater distance in the buffer will be encountered first.
|
||||||
|
// If that's not true, this logic may need to be more complex.
|
||||||
|
|
||||||
|
// If an earlier token has already opened at this point, we want to open
|
||||||
|
// after it.
|
||||||
|
idBundle.push(id)
|
||||||
|
nodeBundle.push(node)
|
||||||
|
} else {
|
||||||
|
// If an earlier token has already closed at this point, we want to close
|
||||||
|
// before it.
|
||||||
|
idBundle.unshift(id)
|
||||||
|
nodeBundle.unshift(node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get (point) {
|
||||||
|
let key = this._normalizePoint(point)
|
||||||
|
return this.map.get(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
clear () {
|
||||||
|
this.map.clear()
|
||||||
|
this.rangeData.clear()
|
||||||
|
this.order = []
|
||||||
|
}
|
||||||
|
|
||||||
|
*[Symbol.iterator] () {
|
||||||
|
for (let key of this.order) {
|
||||||
|
let point = this._keyToObject(key);
|
||||||
|
yield [point, this.map.get(key)]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const VAR_ID = 257
|
const VAR_ID = 257
|
||||||
class WASMTreeSitterLanguageMode {
|
class WASMTreeSitterLanguageMode {
|
||||||
@ -29,7 +150,7 @@ class WASMTreeSitterLanguageMode {
|
|||||||
this.lang = lang
|
this.lang = lang
|
||||||
this.syntaxQuery = lang.query(grammar.syntaxQuery)
|
this.syntaxQuery = lang.query(grammar.syntaxQuery)
|
||||||
if(grammar.localsQuery) {
|
if(grammar.localsQuery) {
|
||||||
this.localsQuery = lang.query(grammar.localsQuery)
|
// this.localsQuery = lang.query(grammar.localsQuery)
|
||||||
}
|
}
|
||||||
this.grammar = grammar
|
this.grammar = grammar
|
||||||
if(grammar.foldsQuery) {
|
if(grammar.foldsQuery) {
|
||||||
@ -41,7 +162,7 @@ class WASMTreeSitterLanguageMode {
|
|||||||
|
|
||||||
// Force first highlight
|
// Force first highlight
|
||||||
this.boundaries = createTree(comparePoints)
|
this.boundaries = createTree(comparePoints)
|
||||||
const startRange = new Range([0, 0], [0, 0])
|
// const startRange = new Range([0, 0], [0, 0])
|
||||||
const range = buffer.getRange()
|
const range = buffer.getRange()
|
||||||
this.tree = this.parser.parse(buffer.getText())
|
this.tree = this.parser.parse(buffer.getText())
|
||||||
this.emitter.emit('did-change-highlighting', range)
|
this.emitter.emit('did-change-highlighting', range)
|
||||||
@ -53,6 +174,22 @@ class WASMTreeSitterLanguageMode {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A hack to force an existing buffer to react to an update in the SCM file.
|
||||||
|
_reloadSyntaxQuery () {
|
||||||
|
// let _oldSyntaxQuery = this.syntaxQuery;
|
||||||
|
this.grammar._reloadQueryFiles()
|
||||||
|
let lang = this.parser.getLanguage()
|
||||||
|
this.syntaxQuery = lang.query(this.grammar.syntaxQuery)
|
||||||
|
// let range = this.buffer.getRange()
|
||||||
|
// this._updateSyntax(range.start, range.end)
|
||||||
|
// Force first highlight
|
||||||
|
this.boundaries = createTree(comparePoints)
|
||||||
|
// const startRange = new Range([0, 0], [0, 0])
|
||||||
|
const range = this.buffer.getRange()
|
||||||
|
this.tree = this.parser.parse(this.buffer.getText())
|
||||||
|
this.emitter.emit('did-change-highlighting', range)
|
||||||
|
}
|
||||||
|
|
||||||
getGrammar() {
|
getGrammar() {
|
||||||
return this.grammar
|
return this.grammar
|
||||||
}
|
}
|
||||||
@ -101,59 +238,160 @@ class WASMTreeSitterLanguageMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateSyntax(from, to) {
|
_findInvalidationRange(from, to) {
|
||||||
const syntax = this.syntaxQuery.captures(this.tree.rootNode, from, to)
|
let iterate = (from, to) => {
|
||||||
let oldDataIterator = this.boundaries.ge(from)
|
let iterator = this.boundaries.ge(from)
|
||||||
let oldScopes = []
|
|
||||||
|
|
||||||
while( oldDataIterator.hasNext && comparePoints(oldDataIterator.key, to) <= 0 ) {
|
let newFrom = from;
|
||||||
this.boundaries = this.boundaries.remove(oldDataIterator.key)
|
let newTo = to;
|
||||||
oldScopes = oldDataIterator.value.closeScopeIds
|
while (iterator.hasNext && comparePoints(iterator.key, to) <= 0) {
|
||||||
oldDataIterator.next()
|
let { key, value } = iterator
|
||||||
|
let { closeNodes, openNodes } = value
|
||||||
|
|
||||||
|
for (let o of openNodes) {
|
||||||
|
if (comparePoints(o.endPosition, newTo) > 0) {
|
||||||
|
newTo = o.endPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let c of closeNodes) {
|
||||||
|
if (comparePoints(c.startPosition, newFrom) < 0) {
|
||||||
|
newFrom = c.startPosition;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator.next()
|
||||||
|
}
|
||||||
|
|
||||||
|
let didChange = comparePoints(from, newFrom) !== 0 || comparePoints(to, newTo) !== 0;
|
||||||
|
|
||||||
|
return [newFrom, newTo, didChange];
|
||||||
|
};
|
||||||
|
|
||||||
|
let currentFrom = from;
|
||||||
|
let currentTo = to;
|
||||||
|
let stable = false;
|
||||||
|
while (!stable) {
|
||||||
|
let [newFrom, newTo, didChange] = iterate(currentFrom, currentTo);
|
||||||
|
if (!didChange) {
|
||||||
|
stable = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
currentFrom = newFrom;
|
||||||
|
currentTo = newTo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return [currentFrom, currentTo];
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateSyntax(from, to) {
|
||||||
|
console.log('_updateSyntax', from, to);
|
||||||
|
let [realFrom, realTo] = this._findInvalidationRange(from, to)
|
||||||
|
console.log('(widening to:)', realFrom, realTo);
|
||||||
|
|
||||||
|
from = realFrom;
|
||||||
|
to = realTo;
|
||||||
|
|
||||||
|
const syntax = this.syntaxQuery.captures(this.tree.rootNode, from, to)
|
||||||
|
console.log('captures:', syntax);
|
||||||
|
let oldDataIterator = this.boundaries.ge(from)
|
||||||
|
let oldScopes = []
|
||||||
|
// Remove all boundaries data for the given range.
|
||||||
|
while (oldDataIterator.hasNext && comparePoints(oldDataIterator.key, to) <= 0 ) {
|
||||||
|
let { key, value } = oldDataIterator
|
||||||
|
this.boundaries = this.boundaries.remove(key)
|
||||||
|
oldScopes = value.closeScopeIds
|
||||||
|
oldDataIterator.next()
|
||||||
|
// TODO: Doesn't this mean that we'll miss the last item in the iterator
|
||||||
|
// under certain circumstances?
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Still don't quite understand this; need to revisit.
|
||||||
oldScopes = oldScopes || []
|
oldScopes = oldScopes || []
|
||||||
syntax.forEach(({node, name}) => {
|
|
||||||
let id = this.scopeNames.get(name)
|
|
||||||
if(!id) {
|
|
||||||
this.lastId += 2
|
|
||||||
id = this.lastId
|
|
||||||
const newId = this.lastId;
|
|
||||||
this.scopeNames.set(name, newId)
|
|
||||||
this.scopeIds.set(newId, name)
|
|
||||||
}
|
|
||||||
// })
|
|
||||||
|
|
||||||
let old = this.boundaries.get(node.startPosition)
|
if (!this.positionIndex) {
|
||||||
if(old) {
|
this.positionIndex = new PositionIndex();
|
||||||
old.openNode = node
|
}
|
||||||
if(old.openScopeIds.length === 0) {
|
this.positionIndex.clear()
|
||||||
old.openScopeIds = [id]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.boundaries = this.boundaries.insert(node.startPosition, {
|
|
||||||
closeScopeIds: [...oldScopes],
|
|
||||||
openScopeIds: [id],
|
|
||||||
openNode: node,
|
|
||||||
position: node.startPosition
|
|
||||||
})
|
|
||||||
oldScopes = [id]
|
|
||||||
}
|
|
||||||
|
|
||||||
old = this.boundaries.get(node.endPosition)
|
console.log('oldScopes:', oldScopes.map(s => this.scopeForId(s)));
|
||||||
if(old) {
|
|
||||||
old.closeNode = node
|
|
||||||
if(old.closeScopeIds.length === 0) old.closeScopeIds = [id]
|
|
||||||
} else {
|
|
||||||
this.boundaries = this.boundaries.insert(node.endPosition, {
|
|
||||||
closeScopeIds: [id],
|
|
||||||
openScopeIds: [],
|
|
||||||
closeNode: node,
|
|
||||||
position: node.endPosition
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
|
let inspect = (id) => {
|
||||||
|
if (Array.isArray(id)) {
|
||||||
|
return id.map(i => inspect(i))
|
||||||
|
}
|
||||||
|
return this.scopeForId(id)
|
||||||
|
};
|
||||||
|
|
||||||
|
syntax.forEach((s) => {
|
||||||
|
let { name } = s
|
||||||
|
let id = this.findOrCreateScopeId(name)
|
||||||
|
|
||||||
|
// PositionIndex takes all our syntax tokens and consolidates them into a
|
||||||
|
// fixed set of boundaries to visit in order. If a token has data, it
|
||||||
|
// sets that data so that a later token for the same range can read it.
|
||||||
|
this.positionIndex.store(s, id)
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let [point, data] of this.positionIndex) {
|
||||||
|
let bundle = {
|
||||||
|
closeScopeIds: [...data.close],
|
||||||
|
openScopeIds: [...data.open],
|
||||||
|
closeNodes: [...data.closeNodes],
|
||||||
|
openNodes: [...data.openNodes],
|
||||||
|
position: point
|
||||||
|
}
|
||||||
|
console.log('inserting', bundle, 'at point:', point, 'close:', inspect(bundle.closeScopeIds), 'open:', inspect(bundle.openScopeIds));
|
||||||
|
this.boundaries = this.boundaries.insert(point, bundle)
|
||||||
|
}
|
||||||
|
|
||||||
|
// syntax.forEach(({ node, name }) => {
|
||||||
|
// // let id = this.scopeNames.get(name)
|
||||||
|
// // console.log(' handling node:', name, node);
|
||||||
|
// // if (!id) {
|
||||||
|
// // this.lastId += 2
|
||||||
|
// // id = this.lastId
|
||||||
|
// // const newId = this.lastId;
|
||||||
|
// // this.scopeNames.set(name, newId)
|
||||||
|
// // this.scopeIds.set(newId, name)
|
||||||
|
// // }
|
||||||
|
// let id = this.findOrCreateScopeId(name)
|
||||||
|
// let old = this.boundaries.get(node.startPosition)
|
||||||
|
// if (old) {
|
||||||
|
// // console.log(' found node:', this.scopeForId(id));
|
||||||
|
// old.openNode = node
|
||||||
|
// if (old.openScopeIds.length === 0) {
|
||||||
|
// old.openScopeIds = [id]
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// let bundle = {
|
||||||
|
// closeScopeIds: [...oldScopes],
|
||||||
|
// openScopeIds: [id],
|
||||||
|
// openNode: node,
|
||||||
|
// position: node.startPosition
|
||||||
|
// }
|
||||||
|
// console.log('inserting close', s(bundle.closeScopeIds), 'open', s(bundle.openScopeIds), 'at', node.startPosition);
|
||||||
|
// this.boundaries = this.boundaries.insert(node.startPosition, bundle)
|
||||||
|
// oldScopes = [id]
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// old = this.boundaries.get(node.endPosition)
|
||||||
|
// if (old) {
|
||||||
|
// old.closeNode = node
|
||||||
|
// if (old.closeScopeIds.length === 0) {
|
||||||
|
// old.closeScopeIds = [id]
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// this.boundaries = this.boundaries.insert(node.endPosition, {
|
||||||
|
// closeScopeIds: [id],
|
||||||
|
// openScopeIds: [],
|
||||||
|
// closeNode: node,
|
||||||
|
// position: node.endPosition
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
|
||||||
|
console.log('closing', oldScopes.map(s => this.scopeForId(s)), 'at the end of the document');
|
||||||
this.boundaries = this.boundaries.insert(Point.INFINITY, {
|
this.boundaries = this.boundaries.insert(Point.INFINITY, {
|
||||||
closeScopeIds: [...oldScopes],
|
closeScopeIds: [...oldScopes],
|
||||||
openScopeIds: [],
|
openScopeIds: [],
|
||||||
@ -161,6 +399,66 @@ class WASMTreeSitterLanguageMode {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// _updateSyntax(from, to) {
|
||||||
|
// const syntax = this.syntaxQuery.captures(this.tree.rootNode, from, to)
|
||||||
|
// let oldDataIterator = this.boundaries.ge(from)
|
||||||
|
// let oldScopes = []
|
||||||
|
//
|
||||||
|
// while( oldDataIterator.hasNext && comparePoints(oldDataIterator.key, to) <= 0 ) {
|
||||||
|
// this.boundaries = this.boundaries.remove(oldDataIterator.key)
|
||||||
|
// oldScopes = oldDataIterator.value.closeScopeIds
|
||||||
|
// oldDataIterator.next()
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// oldScopes = oldScopes || []
|
||||||
|
// syntax.forEach(({node, name}) => {
|
||||||
|
// let id = this.scopeNames.get(name)
|
||||||
|
// if(!id) {
|
||||||
|
// this.lastId += 2
|
||||||
|
// id = this.lastId
|
||||||
|
// const newId = this.lastId;
|
||||||
|
// this.scopeNames.set(name, newId)
|
||||||
|
// this.scopeIds.set(newId, name)
|
||||||
|
// }
|
||||||
|
// // })
|
||||||
|
//
|
||||||
|
// let old = this.boundaries.get(node.startPosition)
|
||||||
|
// if(old) {
|
||||||
|
// old.openNode = node
|
||||||
|
// if(old.openScopeIds.length === 0) {
|
||||||
|
// old.openScopeIds = [id]
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// this.boundaries = this.boundaries.insert(node.startPosition, {
|
||||||
|
// closeScopeIds: [...oldScopes],
|
||||||
|
// openScopeIds: [id],
|
||||||
|
// openNode: node,
|
||||||
|
// position: node.startPosition
|
||||||
|
// })
|
||||||
|
// oldScopes = [id]
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// old = this.boundaries.get(node.endPosition)
|
||||||
|
// if(old) {
|
||||||
|
// old.closeNode = node
|
||||||
|
// if(old.closeScopeIds.length === 0) old.closeScopeIds = [id]
|
||||||
|
// } else {
|
||||||
|
// this.boundaries = this.boundaries.insert(node.endPosition, {
|
||||||
|
// closeScopeIds: [id],
|
||||||
|
// openScopeIds: [],
|
||||||
|
// closeNode: node,
|
||||||
|
// position: node.endPosition
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
//
|
||||||
|
// this.boundaries = this.boundaries.insert(Point.INFINITY, {
|
||||||
|
// closeScopeIds: [...oldScopes],
|
||||||
|
// openScopeIds: [],
|
||||||
|
// position: Point.INFINITY
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
|
||||||
_prepareInvalidations() {
|
_prepareInvalidations() {
|
||||||
let nodes = this.oldNodeTexts
|
let nodes = this.oldNodeTexts
|
||||||
let parentScopes = createTree(comparePoints)
|
let parentScopes = createTree(comparePoints)
|
||||||
@ -299,33 +597,106 @@ class WASMTreeSitterLanguageMode {
|
|||||||
|
|
||||||
classNameForScopeId(scopeId) {
|
classNameForScopeId(scopeId) {
|
||||||
const scope = this.scopeIds.get(scopeId)
|
const scope = this.scopeIds.get(scopeId)
|
||||||
if(scope) return `syntax--${scope.replace(/\./g, ' syntax--')}`
|
if (scope) {
|
||||||
|
return `syntax--${scope.replace(/\./g, ' syntax--')}`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
scopeForId(scopeId) {
|
scopeForId (scopeId) {
|
||||||
return this.scopeIds[scopeId]
|
return this.scopeIds.get(scopeId)
|
||||||
}
|
}
|
||||||
|
|
||||||
scopeDescriptorForPosition(position) {
|
findOrCreateScopeId (name) {
|
||||||
if(!this.tree) return new ScopeDescriptor({scopes: ['text']})
|
let id = this.scopeNames.get(name)
|
||||||
const current = Point.fromObject(position)
|
if (!id) {
|
||||||
let begin = Point.fromObject(position)
|
this.lastId += 2
|
||||||
|
id = this.lastId
|
||||||
|
const newId = this.lastId;
|
||||||
|
this.scopeNames.set(name, newId)
|
||||||
|
this.scopeIds.set(newId, name)
|
||||||
|
}
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
syntaxTreeScopeDescriptorForPosition(point) {
|
||||||
|
point = this.buffer.clipPosition(Point.fromObject(point));
|
||||||
|
|
||||||
|
// If the position is the end of a line, get node of left character instead of newline
|
||||||
|
// This is to match TextMate behaviour, see https://github.com/atom/atom/issues/18463
|
||||||
|
if (
|
||||||
|
point.column > 0 &&
|
||||||
|
point.column === this.buffer.lineLengthForRow(point.row)
|
||||||
|
) {
|
||||||
|
point = point.copy();
|
||||||
|
point.column--;
|
||||||
|
}
|
||||||
|
|
||||||
|
let scopes = [];
|
||||||
|
|
||||||
|
let root = this.tree.rootNode;
|
||||||
|
let rangeIncludesPoint = (start, end, point) => {
|
||||||
|
return comparePoints(start, point) <= 0 && comparePoints(end, point) >= 0
|
||||||
|
};
|
||||||
|
|
||||||
|
let iterate = (node, isAnonymous = false) => {
|
||||||
|
let { startPosition: start, endPosition: end } = node;
|
||||||
|
if (rangeIncludesPoint(start, end, point)) {
|
||||||
|
scopes.push(isAnonymous ? `"${node.type}"` : node.type);
|
||||||
|
let namedChildrenIds = node.namedChildren.map(c => c.typeId);
|
||||||
|
for (let child of node.children) {
|
||||||
|
let isAnonymous = !namedChildrenIds.includes(child.typeId);
|
||||||
|
iterate(child, isAnonymous);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
iterate(root);
|
||||||
|
|
||||||
|
scopes.unshift(this.grammar.scopeName);
|
||||||
|
return new ScopeDescriptor({ scopes });
|
||||||
|
}
|
||||||
|
|
||||||
|
scopeDescriptorForPosition (point) {
|
||||||
|
// If the position is the end of a line, get scope of left character instead of newline
|
||||||
|
// This is to match TextMate behaviour, see https://github.com/atom/atom/issues/18463
|
||||||
|
if (
|
||||||
|
point.column > 0 &&
|
||||||
|
point.column === this.buffer.lineLengthForRow(point.row)
|
||||||
|
) {
|
||||||
|
point = point.copy();
|
||||||
|
point.column--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.tree) {
|
||||||
|
return new ScopeDescriptor({scopes: ['text']})
|
||||||
|
}
|
||||||
|
const current = Point.fromObject(point, true)
|
||||||
|
let begin = Point.fromObject(point, true)
|
||||||
begin.column = 0
|
begin.column = 0
|
||||||
const end = Point.fromObject([begin.row+1, 0])
|
|
||||||
|
const end = Point.fromObject([begin.row + 1, 0])
|
||||||
this._updateBoundaries(begin, end)
|
this._updateBoundaries(begin, end)
|
||||||
const it = this.boundaries.ge(begin)
|
|
||||||
if(!it.value) return new ScopeDescriptor({scopes: ['text']})
|
// Start at the beginning.
|
||||||
|
const it = this.boundaries.ge(new Point(0, 0))
|
||||||
|
if (!it.value) {
|
||||||
|
return new ScopeDescriptor({scopes: ['text']})
|
||||||
|
}
|
||||||
|
|
||||||
let scopeIds = []
|
let scopeIds = []
|
||||||
while(comparePoints(it.key, current) <= 0) {
|
while (comparePoints(it.key, current) <= 0) {
|
||||||
const closing = new Set(it.value.closeScopeIds)
|
const closing = new Set(it.value.closeScopeIds)
|
||||||
scopeIds = scopeIds.filter(s => !closing.has(s))
|
scopeIds = scopeIds.filter(s => !closing.has(s))
|
||||||
scopeIds.push(...it.value.openScopeIds)
|
scopeIds.push(...it.value.openScopeIds)
|
||||||
if(!it.hasNext) break
|
if (!it.hasNext) { break }
|
||||||
it.next()
|
it.next()
|
||||||
}
|
}
|
||||||
|
|
||||||
const scopes = scopeIds.map(id => this.classNameForScopeId(id).replace(/^syntax--/, '').replace(/\s?syntax--/g, '.'))
|
const scopes = scopeIds.map(id => this.scopeForId(id))
|
||||||
|
|
||||||
|
if (scopes.length === 0 || scopes[0] !== this.grammar.scopeName) {
|
||||||
|
scopes.unshift(this.grammar.scopeName);
|
||||||
|
}
|
||||||
return new ScopeDescriptor({scopes})
|
return new ScopeDescriptor({scopes})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -350,34 +721,83 @@ class WASMTreeSitterLanguageMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
suggestedIndentForBufferRow(row, tabLength, options) {
|
suggestedIndentForBufferRow(row, tabLength, options) {
|
||||||
if(row === 0) return 0;
|
console.log('suggestedLineForBufferRow', row, tabLength);
|
||||||
|
if (row === 0) { return 0; }
|
||||||
|
|
||||||
|
const lastLineIndent = this.indentLevelForLine(
|
||||||
|
this.buffer.lineForRow(row - 1), tabLength
|
||||||
|
)
|
||||||
|
let amount = lastLineIndent
|
||||||
|
|
||||||
|
console.log('going from', row - 1, 'to', row, this.tree.rootNode);
|
||||||
const indents = this.indentsQuery.captures(
|
const indents = this.indentsQuery.captures(
|
||||||
this.tree.rootNode,
|
this.tree.rootNode,
|
||||||
{row: row-1, column: 0},
|
{row: row - 1, column: 0},
|
||||||
{row: row, column: 0}
|
{row: row, column: 0}
|
||||||
)
|
)
|
||||||
const indent = indents.find(i => i.node.startPosition.row === row-1)
|
console.log('indents:', indents);
|
||||||
const lastLineIndent = this.indentLevelForLine(
|
|
||||||
this.buffer.getLines()[row-1], tabLength
|
|
||||||
)
|
|
||||||
|
|
||||||
if(indent?.name === 'indent') {
|
let delta = 0;
|
||||||
return lastLineIndent + 1
|
for (let { name, node } of indents) {
|
||||||
} else {
|
let text = node.text;
|
||||||
const suggestion = this.suggestedIndentForEditedBufferRow(row, tabLength)
|
if (!text || !text.length) { continue; }
|
||||||
return suggestion !== undefined ? suggestion : lastLineIndent
|
if (name === 'indent') { delta++ }
|
||||||
|
else if (name === 'indent_end') { delta-- }
|
||||||
|
console.log('delta is now:', delta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (delta > 1) { delta = 1; }
|
||||||
|
if (delta < 0) { delta = 0; }
|
||||||
|
|
||||||
|
return lastLineIndent + delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// suggestedIndentForEditedBufferRow(row, tabLength) {
|
||||||
|
// if (row === 0) { return 0; }
|
||||||
|
//
|
||||||
|
// const indents = this.indentsQuery.captures(
|
||||||
|
// this.tree.rootNode,
|
||||||
|
// {row: row, column: 0},
|
||||||
|
// {row: row + 1, column: 0}
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// console.log('edited indents:', indents);
|
||||||
|
//
|
||||||
|
// let currentLineIndent = this.indentLevelForLine(this.buffer.lineForRow(row), tabLength);
|
||||||
|
// let originalLineIndent = this.suggestedIndentForBufferRow(row, tabLength);
|
||||||
|
// // let startingLineIndent = Math.
|
||||||
|
// console.log('starting at', originalLineIndent);
|
||||||
|
//
|
||||||
|
// let delta = 0;
|
||||||
|
// for (let { name, node } of indents) {
|
||||||
|
// if (!node.text?.length) { continue; }
|
||||||
|
//
|
||||||
|
// if (name === 'branch') {
|
||||||
|
// delta--
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (delta === 0) {
|
||||||
|
// return currentLineIndent;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (delta < -1) { delta = -1; }
|
||||||
|
// return Math.max(0, originalLineIndent + delta);
|
||||||
|
// }
|
||||||
|
|
||||||
suggestedIndentForEditedBufferRow(row, tabLength) {
|
suggestedIndentForEditedBufferRow(row, tabLength) {
|
||||||
const indents = this.indentsQuery.captures(
|
const indents = this.indentsQuery.captures(
|
||||||
this.tree.rootNode,
|
this.tree.rootNode,
|
||||||
{row: row, column: 0},
|
{row: row, column: 0},
|
||||||
{row: row+1, column: 0}
|
{row: row+1, column: 0}
|
||||||
)
|
)
|
||||||
const indent = indents.find(i => i.node.startPosition.row === row)
|
console.log('indents:', indents);
|
||||||
if(indent?.name === "indent_end") {
|
const indent = indents.find(i => {
|
||||||
if(this.buffer.getLines()[row].trim() === indent.node.text) {
|
return i.node.startPosition.row === row && i.name === 'branch'
|
||||||
|
});
|
||||||
|
console.log('specific indent:', indent);
|
||||||
|
if(indent?.name === "branch") {
|
||||||
|
if(this.buffer.lineForRow(row).trim() === indent.node.text) {
|
||||||
const parent = indent.node.parent
|
const parent = indent.node.parent
|
||||||
if(parent) return this.indentLevelForLine(
|
if(parent) return this.indentLevelForLine(
|
||||||
this.buffer.getLines()[parent.startPosition.row],
|
this.buffer.getLines()[parent.startPosition.row],
|
||||||
@ -386,7 +806,6 @@ class WASMTreeSitterLanguageMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copied from original tree-sitter. I honestly didn't even read this.
|
// Copied from original tree-sitter. I honestly didn't even read this.
|
||||||
indentLevelForLine(line, tabLength) {
|
indentLevelForLine(line, tabLength) {
|
||||||
let indentLength = 0;
|
let indentLength = 0;
|
||||||
@ -424,9 +843,12 @@ class WASMTreeSitterLanguageMode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_getFoldsAtRow(row) {
|
_getFoldsAtRow(row) {
|
||||||
if(!this.tree) return []
|
if (!this.tree) { return [] }
|
||||||
const folds = this.foldsQuery.captures(this.tree.rootNode,
|
const folds = this.foldsQuery.captures(
|
||||||
{row: row, column: 0}, {row: row+1, column: 0})
|
this.tree.rootNode,
|
||||||
|
{ row: row, column: 0 },
|
||||||
|
{ row: row + 1, column: 0 }
|
||||||
|
)
|
||||||
return folds.filter(fold => fold.node.startPosition.row === row)
|
return folds.filter(fold => fold.node.startPosition.row === row)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -467,3 +889,10 @@ function comparePoints(a, b) {
|
|||||||
else
|
else
|
||||||
return rows
|
return rows
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isBetweenPoints (point, a, b) {
|
||||||
|
let comp = comparePoints(a, b);
|
||||||
|
let lesser = comp > 0 ? b : a;
|
||||||
|
let greater = comp > 0 ? a : b;
|
||||||
|
return comparePoints(point, lesser) >= 0 && comparePoints(point, greater) <= 0;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user