t-ruby 0.0.35 → 0.0.36
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/README.md +5 -4
- data/lib/t_ruby/benchmark.rb +16 -16
- data/lib/t_ruby/bundler_integration.rb +33 -35
- data/lib/t_ruby/cache.rb +46 -43
- data/lib/t_ruby/cli.rb +3 -3
- data/lib/t_ruby/compiler.rb +25 -27
- data/lib/t_ruby/config.rb +15 -11
- data/lib/t_ruby/constraint_checker.rb +14 -8
- data/lib/t_ruby/declaration_generator.rb +8 -8
- data/lib/t_ruby/doc_generator.rb +33 -33
- data/lib/t_ruby/docs_badge_generator.rb +8 -8
- data/lib/t_ruby/docs_example_verifier.rb +5 -6
- data/lib/t_ruby/error_handler.rb +21 -22
- data/lib/t_ruby/generic_type_parser.rb +3 -3
- data/lib/t_ruby/intersection_type_parser.rb +2 -2
- data/lib/t_ruby/ir.rb +21 -24
- data/lib/t_ruby/lsp_server.rb +128 -138
- data/lib/t_ruby/package_manager.rb +16 -18
- data/lib/t_ruby/parser.rb +7 -7
- data/lib/t_ruby/parser_combinator.rb +22 -22
- data/lib/t_ruby/rbs_generator.rb +2 -4
- data/lib/t_ruby/runtime_validator.rb +41 -37
- data/lib/t_ruby/smt_solver.rb +31 -29
- data/lib/t_ruby/type_alias_registry.rb +1 -0
- data/lib/t_ruby/type_checker.rb +64 -63
- data/lib/t_ruby/type_erasure.rb +2 -4
- data/lib/t_ruby/type_inferencer.rb +22 -26
- data/lib/t_ruby/union_type_parser.rb +3 -3
- data/lib/t_ruby/version.rb +1 -1
- data/lib/t_ruby/watcher.rb +18 -14
- metadata +4 -3
data/lib/t_ruby/lsp_server.rb
CHANGED
|
@@ -10,13 +10,13 @@ module TRuby
|
|
|
10
10
|
|
|
11
11
|
# LSP Error codes
|
|
12
12
|
module ErrorCodes
|
|
13
|
-
PARSE_ERROR = -
|
|
14
|
-
INVALID_REQUEST = -
|
|
15
|
-
METHOD_NOT_FOUND = -
|
|
16
|
-
INVALID_PARAMS = -
|
|
17
|
-
INTERNAL_ERROR = -
|
|
18
|
-
SERVER_NOT_INITIALIZED = -
|
|
19
|
-
UNKNOWN_ERROR_CODE = -
|
|
13
|
+
PARSE_ERROR = -32_700
|
|
14
|
+
INVALID_REQUEST = -32_600
|
|
15
|
+
METHOD_NOT_FOUND = -32_601
|
|
16
|
+
INVALID_PARAMS = -32_602
|
|
17
|
+
INTERNAL_ERROR = -32_603
|
|
18
|
+
SERVER_NOT_INITIALIZED = -32_002
|
|
19
|
+
UNKNOWN_ERROR_CODE = -32_001
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
# LSP Completion item kinds
|
|
@@ -152,7 +152,7 @@ module TRuby
|
|
|
152
152
|
end
|
|
153
153
|
|
|
154
154
|
content_length = headers["Content-Length"]&.to_i
|
|
155
|
-
return nil unless content_length
|
|
155
|
+
return nil unless content_length&.positive?
|
|
156
156
|
|
|
157
157
|
# Read content
|
|
158
158
|
content = @input.read(content_length)
|
|
@@ -178,7 +178,7 @@ module TRuby
|
|
|
178
178
|
notification = {
|
|
179
179
|
"jsonrpc" => "2.0",
|
|
180
180
|
"method" => method,
|
|
181
|
-
"params" => params
|
|
181
|
+
"params" => params,
|
|
182
182
|
}
|
|
183
183
|
send_response(notification)
|
|
184
184
|
end
|
|
@@ -210,7 +210,7 @@ module TRuby
|
|
|
210
210
|
|
|
211
211
|
private
|
|
212
212
|
|
|
213
|
-
def dispatch_method(method, params,
|
|
213
|
+
def dispatch_method(method, params, _id)
|
|
214
214
|
case method
|
|
215
215
|
when "initialize"
|
|
216
216
|
handle_initialize(params)
|
|
@@ -253,31 +253,31 @@ module TRuby
|
|
|
253
253
|
"textDocumentSync" => {
|
|
254
254
|
"openClose" => true,
|
|
255
255
|
"change" => 1, # Full sync
|
|
256
|
-
"save" => { "includeText" => true }
|
|
256
|
+
"save" => { "includeText" => true },
|
|
257
257
|
},
|
|
258
258
|
"completionProvider" => {
|
|
259
259
|
"triggerCharacters" => [":", "<", "|", "&"],
|
|
260
|
-
"resolveProvider" => false
|
|
260
|
+
"resolveProvider" => false,
|
|
261
261
|
},
|
|
262
262
|
"hoverProvider" => true,
|
|
263
263
|
"definitionProvider" => true,
|
|
264
264
|
"diagnosticProvider" => {
|
|
265
265
|
"interFileDependencies" => false,
|
|
266
|
-
"workspaceDiagnostics" => false
|
|
266
|
+
"workspaceDiagnostics" => false,
|
|
267
267
|
},
|
|
268
268
|
"semanticTokensProvider" => {
|
|
269
269
|
"legend" => {
|
|
270
270
|
"tokenTypes" => SEMANTIC_TOKEN_TYPES,
|
|
271
|
-
"tokenModifiers" => SEMANTIC_TOKEN_MODIFIERS
|
|
271
|
+
"tokenModifiers" => SEMANTIC_TOKEN_MODIFIERS,
|
|
272
272
|
},
|
|
273
273
|
"full" => true,
|
|
274
|
-
"range" => false
|
|
275
|
-
}
|
|
274
|
+
"range" => false,
|
|
275
|
+
},
|
|
276
276
|
},
|
|
277
277
|
"serverInfo" => {
|
|
278
278
|
"name" => "t-ruby-lsp",
|
|
279
|
-
"version" => VERSION
|
|
280
|
-
}
|
|
279
|
+
"version" => VERSION,
|
|
280
|
+
},
|
|
281
281
|
}
|
|
282
282
|
end
|
|
283
283
|
|
|
@@ -304,7 +304,7 @@ module TRuby
|
|
|
304
304
|
|
|
305
305
|
@documents[uri] = {
|
|
306
306
|
text: text,
|
|
307
|
-
version: text_document["version"]
|
|
307
|
+
version: text_document["version"],
|
|
308
308
|
}
|
|
309
309
|
|
|
310
310
|
# Parse and send diagnostics
|
|
@@ -321,7 +321,7 @@ module TRuby
|
|
|
321
321
|
if changes && !changes.empty?
|
|
322
322
|
@documents[uri] = {
|
|
323
323
|
text: changes.last["text"],
|
|
324
|
-
version: text_document["version"]
|
|
324
|
+
version: text_document["version"],
|
|
325
325
|
}
|
|
326
326
|
|
|
327
327
|
# Re-parse and send diagnostics
|
|
@@ -336,9 +336,9 @@ module TRuby
|
|
|
336
336
|
|
|
337
337
|
# Clear diagnostics
|
|
338
338
|
send_notification("textDocument/publishDiagnostics", {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
339
|
+
"uri" => uri,
|
|
340
|
+
"diagnostics" => [],
|
|
341
|
+
})
|
|
342
342
|
nil
|
|
343
343
|
end
|
|
344
344
|
|
|
@@ -363,9 +363,9 @@ module TRuby
|
|
|
363
363
|
diagnostics = analyze_document(text)
|
|
364
364
|
|
|
365
365
|
send_notification("textDocument/publishDiagnostics", {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
366
|
+
"uri" => uri,
|
|
367
|
+
"diagnostics" => diagnostics,
|
|
368
|
+
})
|
|
369
369
|
end
|
|
370
370
|
|
|
371
371
|
def analyze_document(text)
|
|
@@ -377,12 +377,12 @@ module TRuby
|
|
|
377
377
|
|
|
378
378
|
errors.each do |error|
|
|
379
379
|
# Parse line number from error message
|
|
380
|
-
|
|
381
|
-
line_num = Regexp.last_match(1).to_i - 1 # LSP uses 0-based line numbers
|
|
382
|
-
message = Regexp.last_match(2)
|
|
380
|
+
next unless error =~ /^Line (\d+):\s*(.+)$/
|
|
383
381
|
|
|
384
|
-
|
|
385
|
-
|
|
382
|
+
line_num = Regexp.last_match(1).to_i - 1 # LSP uses 0-based line numbers
|
|
383
|
+
message = Regexp.last_match(2)
|
|
384
|
+
|
|
385
|
+
diagnostics << create_diagnostic(line_num, message, DiagnosticSeverity::ERROR)
|
|
386
386
|
end
|
|
387
387
|
|
|
388
388
|
# Additional validation using Parser
|
|
@@ -428,25 +428,23 @@ module TRuby
|
|
|
428
428
|
next unless line_num
|
|
429
429
|
|
|
430
430
|
# Validate return type
|
|
431
|
-
if func[:return_type]
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
)
|
|
438
|
-
end
|
|
431
|
+
if func[:return_type] && !valid_type?(func[:return_type])
|
|
432
|
+
diagnostics << create_diagnostic(
|
|
433
|
+
line_num,
|
|
434
|
+
"Unknown return type '#{func[:return_type]}'",
|
|
435
|
+
DiagnosticSeverity::WARNING
|
|
436
|
+
)
|
|
439
437
|
end
|
|
440
438
|
|
|
441
439
|
# Validate parameter types
|
|
442
440
|
func[:params]&.each do |param|
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
441
|
+
next unless param[:type] && !valid_type?(param[:type])
|
|
442
|
+
|
|
443
|
+
diagnostics << create_diagnostic(
|
|
444
|
+
line_num,
|
|
445
|
+
"Unknown parameter type '#{param[:type]}' for '#{param[:name]}'",
|
|
446
|
+
DiagnosticSeverity::WARNING
|
|
447
|
+
)
|
|
450
448
|
end
|
|
451
449
|
end
|
|
452
450
|
end
|
|
@@ -484,11 +482,11 @@ module TRuby
|
|
|
484
482
|
{
|
|
485
483
|
"range" => {
|
|
486
484
|
"start" => { "line" => line, "character" => 0 },
|
|
487
|
-
"end" => { "line" => line, "character" => 1000 }
|
|
485
|
+
"end" => { "line" => line, "character" => 1000 },
|
|
488
486
|
},
|
|
489
487
|
"severity" => severity,
|
|
490
488
|
"source" => "t-ruby",
|
|
491
|
-
"message" => message
|
|
489
|
+
"message" => message,
|
|
492
490
|
}
|
|
493
491
|
end
|
|
494
492
|
|
|
@@ -512,28 +510,29 @@ module TRuby
|
|
|
512
510
|
completions = []
|
|
513
511
|
|
|
514
512
|
# Context-aware completion
|
|
515
|
-
|
|
513
|
+
case prefix
|
|
514
|
+
when /:\s*$/
|
|
516
515
|
# After colon - suggest types
|
|
517
516
|
completions.concat(type_completions)
|
|
518
|
-
|
|
517
|
+
when /\|\s*$/
|
|
519
518
|
# After pipe - suggest types for union
|
|
520
519
|
completions.concat(type_completions)
|
|
521
|
-
|
|
520
|
+
when /&\s*$/
|
|
522
521
|
# After ampersand - suggest types for intersection
|
|
523
522
|
completions.concat(type_completions)
|
|
524
|
-
|
|
523
|
+
when /<\s*$/
|
|
525
524
|
# Inside generic - suggest types
|
|
526
525
|
completions.concat(type_completions)
|
|
527
|
-
|
|
526
|
+
when /^\s*$/
|
|
528
527
|
# Start of line - suggest keywords
|
|
529
528
|
completions.concat(keyword_completions)
|
|
530
|
-
|
|
529
|
+
when /^\s*def\s+\w*$/
|
|
531
530
|
# Function definition - no completion needed
|
|
532
531
|
completions = []
|
|
533
|
-
|
|
532
|
+
when /^\s*type\s+\w*$/
|
|
534
533
|
# Type alias definition - no completion needed
|
|
535
534
|
completions = []
|
|
536
|
-
|
|
535
|
+
when /^\s*interface\s+\w*$/
|
|
537
536
|
# Interface definition - no completion needed
|
|
538
537
|
completions = []
|
|
539
538
|
else
|
|
@@ -554,7 +553,7 @@ module TRuby
|
|
|
554
553
|
"label" => type,
|
|
555
554
|
"kind" => CompletionItemKind::CLASS,
|
|
556
555
|
"detail" => "Built-in type",
|
|
557
|
-
"documentation" => "T-Ruby built-in type: #{type}"
|
|
556
|
+
"documentation" => "T-Ruby built-in type: #{type}",
|
|
558
557
|
}
|
|
559
558
|
end
|
|
560
559
|
end
|
|
@@ -565,7 +564,7 @@ module TRuby
|
|
|
565
564
|
"label" => keyword,
|
|
566
565
|
"kind" => CompletionItemKind::KEYWORD,
|
|
567
566
|
"detail" => "Keyword",
|
|
568
|
-
"documentation" => keyword_documentation(keyword)
|
|
567
|
+
"documentation" => keyword_documentation(keyword),
|
|
569
568
|
}
|
|
570
569
|
end
|
|
571
570
|
end
|
|
@@ -586,17 +585,16 @@ module TRuby
|
|
|
586
585
|
end
|
|
587
586
|
|
|
588
587
|
def document_type_completions(text)
|
|
589
|
-
completions = []
|
|
590
588
|
parser = Parser.new(text)
|
|
591
589
|
result = parser.parse
|
|
592
590
|
|
|
593
591
|
# Add type aliases from the document
|
|
594
|
-
(result[:type_aliases] || []).
|
|
595
|
-
|
|
592
|
+
completions = (result[:type_aliases] || []).map do |alias_info|
|
|
593
|
+
{
|
|
596
594
|
"label" => alias_info[:name],
|
|
597
595
|
"kind" => CompletionItemKind::CLASS,
|
|
598
596
|
"detail" => "Type alias",
|
|
599
|
-
"documentation" => "type #{alias_info[:name]} = #{alias_info[:definition]}"
|
|
597
|
+
"documentation" => "type #{alias_info[:name]} = #{alias_info[:definition]}",
|
|
600
598
|
}
|
|
601
599
|
end
|
|
602
600
|
|
|
@@ -606,7 +604,7 @@ module TRuby
|
|
|
606
604
|
"label" => interface_info[:name],
|
|
607
605
|
"kind" => CompletionItemKind::INTERFACE,
|
|
608
606
|
"detail" => "Interface",
|
|
609
|
-
"documentation" => "interface #{interface_info[:name]}"
|
|
607
|
+
"documentation" => "interface #{interface_info[:name]}",
|
|
610
608
|
}
|
|
611
609
|
end
|
|
612
610
|
|
|
@@ -637,9 +635,9 @@ module TRuby
|
|
|
637
635
|
{
|
|
638
636
|
"contents" => {
|
|
639
637
|
"kind" => "markdown",
|
|
640
|
-
"value" => hover_info
|
|
638
|
+
"value" => hover_info,
|
|
641
639
|
},
|
|
642
|
-
"range" => word_range(position["line"], line, char_pos, word)
|
|
640
|
+
"range" => word_range(position["line"], line, char_pos, word),
|
|
643
641
|
}
|
|
644
642
|
end
|
|
645
643
|
|
|
@@ -651,14 +649,10 @@ module TRuby
|
|
|
651
649
|
end_pos = char_pos
|
|
652
650
|
|
|
653
651
|
# Move start back to word start
|
|
654
|
-
while start_pos
|
|
655
|
-
start_pos -= 1
|
|
656
|
-
end
|
|
652
|
+
start_pos -= 1 while start_pos.positive? && line[start_pos - 1] =~ /[\w<>]/
|
|
657
653
|
|
|
658
654
|
# Move end forward to word end
|
|
659
|
-
while end_pos < line.length && line[end_pos] =~ /[\w<>]/
|
|
660
|
-
end_pos += 1
|
|
661
|
-
end
|
|
655
|
+
end_pos += 1 while end_pos < line.length && line[end_pos] =~ /[\w<>]/
|
|
662
656
|
|
|
663
657
|
return nil if start_pos == end_pos
|
|
664
658
|
|
|
@@ -671,7 +665,7 @@ module TRuby
|
|
|
671
665
|
|
|
672
666
|
{
|
|
673
667
|
"start" => { "line" => line_num, "character" => start_pos },
|
|
674
|
-
"end" => { "line" => line_num, "character" => end_pos }
|
|
668
|
+
"end" => { "line" => line_num, "character" => end_pos },
|
|
675
669
|
}
|
|
676
670
|
end
|
|
677
671
|
|
|
@@ -701,11 +695,11 @@ module TRuby
|
|
|
701
695
|
|
|
702
696
|
# Check if it's a function
|
|
703
697
|
(result[:functions] || []).each do |func|
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
698
|
+
next unless func[:name] == word
|
|
699
|
+
|
|
700
|
+
params = func[:params].map { |p| "#{p[:name]}: #{p[:type] || "untyped"}" }.join(", ")
|
|
701
|
+
return_type = func[:return_type] || "void"
|
|
702
|
+
return "**Function**\n\n```ruby\ndef #{func[:name]}(#{params}): #{return_type}\n```"
|
|
709
703
|
end
|
|
710
704
|
|
|
711
705
|
nil
|
|
@@ -745,34 +739,30 @@ module TRuby
|
|
|
745
739
|
"uri" => uri,
|
|
746
740
|
"range" => {
|
|
747
741
|
"start" => { "line" => idx, "character" => 0 },
|
|
748
|
-
"end" => { "line" => idx, "character" => line.length }
|
|
749
|
-
}
|
|
742
|
+
"end" => { "line" => idx, "character" => line.length },
|
|
743
|
+
},
|
|
750
744
|
}
|
|
751
745
|
end
|
|
752
|
-
end
|
|
753
746
|
|
|
754
|
-
|
|
755
|
-
lines.each_with_index do |line, idx|
|
|
747
|
+
# Search for interface definition
|
|
756
748
|
if line.match?(/^\s*interface\s+#{Regexp.escape(word)}\s*$/)
|
|
757
749
|
return {
|
|
758
750
|
"uri" => uri,
|
|
759
751
|
"range" => {
|
|
760
752
|
"start" => { "line" => idx, "character" => 0 },
|
|
761
|
-
"end" => { "line" => idx, "character" => line.length }
|
|
762
|
-
}
|
|
753
|
+
"end" => { "line" => idx, "character" => line.length },
|
|
754
|
+
},
|
|
763
755
|
}
|
|
764
756
|
end
|
|
765
|
-
end
|
|
766
757
|
|
|
767
|
-
|
|
768
|
-
lines.each_with_index do |line, idx|
|
|
758
|
+
# Search for function definition
|
|
769
759
|
if line.match?(/^\s*def\s+#{Regexp.escape(word)}\s*\(/)
|
|
770
760
|
return {
|
|
771
761
|
"uri" => uri,
|
|
772
762
|
"range" => {
|
|
773
763
|
"start" => { "line" => idx, "character" => 0 },
|
|
774
|
-
"end" => { "line" => idx, "character" => line.length }
|
|
775
|
-
}
|
|
764
|
+
"end" => { "line" => idx, "character" => line.length },
|
|
765
|
+
},
|
|
776
766
|
}
|
|
777
767
|
end
|
|
778
768
|
end
|
|
@@ -794,13 +784,12 @@ module TRuby
|
|
|
794
784
|
end
|
|
795
785
|
|
|
796
786
|
def generate_semantic_tokens(text)
|
|
797
|
-
tokens = []
|
|
798
787
|
lines = text.split("\n")
|
|
799
788
|
|
|
800
789
|
# Parse the document to get IR
|
|
801
790
|
parser = Parser.new(text, use_combinator: true)
|
|
802
791
|
parse_result = parser.parse
|
|
803
|
-
|
|
792
|
+
parser.ir_program
|
|
804
793
|
|
|
805
794
|
# Collect all tokens from parsing
|
|
806
795
|
raw_tokens = []
|
|
@@ -808,25 +797,25 @@ module TRuby
|
|
|
808
797
|
# Process type aliases
|
|
809
798
|
(parse_result[:type_aliases] || []).each do |alias_info|
|
|
810
799
|
lines.each_with_index do |line, line_idx|
|
|
811
|
-
|
|
812
|
-
# 'type' keyword
|
|
813
|
-
type_pos = line.index("type")
|
|
814
|
-
raw_tokens << [line_idx, type_pos, 4, SemanticTokenTypes::KEYWORD, SemanticTokenModifiers::DECLARATION]
|
|
800
|
+
next unless (match = line.match(/^\s*type\s+(#{Regexp.escape(alias_info[:name])})\s*=/))
|
|
815
801
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
802
|
+
# 'type' keyword
|
|
803
|
+
type_pos = line.index("type")
|
|
804
|
+
raw_tokens << [line_idx, type_pos, 4, SemanticTokenTypes::KEYWORD, SemanticTokenModifiers::DECLARATION]
|
|
819
805
|
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
806
|
+
# Type name
|
|
807
|
+
name_pos = match.begin(1)
|
|
808
|
+
raw_tokens << [line_idx, name_pos, alias_info[:name].length, SemanticTokenTypes::TYPE, SemanticTokenModifiers::DEFINITION]
|
|
809
|
+
|
|
810
|
+
# Type definition (after =)
|
|
811
|
+
add_type_tokens(raw_tokens, line, line_idx, alias_info[:definition])
|
|
823
812
|
end
|
|
824
813
|
end
|
|
825
814
|
|
|
826
815
|
# Process interfaces
|
|
827
816
|
(parse_result[:interfaces] || []).each do |interface_info|
|
|
828
817
|
lines.each_with_index do |line, line_idx|
|
|
829
|
-
if match = line.match(/^\s*interface\s+(#{Regexp.escape(interface_info[:name])})/)
|
|
818
|
+
if (match = line.match(/^\s*interface\s+(#{Regexp.escape(interface_info[:name])})/))
|
|
830
819
|
# 'interface' keyword
|
|
831
820
|
interface_pos = line.index("interface")
|
|
832
821
|
raw_tokens << [line_idx, interface_pos, 9, SemanticTokenTypes::KEYWORD, SemanticTokenModifiers::DECLARATION]
|
|
@@ -838,13 +827,13 @@ module TRuby
|
|
|
838
827
|
|
|
839
828
|
# Interface members
|
|
840
829
|
interface_info[:members]&.each do |member|
|
|
841
|
-
|
|
842
|
-
prop_pos = match.begin(1)
|
|
843
|
-
raw_tokens << [line_idx, prop_pos, member[:name].length, SemanticTokenTypes::PROPERTY, 0]
|
|
830
|
+
next unless (match = line.match(/^\s*(#{Regexp.escape(member[:name])})\s*:\s*/))
|
|
844
831
|
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
832
|
+
prop_pos = match.begin(1)
|
|
833
|
+
raw_tokens << [line_idx, prop_pos, member[:name].length, SemanticTokenTypes::PROPERTY, 0]
|
|
834
|
+
|
|
835
|
+
# Member type
|
|
836
|
+
add_type_tokens(raw_tokens, line, line_idx, member[:type])
|
|
848
837
|
end
|
|
849
838
|
end
|
|
850
839
|
end
|
|
@@ -852,39 +841,39 @@ module TRuby
|
|
|
852
841
|
# Process functions
|
|
853
842
|
(parse_result[:functions] || []).each do |func|
|
|
854
843
|
lines.each_with_index do |line, line_idx|
|
|
855
|
-
|
|
856
|
-
# 'def' keyword
|
|
857
|
-
def_pos = line.index("def")
|
|
858
|
-
raw_tokens << [line_idx, def_pos, 3, SemanticTokenTypes::KEYWORD, 0]
|
|
844
|
+
next unless (match = line.match(/^\s*def\s+(#{Regexp.escape(func[:name])})\s*\(/))
|
|
859
845
|
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
if param[:type]
|
|
872
|
-
add_type_tokens(raw_tokens, line, line_idx, param[:type])
|
|
873
|
-
end
|
|
874
|
-
end
|
|
875
|
-
end
|
|
846
|
+
# 'def' keyword
|
|
847
|
+
def_pos = line.index("def")
|
|
848
|
+
raw_tokens << [line_idx, def_pos, 3, SemanticTokenTypes::KEYWORD, 0]
|
|
849
|
+
|
|
850
|
+
# Function name
|
|
851
|
+
name_pos = match.begin(1)
|
|
852
|
+
raw_tokens << [line_idx, name_pos, func[:name].length, SemanticTokenTypes::FUNCTION, SemanticTokenModifiers::DEFINITION]
|
|
853
|
+
|
|
854
|
+
# Parameters
|
|
855
|
+
func[:params]&.each do |param|
|
|
856
|
+
next unless (param_match = line.match(/\b(#{Regexp.escape(param[:name])})\s*(?::\s*)?/))
|
|
876
857
|
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
858
|
+
param_pos = param_match.begin(1)
|
|
859
|
+
raw_tokens << [line_idx, param_pos, param[:name].length, SemanticTokenTypes::PARAMETER, 0]
|
|
860
|
+
|
|
861
|
+
# Parameter type if present
|
|
862
|
+
if param[:type]
|
|
863
|
+
add_type_tokens(raw_tokens, line, line_idx, param[:type])
|
|
880
864
|
end
|
|
881
865
|
end
|
|
866
|
+
|
|
867
|
+
# Return type
|
|
868
|
+
if func[:return_type]
|
|
869
|
+
add_type_tokens(raw_tokens, line, line_idx, func[:return_type])
|
|
870
|
+
end
|
|
882
871
|
end
|
|
883
872
|
end
|
|
884
873
|
|
|
885
874
|
# Process 'end' keywords
|
|
886
875
|
lines.each_with_index do |line, line_idx|
|
|
887
|
-
if match = line.match(/^\s*(end)\s*$/)
|
|
876
|
+
if (match = line.match(/^\s*(end)\s*$/))
|
|
888
877
|
end_pos = match.begin(1)
|
|
889
878
|
raw_tokens << [line_idx, end_pos, 3, SemanticTokenTypes::KEYWORD, 0]
|
|
890
879
|
end
|
|
@@ -912,7 +901,7 @@ module TRuby
|
|
|
912
901
|
|
|
913
902
|
# Handle generic types like Array<String>
|
|
914
903
|
if type_str.include?("<")
|
|
915
|
-
if match = type_str.match(/^(\w+)<(.+)>$/)
|
|
904
|
+
if (match = type_str.match(/^(\w+)<(.+)>$/))
|
|
916
905
|
base = match[1]
|
|
917
906
|
base_pos = line.index(base, pos)
|
|
918
907
|
if base_pos
|
|
@@ -923,8 +912,9 @@ module TRuby
|
|
|
923
912
|
# (simplified - just mark them as types)
|
|
924
913
|
args = match[2]
|
|
925
914
|
args.split(/[,\s]+/).each do |arg|
|
|
926
|
-
arg = arg.strip.gsub(/[<>]/,
|
|
915
|
+
arg = arg.strip.gsub(/[<>]/, "")
|
|
927
916
|
next if arg.empty?
|
|
917
|
+
|
|
928
918
|
arg_pos = line.index(arg, pos)
|
|
929
919
|
if arg_pos
|
|
930
920
|
modifier = BUILT_IN_TYPES.include?(arg) ? SemanticTokenModifiers::DEFAULT_LIBRARY : 0
|
|
@@ -972,7 +962,7 @@ module TRuby
|
|
|
972
962
|
line, char, length, token_type, modifiers = token
|
|
973
963
|
|
|
974
964
|
delta_line = line - prev_line
|
|
975
|
-
delta_char = delta_line
|
|
965
|
+
delta_char = delta_line.zero? ? char - prev_char : char
|
|
976
966
|
|
|
977
967
|
encoded << delta_line
|
|
978
968
|
encoded << delta_char
|
|
@@ -993,7 +983,7 @@ module TRuby
|
|
|
993
983
|
{
|
|
994
984
|
"jsonrpc" => "2.0",
|
|
995
985
|
"id" => id,
|
|
996
|
-
"result" => result
|
|
986
|
+
"result" => result,
|
|
997
987
|
}
|
|
998
988
|
end
|
|
999
989
|
|
|
@@ -1003,8 +993,8 @@ module TRuby
|
|
|
1003
993
|
"id" => id,
|
|
1004
994
|
"error" => {
|
|
1005
995
|
"code" => code,
|
|
1006
|
-
"message" => message
|
|
1007
|
-
}
|
|
996
|
+
"message" => message,
|
|
997
|
+
},
|
|
1008
998
|
}
|
|
1009
999
|
end
|
|
1010
1000
|
end
|