textbringer-tree-sitter 1.2.8 → 1.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/exe/textbringer-tree-sitter +19 -19
- data/lib/textbringer/tree_sitter/node_maps/cobol.rb +1 -1
- data/lib/textbringer/tree_sitter/node_maps/crystal.rb +1 -1
- data/lib/textbringer/tree_sitter/node_maps/elixir.rb +2 -2
- data/lib/textbringer/tree_sitter/node_maps/hcl.rb +3 -3
- data/lib/textbringer/tree_sitter/node_maps/javascript.rb +1 -0
- data/lib/textbringer/tree_sitter/node_maps/json.rb +1 -1
- data/lib/textbringer/tree_sitter/node_maps/pascal.rb +21 -2
- data/lib/textbringer/tree_sitter/node_maps/python.rb +2 -0
- data/lib/textbringer/tree_sitter/node_maps/ruby.rb +3 -3
- data/lib/textbringer/tree_sitter/node_maps/rust.rb +1 -0
- data/lib/textbringer/tree_sitter/node_maps/yaml.rb +1 -1
- data/lib/textbringer/tree_sitter/node_maps.rb +1 -1
- data/lib/textbringer/tree_sitter/version.rb +1 -1
- data/lib/textbringer/tree_sitter_adapter.rb +20 -20
- data/lib/textbringer/tree_sitter_config.rb +7 -7
- data/lib/textbringer_plugin.rb +20 -20
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 40361ec3201e02b7f5595ba884dcb56e6736d706727890af979f4dea89fcc8a0
|
|
4
|
+
data.tar.gz: 1b5f024d3c895ad3604a9621c8ce3e739bb3c7fc9be4b78a799de8a1791e2572
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d3bbf011137dcdea42a572b74374f943a2b36ff5274b6f4f2515172adbcd1f1b3bb6d1b2dd2a75cf3b0fe312d9e88c6f437514f294e9c083cb59c30e35c9ef69
|
|
7
|
+
data.tar.gz: 71f32a6509c896e26087e8e43a8fb4f2d072b539d4c12c39c7d295d194e302626683dcd4684458659c7dfb2b39358f3a2cc7e68d57ea080ded93b5876dd9559a
|
data/exe/textbringer-tree-sitter
CHANGED
|
@@ -18,13 +18,13 @@ require_relative "../lib/textbringer/tree_sitter/platform"
|
|
|
18
18
|
module TextbringerTreeSitterCLI
|
|
19
19
|
FAVEOD_VERSION = "v4.11"
|
|
20
20
|
|
|
21
|
-
# Faveod tarball
|
|
21
|
+
# Parsers included in the Faveod tarball
|
|
22
22
|
FAVEOD_PARSERS = %w[
|
|
23
23
|
bash c c-sharp cobol embedded-template groovy haml html
|
|
24
24
|
java javascript json pascal php python ruby rust
|
|
25
25
|
].freeze
|
|
26
26
|
|
|
27
|
-
#
|
|
27
|
+
# Requires building from source (not included in Faveod)
|
|
28
28
|
BUILD_PARSERS = {
|
|
29
29
|
hcl: {
|
|
30
30
|
repo: "mitchellh/tree-sitter-hcl",
|
|
@@ -80,7 +80,7 @@ module TextbringerTreeSitterCLI
|
|
|
80
80
|
markdown: {
|
|
81
81
|
repo: "tree-sitter-grammars/tree-sitter-markdown",
|
|
82
82
|
branch: "split_parser",
|
|
83
|
-
commit: "9a23c1a", # LANGUAGE_VERSION 14 (ruby_tree_sitter
|
|
83
|
+
commit: "9a23c1a", # LANGUAGE_VERSION 14 (compatible with ruby_tree_sitter)
|
|
84
84
|
subdir: "tree-sitter-markdown",
|
|
85
85
|
build_cmd: ->(src_dir, out_file) {
|
|
86
86
|
["cc", "-shared", "-fPIC", "-O2", "-I#{src_dir}/src",
|
|
@@ -108,15 +108,15 @@ module TextbringerTreeSitterCLI
|
|
|
108
108
|
branch: "main",
|
|
109
109
|
build_cmd: ->(src_dir, out_file) {
|
|
110
110
|
["cc", "-shared", "-fPIC", "-O2", "-I#{src_dir}/src",
|
|
111
|
-
"#{src_dir}/src/parser.c", "-o", out_file]
|
|
111
|
+
"#{src_dir}/src/parser.c", "#{src_dir}/src/scanner.c", "-o", out_file]
|
|
112
112
|
}
|
|
113
113
|
},
|
|
114
114
|
swift: {
|
|
115
115
|
repo: "alex-pinkus/tree-sitter-swift",
|
|
116
|
-
branch: "
|
|
116
|
+
branch: "0.7.1-with-generated-files",
|
|
117
117
|
build_cmd: ->(src_dir, out_file) {
|
|
118
118
|
["cc", "-shared", "-fPIC", "-O2", "-I#{src_dir}/src",
|
|
119
|
-
"#{src_dir}/src/parser.c", "-o", out_file]
|
|
119
|
+
"#{src_dir}/src/parser.c", "#{src_dir}/src/scanner.c", "-o", out_file]
|
|
120
120
|
}
|
|
121
121
|
},
|
|
122
122
|
zig: {
|
|
@@ -310,7 +310,7 @@ module TextbringerTreeSitterCLI
|
|
|
310
310
|
# Normalize language name to handle aliases
|
|
311
311
|
normalized = Textbringer::TreeSitter::LanguageAliases.normalize(language)
|
|
312
312
|
|
|
313
|
-
#
|
|
313
|
+
# Install a specific parser from the Faveod tarball
|
|
314
314
|
filename = "libtree-sitter-#{normalized}#{dylib_ext}"
|
|
315
315
|
dest_path = File.join(parser_dir, filename)
|
|
316
316
|
|
|
@@ -349,7 +349,7 @@ module TextbringerTreeSitterCLI
|
|
|
349
349
|
return false
|
|
350
350
|
end
|
|
351
351
|
|
|
352
|
-
# Faveod tarball
|
|
352
|
+
# Filenames in the Faveod tarball may use pre-normalized names (e.g., c-sharp)
|
|
353
353
|
faveod_filename = "libtree-sitter-#{language}#{dylib_ext}"
|
|
354
354
|
src = Dir.glob("#{extract_dir}/**/#{filename}").first
|
|
355
355
|
src ||= Dir.glob("#{extract_dir}/**/#{faveod_filename}").first if filename != faveod_filename
|
|
@@ -411,7 +411,7 @@ module TextbringerTreeSitterCLI
|
|
|
411
411
|
|
|
412
412
|
puts " Cloning..."
|
|
413
413
|
if config.commit
|
|
414
|
-
#
|
|
414
|
+
# Cannot use shallow clone when a specific commit is required
|
|
415
415
|
_, status = Open3.capture2e("git", "clone", "-b", config.branch,
|
|
416
416
|
"https://github.com/#{config.repo}.git", repo_dir)
|
|
417
417
|
unless status.success?
|
|
@@ -507,7 +507,7 @@ module TextbringerTreeSitterCLI
|
|
|
507
507
|
normalized = Textbringer::TreeSitter::LanguageAliases.normalize(language)
|
|
508
508
|
languages = all_languages
|
|
509
509
|
|
|
510
|
-
# normalized form
|
|
510
|
+
# Find a matching entry by comparing in normalized form
|
|
511
511
|
lang, config = languages.find { |k, _|
|
|
512
512
|
Textbringer::TreeSitter::LanguageAliases.normalize(k) == normalized
|
|
513
513
|
}
|
|
@@ -710,7 +710,7 @@ module TextbringerTreeSitterCLI
|
|
|
710
710
|
return false
|
|
711
711
|
end
|
|
712
712
|
|
|
713
|
-
#
|
|
713
|
+
# Collect node types and map them heuristically
|
|
714
714
|
mappings = {}
|
|
715
715
|
unmapped = []
|
|
716
716
|
|
|
@@ -730,7 +730,7 @@ module TextbringerTreeSitterCLI
|
|
|
730
730
|
end
|
|
731
731
|
end
|
|
732
732
|
|
|
733
|
-
#
|
|
733
|
+
# Generate template
|
|
734
734
|
const_name = lang.upcase
|
|
735
735
|
output = <<~RUBY
|
|
736
736
|
# frozen_string_literal: true
|
|
@@ -748,7 +748,7 @@ module TextbringerTreeSitterCLI
|
|
|
748
748
|
#{const_name}_FEATURES = {
|
|
749
749
|
RUBY
|
|
750
750
|
|
|
751
|
-
#
|
|
751
|
+
# Output mapped nodes
|
|
752
752
|
mappings.each do |face, nodes|
|
|
753
753
|
nodes_str = nodes.join(" ")
|
|
754
754
|
output << " #{face}: %i[#{nodes_str}],\n"
|
|
@@ -756,7 +756,7 @@ module TextbringerTreeSitterCLI
|
|
|
756
756
|
|
|
757
757
|
output << " }.freeze\n\n"
|
|
758
758
|
|
|
759
|
-
#
|
|
759
|
+
# Output unmapped nodes as comments
|
|
760
760
|
if unmapped.any?
|
|
761
761
|
output << " # Unmapped nodes (add to appropriate face if needed):\n"
|
|
762
762
|
unmapped.each do |name|
|
|
@@ -770,14 +770,14 @@ module TextbringerTreeSitterCLI
|
|
|
770
770
|
nodes.map { |node| [node, face] }
|
|
771
771
|
}.to_h.freeze
|
|
772
772
|
|
|
773
|
-
#
|
|
773
|
+
# Auto-register
|
|
774
774
|
register(:#{lang}, #{const_name})
|
|
775
775
|
end
|
|
776
776
|
end
|
|
777
777
|
end
|
|
778
778
|
RUBY
|
|
779
779
|
|
|
780
|
-
#
|
|
780
|
+
# Write to file
|
|
781
781
|
FileUtils.mkdir_p(node_map_dir)
|
|
782
782
|
dest_path = File.join(node_map_dir, "#{lang}.rb")
|
|
783
783
|
File.write(dest_path, output)
|
|
@@ -889,7 +889,7 @@ module TextbringerTreeSitterCLI
|
|
|
889
889
|
skip_map = args.include?("--no-map")
|
|
890
890
|
success = get_parser(lang)
|
|
891
891
|
if success && !skip_map
|
|
892
|
-
#
|
|
892
|
+
# Skip languages that already have a default node_map
|
|
893
893
|
default_node_map_languages = %w[
|
|
894
894
|
bash c cobol crystal csharp elixir groovy haml hcl html java javascript
|
|
895
895
|
json pascal php python ruby rust sql swift yaml
|
|
@@ -930,8 +930,8 @@ module TextbringerTreeSitterCLI
|
|
|
930
930
|
end
|
|
931
931
|
|
|
932
932
|
# Only run CLI if executed directly (not when loaded by tests)
|
|
933
|
-
# __FILE__ == $PROGRAM_NAME
|
|
934
|
-
# basename
|
|
933
|
+
# __FILE__ == $PROGRAM_NAME matches when executed directly with ruby
|
|
934
|
+
# The basename check matches when run via bundle exec (binstub -> Gem.activate_and_load_bin_path -> load)
|
|
935
935
|
if __FILE__ == $PROGRAM_NAME || File.basename($PROGRAM_NAME) == "textbringer-tree-sitter"
|
|
936
936
|
TextbringerTreeSitterCLI.run(ARGV)
|
|
937
937
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Textbringer
|
|
4
4
|
module TreeSitter
|
|
5
5
|
module NodeMaps
|
|
6
|
-
# Feature-based
|
|
6
|
+
# Feature-based node mapping for Elixir
|
|
7
7
|
ELIXIR_FEATURES = {
|
|
8
8
|
comment: %i[comment],
|
|
9
9
|
string: %i[
|
|
@@ -84,7 +84,7 @@ module Textbringer
|
|
|
84
84
|
]
|
|
85
85
|
}.freeze
|
|
86
86
|
|
|
87
|
-
# Feature
|
|
87
|
+
# Expand Feature -> Face mapping
|
|
88
88
|
ELIXIR = ELIXIR_FEATURES.flat_map { |face, nodes|
|
|
89
89
|
nodes.map { |node| [node, face] }
|
|
90
90
|
}.to_h.freeze
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module Textbringer
|
|
4
4
|
module TreeSitter
|
|
5
5
|
module NodeMaps
|
|
6
|
-
# HCL (HashiCorp Configuration Language)
|
|
7
|
-
#
|
|
6
|
+
# HCL (HashiCorp Configuration Language) feature-based node mapping
|
|
7
|
+
# Correctly handles for, in, function_call which Rouge's Terraform lexer fails to recognize
|
|
8
8
|
HCL_FEATURES = {
|
|
9
9
|
comment: %i[comment],
|
|
10
10
|
string: %i[
|
|
@@ -62,7 +62,7 @@ module Textbringer
|
|
|
62
62
|
]
|
|
63
63
|
}.freeze
|
|
64
64
|
|
|
65
|
-
# Feature
|
|
65
|
+
# Expand Feature -> Face mapping
|
|
66
66
|
HCL = HCL_FEATURES.flat_map { |face, nodes|
|
|
67
67
|
nodes.map { |node| [node, face] }
|
|
68
68
|
}.to_h.freeze
|
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
module Textbringer
|
|
4
4
|
module TreeSitter
|
|
5
5
|
module NodeMaps
|
|
6
|
-
# Pascal
|
|
6
|
+
# Pascal tree-sitter node types often use the kXxx naming convention
|
|
7
7
|
PASCAL_FEATURES = {
|
|
8
8
|
comment: %i[comment],
|
|
9
9
|
string: %i[
|
|
10
10
|
literalString
|
|
11
|
+
literalChar
|
|
11
12
|
declString
|
|
12
13
|
],
|
|
13
14
|
keyword: %i[
|
|
@@ -161,6 +162,7 @@ module Textbringer
|
|
|
161
162
|
kObjcprotocol
|
|
162
163
|
kOptional
|
|
163
164
|
kRequired
|
|
165
|
+
kString
|
|
164
166
|
],
|
|
165
167
|
number: %i[
|
|
166
168
|
literalNumber
|
|
@@ -202,8 +204,25 @@ module Textbringer
|
|
|
202
204
|
exprBinary
|
|
203
205
|
exprUnary
|
|
204
206
|
assignment
|
|
207
|
+
kAdd
|
|
208
|
+
kSub
|
|
209
|
+
kMul
|
|
210
|
+
kHat
|
|
211
|
+
kAssign
|
|
212
|
+
kAssignAdd
|
|
213
|
+
kAssignDiv
|
|
214
|
+
kAssignMul
|
|
215
|
+
kAssignSub
|
|
216
|
+
kEq
|
|
217
|
+
kNeq
|
|
218
|
+
kGt
|
|
219
|
+
kGte
|
|
220
|
+
kLt
|
|
221
|
+
kLte
|
|
222
|
+
],
|
|
223
|
+
punctuation: %i[
|
|
224
|
+
kDot
|
|
205
225
|
],
|
|
206
|
-
punctuation: %i[],
|
|
207
226
|
builtin: %i[
|
|
208
227
|
kTrue
|
|
209
228
|
kFalse
|
|
@@ -16,6 +16,7 @@ module Textbringer
|
|
|
16
16
|
interpolation
|
|
17
17
|
format_specifier
|
|
18
18
|
format_expression
|
|
19
|
+
type_conversion
|
|
19
20
|
],
|
|
20
21
|
keyword: %i[
|
|
21
22
|
if
|
|
@@ -93,6 +94,7 @@ module Textbringer
|
|
|
93
94
|
chevron
|
|
94
95
|
ellipsis
|
|
95
96
|
line_continuation
|
|
97
|
+
positional_separator
|
|
96
98
|
],
|
|
97
99
|
builtin: %i[
|
|
98
100
|
true
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
module Textbringer
|
|
4
4
|
module TreeSitter
|
|
5
5
|
module NodeMaps
|
|
6
|
-
# Feature-based
|
|
7
|
-
# Emacs
|
|
6
|
+
# Feature-based node mapping
|
|
7
|
+
# Classified by feature, similar to Emacs treesit-font-lock-rules
|
|
8
8
|
RUBY_FEATURES = {
|
|
9
9
|
comment: %i[comment],
|
|
10
10
|
string: %i[
|
|
@@ -88,7 +88,7 @@ module Textbringer
|
|
|
88
88
|
property: %i[]
|
|
89
89
|
}.freeze
|
|
90
90
|
|
|
91
|
-
# Feature
|
|
91
|
+
# Expand Feature -> Face mapping
|
|
92
92
|
RUBY = RUBY_FEATURES.flat_map { |face, nodes|
|
|
93
93
|
nodes.map { |node| [node, face] }
|
|
94
94
|
}.to_h.freeze
|
|
@@ -31,7 +31,7 @@ module Textbringer
|
|
|
31
31
|
# Normalize language name to handle aliases
|
|
32
32
|
normalized = LanguageAliases.to_sym(language)
|
|
33
33
|
|
|
34
|
-
#
|
|
34
|
+
# If a custom map is registered, merge it with defaults and return
|
|
35
35
|
if @custom_maps&.key?(normalized)
|
|
36
36
|
default_map = default_maps[normalized]
|
|
37
37
|
if default_map
|
|
@@ -5,19 +5,19 @@ require_relative "tree_sitter/node_maps"
|
|
|
5
5
|
|
|
6
6
|
module Textbringer
|
|
7
7
|
module TreeSitterAdapter
|
|
8
|
-
# Emacs
|
|
8
|
+
# Emacs-style 4-level highlighting
|
|
9
9
|
HIGHLIGHT_LEVELS = [
|
|
10
|
-
%i[comment string], # Level 1:
|
|
11
|
-
%i[keyword type constant], # Level 2:
|
|
12
|
-
%i[function_name variable number], # Level 3:
|
|
13
|
-
%i[operator punctuation builtin] # Level 4:
|
|
10
|
+
%i[comment string], # Level 1: minimal
|
|
11
|
+
%i[keyword type constant], # Level 2: basic
|
|
12
|
+
%i[function_name variable number], # Level 3: standard (default)
|
|
13
|
+
%i[operator punctuation builtin] # Level 4: everything
|
|
14
14
|
].freeze
|
|
15
15
|
|
|
16
16
|
module ClassMethods
|
|
17
17
|
def use_tree_sitter(language)
|
|
18
18
|
@tree_sitter_language = language
|
|
19
19
|
|
|
20
|
-
# prepend
|
|
20
|
+
# Use prepend to take priority over existing custom_highlight
|
|
21
21
|
prepend InstanceMethods
|
|
22
22
|
|
|
23
23
|
define_method(:tree_sitter_language) do
|
|
@@ -43,16 +43,16 @@ module Textbringer
|
|
|
43
43
|
return unless parser
|
|
44
44
|
|
|
45
45
|
buffer = window.buffer
|
|
46
|
-
# textbringer
|
|
46
|
+
# Same logic as textbringer core: use base_pos as the reference point
|
|
47
47
|
base_pos = buffer.point_min
|
|
48
48
|
buffer_text = buffer.to_s
|
|
49
49
|
|
|
50
|
-
#
|
|
50
|
+
# Incremental parsing: reuse previous Tree if content unchanged
|
|
51
51
|
old_tree = get_cached_tree(buffer, buffer_text)
|
|
52
52
|
tree = parser.parse_string(old_tree, buffer_text)
|
|
53
53
|
return unless tree
|
|
54
54
|
|
|
55
|
-
#
|
|
55
|
+
# Cache the new Tree
|
|
56
56
|
cache_tree(buffer, tree, buffer_text)
|
|
57
57
|
|
|
58
58
|
if TreeSitterAdapter.debug?
|
|
@@ -73,7 +73,7 @@ module Textbringer
|
|
|
73
73
|
|
|
74
74
|
attrs = Face[face]&.attributes
|
|
75
75
|
if attrs
|
|
76
|
-
# Tree-sitter
|
|
76
|
+
# Both Tree-sitter and Textbringer use byte offsets
|
|
77
77
|
highlight_on[base_pos + start_byte] = attrs
|
|
78
78
|
highlight_off[base_pos + end_byte] = attrs
|
|
79
79
|
|
|
@@ -104,12 +104,12 @@ module Textbringer
|
|
|
104
104
|
cached = @tree_cache[buffer_id]
|
|
105
105
|
|
|
106
106
|
if cached && cached[:language] == tree_sitter_language && cached[:content_hash] == buffer_text.hash
|
|
107
|
-
# LRU
|
|
107
|
+
# LRU refresh: delete and re-insert to move to the end
|
|
108
108
|
@tree_cache.delete(buffer_id)
|
|
109
109
|
@tree_cache[buffer_id] = cached
|
|
110
110
|
cached[:tree]
|
|
111
111
|
else
|
|
112
|
-
#
|
|
112
|
+
# Content changed or no cache -> full reparse
|
|
113
113
|
@tree_cache.delete(buffer_id)
|
|
114
114
|
nil
|
|
115
115
|
end
|
|
@@ -120,7 +120,7 @@ module Textbringer
|
|
|
120
120
|
|
|
121
121
|
buffer_id = buffer.object_id
|
|
122
122
|
|
|
123
|
-
#
|
|
123
|
+
# Delete existing entry and re-insert (LRU order update)
|
|
124
124
|
@tree_cache.delete(buffer_id)
|
|
125
125
|
@tree_cache[buffer_id] = {
|
|
126
126
|
language: tree_sitter_language,
|
|
@@ -133,7 +133,7 @@ module Textbringer
|
|
|
133
133
|
end
|
|
134
134
|
|
|
135
135
|
def can_highlight?
|
|
136
|
-
# textbringer
|
|
136
|
+
# Same check as textbringer core: use @@has_colors
|
|
137
137
|
return false unless Window.class_variable_get(:@@has_colors)
|
|
138
138
|
return false if CONFIG[:syntax_highlight] == false
|
|
139
139
|
|
|
@@ -165,14 +165,14 @@ module Textbringer
|
|
|
165
165
|
my_face = node_map&.[](node.type.to_sym)
|
|
166
166
|
|
|
167
167
|
if node.child_count == 0
|
|
168
|
-
#
|
|
168
|
+
# Leaf node: skip yield if already covered by the same face as parent
|
|
169
169
|
block.call(node, node.start_byte, node.end_byte) unless my_face && my_face == covered_face
|
|
170
170
|
else
|
|
171
|
-
#
|
|
171
|
+
# Non-leaf node: yield if in node_map and face differs from parent
|
|
172
172
|
if my_face && my_face != covered_face
|
|
173
173
|
block.call(node, node.start_byte, node.end_byte)
|
|
174
174
|
end
|
|
175
|
-
#
|
|
175
|
+
# Recurse into children (propagate this node's face)
|
|
176
176
|
child_covered = my_face || covered_face
|
|
177
177
|
node.child_count.times do |i|
|
|
178
178
|
child = node.child(i)
|
|
@@ -193,12 +193,12 @@ module Textbringer
|
|
|
193
193
|
end
|
|
194
194
|
|
|
195
195
|
def enabled_faces
|
|
196
|
-
#
|
|
196
|
+
# Custom feature settings take priority
|
|
197
197
|
if CONFIG[:tree_sitter_enabled_features]
|
|
198
198
|
return CONFIG[:tree_sitter_enabled_features]
|
|
199
199
|
end
|
|
200
200
|
|
|
201
|
-
#
|
|
201
|
+
# Level-based control
|
|
202
202
|
level = CONFIG[:tree_sitter_highlight_level] || 3
|
|
203
203
|
HIGHLIGHT_LEVELS.take(level).flatten
|
|
204
204
|
end
|
|
@@ -206,7 +206,7 @@ module Textbringer
|
|
|
206
206
|
end
|
|
207
207
|
end
|
|
208
208
|
|
|
209
|
-
# Window
|
|
209
|
+
# Window monkey-patch
|
|
210
210
|
class Window
|
|
211
211
|
unless method_defined?(:original_highlight)
|
|
212
212
|
alias_method :original_highlight, :highlight
|
|
@@ -14,25 +14,25 @@ module Textbringer
|
|
|
14
14
|
TreeSitter::Platform.dylib_ext
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
#
|
|
17
|
+
# List of directories to search for parsers (in priority order)
|
|
18
18
|
def parser_search_paths
|
|
19
19
|
paths = []
|
|
20
20
|
|
|
21
|
-
# 1. CONFIG
|
|
21
|
+
# 1. Custom path specified in CONFIG (highest priority)
|
|
22
22
|
if defined?(CONFIG) && CONFIG[:tree_sitter_parser_dir]
|
|
23
23
|
paths << CONFIG[:tree_sitter_parser_dir]
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
# 2. ~/.textbringer/parsers/{platform}
|
|
26
|
+
# 2. ~/.textbringer/parsers/{platform} (user-level shared)
|
|
27
27
|
paths << File.expand_path("~/.textbringer/parsers/#{platform}")
|
|
28
28
|
|
|
29
|
-
# 3.
|
|
29
|
+
# 3. parsers/{platform} inside the gem (default)
|
|
30
30
|
paths << File.expand_path("../../../parsers/#{platform}", __FILE__)
|
|
31
31
|
|
|
32
32
|
paths
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
#
|
|
35
|
+
# For backward compatibility, return the first path found
|
|
36
36
|
def parser_dir
|
|
37
37
|
parser_search_paths.find { |path| Dir.exist?(path) } || parser_search_paths.last
|
|
38
38
|
end
|
|
@@ -42,13 +42,13 @@ module Textbringer
|
|
|
42
42
|
normalized = TreeSitter::LanguageAliases.normalize(language)
|
|
43
43
|
filename = "libtree-sitter-#{normalized}#{dylib_ext}"
|
|
44
44
|
|
|
45
|
-
#
|
|
45
|
+
# Search for parser in search paths
|
|
46
46
|
parser_search_paths.each do |dir|
|
|
47
47
|
path = File.join(dir, filename)
|
|
48
48
|
return path if File.exist?(path)
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
#
|
|
51
|
+
# Return default path if not found
|
|
52
52
|
File.join(parser_search_paths.last, filename)
|
|
53
53
|
end
|
|
54
54
|
|
data/lib/textbringer_plugin.rb
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Textbringer plugin
|
|
4
|
-
# Textbringer
|
|
3
|
+
# Textbringer plugin entry point
|
|
4
|
+
# Loaded when Textbringer auto-loads plugins
|
|
5
5
|
|
|
6
|
-
# tree_sitter gem
|
|
7
|
-
# (
|
|
6
|
+
# Require tree_sitter gem first to prevent namespace collision
|
|
7
|
+
# (Define ::TreeSitter before Textbringer::TreeSitter)
|
|
8
8
|
begin
|
|
9
9
|
require "tree_sitter"
|
|
10
10
|
rescue LoadError
|
|
11
|
-
# tree_sitter gem
|
|
11
|
+
# Ignore if tree_sitter gem is not available (works without parser)
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
require "textbringer/tree_sitter/version"
|
|
@@ -16,10 +16,10 @@ require "textbringer/tree_sitter_config"
|
|
|
16
16
|
require "textbringer/tree_sitter/node_maps"
|
|
17
17
|
require "textbringer/tree_sitter_adapter"
|
|
18
18
|
|
|
19
|
-
#
|
|
19
|
+
# Define default Faces
|
|
20
20
|
Textbringer::TreeSitterConfig.define_default_faces
|
|
21
21
|
|
|
22
|
-
#
|
|
22
|
+
# Load user-defined node_maps
|
|
23
23
|
# ~/.textbringer/tree_sitter/node_maps/*.rb
|
|
24
24
|
user_node_maps_dir = File.expand_path("~/.textbringer/tree_sitter/node_maps")
|
|
25
25
|
if Dir.exist?(user_node_maps_dir)
|
|
@@ -32,7 +32,7 @@ if Dir.exist?(user_node_maps_dir)
|
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
|
-
#
|
|
35
|
+
# Existing Mode -> language mapping
|
|
36
36
|
MODE_LANGUAGE_MAP = {
|
|
37
37
|
"RubyMode" => :ruby,
|
|
38
38
|
"CMode" => :c,
|
|
@@ -65,7 +65,7 @@ MODE_LANGUAGE_MAP = {
|
|
|
65
65
|
"PascalMode" => :pascal,
|
|
66
66
|
}.freeze
|
|
67
67
|
|
|
68
|
-
#
|
|
68
|
+
# Language -> file pattern (for automatic Mode generation)
|
|
69
69
|
LANGUAGE_FILE_PATTERNS = {
|
|
70
70
|
markdown: /\.(md|markdown|mkd|mkdn)$/i,
|
|
71
71
|
hcl: /\.(tf|tfvars|hcl)$/i,
|
|
@@ -95,26 +95,26 @@ LANGUAGE_FILE_PATTERNS = {
|
|
|
95
95
|
pascal: /\.(pas|pp|p|inc)$/i,
|
|
96
96
|
}.freeze
|
|
97
97
|
|
|
98
|
-
# parser + node_map
|
|
98
|
+
# Auto-generate Mode for languages that have parser + node_map but no existing Mode
|
|
99
99
|
Textbringer::TreeSitter::NodeMaps.available_languages.each do |language|
|
|
100
100
|
next unless Textbringer::TreeSitterConfig.parser_available?(language)
|
|
101
101
|
|
|
102
|
-
#
|
|
102
|
+
# Look for an existing Mode
|
|
103
103
|
mode_name = MODE_LANGUAGE_MAP.key(language)&.to_s ||
|
|
104
104
|
"#{language.to_s.split('_').map(&:capitalize).join}Mode"
|
|
105
105
|
|
|
106
106
|
unless Textbringer.const_defined?(mode_name)
|
|
107
|
-
# Mode
|
|
107
|
+
# Auto-generate Mode if it does not exist
|
|
108
108
|
pattern = LANGUAGE_FILE_PATTERNS[language]
|
|
109
109
|
if pattern
|
|
110
|
-
# class_eval
|
|
110
|
+
# Define named class via class_eval (to trigger inherited hook properly)
|
|
111
111
|
Textbringer.class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
112
112
|
class #{mode_name} < ProgrammingMode
|
|
113
113
|
self.file_name_pattern = #{pattern.inspect}
|
|
114
114
|
|
|
115
|
-
#
|
|
115
|
+
# Define minimal indent_line (to allow line breaks)
|
|
116
116
|
def indent_line
|
|
117
|
-
#
|
|
117
|
+
# No-op by default
|
|
118
118
|
end
|
|
119
119
|
end
|
|
120
120
|
RUBY
|
|
@@ -122,13 +122,13 @@ Textbringer::TreeSitter::NodeMaps.available_languages.each do |language|
|
|
|
122
122
|
end
|
|
123
123
|
end
|
|
124
124
|
|
|
125
|
-
#
|
|
125
|
+
# Debug logging helper
|
|
126
126
|
def tree_sitter_debug(msg)
|
|
127
127
|
return unless ENV["TEXTBRINGER_TREE_SITTER_DEBUG"] == "1"
|
|
128
128
|
File.open("/tmp/tree_sitter_plugin.log", "a") { |f| f.puts "[#{Time.now}] #{msg}" }
|
|
129
129
|
end
|
|
130
130
|
|
|
131
|
-
#
|
|
131
|
+
# Enable tree-sitter on Modes that have an available parser and node_map
|
|
132
132
|
tree_sitter_debug "=== Enabling tree-sitter on modes ==="
|
|
133
133
|
tree_sitter_debug "available_languages: #{Textbringer::TreeSitter::NodeMaps.available_languages.inspect}"
|
|
134
134
|
|
|
@@ -147,19 +147,19 @@ MODE_LANGUAGE_MAP.each do |mode_name, language|
|
|
|
147
147
|
mode_class = Textbringer.const_get(mode_name)
|
|
148
148
|
tree_sitter_debug " found mode_class: #{mode_class}"
|
|
149
149
|
|
|
150
|
-
#
|
|
150
|
+
# Skip if tree-sitter is already configured
|
|
151
151
|
if mode_class.respond_to?(:tree_sitter_language) && mode_class.tree_sitter_language
|
|
152
152
|
tree_sitter_debug " already has tree_sitter_language: #{mode_class.tree_sitter_language}"
|
|
153
153
|
next
|
|
154
154
|
end
|
|
155
155
|
|
|
156
|
-
# TreeSitterAdapter
|
|
156
|
+
# Extend with TreeSitterAdapter and call use_tree_sitter
|
|
157
157
|
mode_class.extend(Textbringer::TreeSitterAdapter::ClassMethods)
|
|
158
158
|
mode_class.use_tree_sitter(language)
|
|
159
159
|
tree_sitter_debug " enabled tree-sitter for #{mode_name}"
|
|
160
160
|
rescue NameError => e
|
|
161
161
|
tree_sitter_debug " NameError: #{e.message}"
|
|
162
|
-
# Mode
|
|
162
|
+
# Ignore if Mode does not exist
|
|
163
163
|
end
|
|
164
164
|
end
|
|
165
165
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: textbringer-tree-sitter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.2.
|
|
4
|
+
version: 1.2.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- yancya
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-02-
|
|
11
|
+
date: 2026-02-23 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: textbringer
|