class TypeProf::LSP::Text
Attributes
caller_table[R]
definition_table[RW]
sigs[R]
text[R]
version[R]
Public Class Methods
new(server, uri, text, version)
click to toggle source
# File typeprof-0.21.11/lib/typeprof/lsp.rb, line 66 def initialize(server, uri, text, version) @server = server @uri = uri @text = text @version = version @sigs = nil @last_analysis_cancel_token = nil @analysis_queue = Queue.new @analysis_thread = Thread.new do loop do work = @analysis_queue.pop begin work.call rescue Exception puts "Rescued exception:" puts $!.full_message puts end end end # analyze synchronously to respond the first codeLens request res, def_table, caller_table = self.analyze(uri, text) on_text_changed_analysis(res, def_table, caller_table) end
Public Instance Methods
analyze(uri, text, cancel_token: nil, signature_help_loc: nil)
click to toggle source
# File typeprof-0.21.11/lib/typeprof/lsp.rb, line 277 def analyze(uri, text, cancel_token: nil, signature_help_loc: nil) config = @server.typeprof_config.dup path = URI(uri).path config.rb_files = [[path, text]] config.rbs_files = ["typeprof.rbs"] # XXX config.verbose = 0 config.max_sec = 1 config.options[:show_errors] = true config.options[:show_indicator] = false config.options[:lsp] = true config.options[:signature_help_loc] = [path, signature_help_loc] if signature_help_loc TypeProf.analyze(config, cancel_token) rescue SyntaxError end
apply_changes(changes, version)
click to toggle source
# File typeprof-0.21.11/lib/typeprof/lsp.rb, line 100 def apply_changes(changes, version) @definition_table = nil text = @text.empty? ? [] : @text.lines changes.each do |change| case change in { range: { start: { line: start_row, character: start_col }, end: { line: end_row , character: end_col } }, text: change_text, } else raise end text << "" if start_row == text.size text << "" if end_row == text.size if start_row == end_row text[start_row][start_col...end_col] = change_text else text[start_row][start_col..] = "" text[end_row][...end_col] = "" change_text = change_text.lines case change_text.size when 0 text[start_row] += text[end_row] text[start_row + 1 .. end_row] = [] when 1 text[start_row] += change_text.first + text[end_row] text[start_row + 1 .. end_row] = [] else text[start_row] += change_text.shift text[end_row].prepend(change_text.pop) text[start_row + 1 ... end_row - 1] = change_text end end end @text = text.join @version = version on_text_changed end
code_complete(loc, trigger_kind)
click to toggle source
# File typeprof-0.21.11/lib/typeprof/lsp.rb, line 161 def code_complete(loc, trigger_kind) case loc in { line: row, character: col } end unless row < @text.lines.length && col >= 1 && @text.lines[row][0, col] =~ /\.\w*$/ return nil end start_offset = $~.begin(0) end_offset = $&.size case trigger_kind when LSP::CompletionTriggerKind::TRIGGER_FOR_INCOMPLETE_COMPLETIONS unless @current_completion_session&.reusable?(row, start_offset) puts "no reusable completion session but got TRIGGER_FOR_INCOMPLETE_COMPLETIONS" @current_completion_session = new_code_completion_session(row, start_offset, end_offset) end return @current_completion_session.results else @current_completion_session = new_code_completion_session(row, start_offset, end_offset) return @current_completion_session&.results end end
lines()
click to toggle source
# File typeprof-0.21.11/lib/typeprof/lsp.rb, line 96 def lines @text.lines end
new_code_completion_session(row, start_offset, end_offset)
click to toggle source
# File typeprof-0.21.11/lib/typeprof/lsp.rb, line 143 def new_code_completion_session(row, start_offset, end_offset) lines = @text.lines lines[row][start_offset, end_offset] = ".__typeprof_lsp_completion" tmp_text = lines.join res, = analyze(@uri, tmp_text) if res && res[:completion] results = res[:completion].keys.map do |name| { label: name, kind: 2, # Method } end return CompletionSession.new(results, row, start_offset) else nil end end
on_text_changed()
click to toggle source
# File typeprof-0.21.11/lib/typeprof/lsp.rb, line 297 def on_text_changed cancel_token = AnalysisToken.new @last_analysis_cancel_token&.cancel @last_analysis_cancel_token = cancel_token uri = @uri text = @text self.push_analysis_queue do if cancel_token.cancelled? next end res, def_table, caller_table = self.analyze(uri, text, cancel_token: cancel_token) unless cancel_token.cancelled? on_text_changed_analysis(res, def_table, caller_table) end end end
on_text_changed_analysis(res, definition_table, caller_table)
click to toggle source
# File typeprof-0.21.11/lib/typeprof/lsp.rb, line 315 def on_text_changed_analysis(res, definition_table, caller_table) @definition_table = definition_table @caller_table = caller_table return unless res @sigs = [] res[:sigs].each do |file, lineno, sig_str, rbs_code_range, class_kind, class_name| uri0 = "file://" + file if @uri == uri0 command = { title: sig_str } if rbs_code_range command[:command] = "typeprof.jumpToRBS" command[:arguments] = [uri0, { line: lineno - 1, character: 0 }, @server.root_uri + "/" + rbs_code_range[0], rbs_code_range[1].to_lsp] else command[:command] = "typeprof.createPrototypeRBS" command[:arguments] = [class_kind, class_name, sig_str] end @sigs << { range: { start: { line: lineno - 1, character: 0 }, end: { line: lineno - 1, character: 1 }, }, command: command, } end end diagnostics = {} res[:errors]&.each do |(file, code_range), msg| next unless file and code_range uri0 = "file://" + file diagnostics[uri0] ||= [] diagnostics[uri0] << { range: code_range.to_lsp, severity: 1, source: "TypeProf", message: msg, } end @server.send_notification('typeprof.enableToggleButton') @server.send_request("workspace/codeLens/refresh") @server.send_notification( "textDocument/publishDiagnostics", { uri: @uri, version: version, diagnostics: diagnostics[@uri] || [], } ) end
push_analysis_queue(&work)
click to toggle source
# File typeprof-0.21.11/lib/typeprof/lsp.rb, line 293 def push_analysis_queue(&work) @analysis_queue.push(work) end
signature_help(loc, trigger_kind)
click to toggle source
# File typeprof-0.21.11/lib/typeprof/lsp.rb, line 250 def signature_help(loc, trigger_kind) loc = CodeLocation.from_lsp(loc) res, = analyze(@uri, @text, signature_help_loc: loc) if res res[:signature_help].filter_map do |sig_str, sig_help, node_id| node = ISeq.find_node_by_id(@text, node_id) if node && ISeq.code_range_from_node(node).contain_loc?(loc) idx = locate_arg_index_in_signature_help(node, loc, sig_help) { label: sig_str, parameters: sig_help.values.map do |r| { label: [r.begin, r.end], } end, activeParameter: idx, } end end else nil end end
Private Instance Methods
locate_arg_index_in_signature_help(node, loc, sig_help)
click to toggle source
# File typeprof-0.21.11/lib/typeprof/lsp.rb, line 184 def locate_arg_index_in_signature_help(node, loc, sig_help) case node.type when :FCALL _mid, args_node = node.children when :CALL _recv, _mid, args_node = node.children end idx = 0 if args_node arg_nodes = args_node.children.compact arg_indexes = {} hash = arg_nodes.pop if arg_nodes.last&.type == :HASH arg_nodes.each_with_index do |node, i| # Ingore arguments after rest argument break if node.type == :LIST || node.type == :ARGSCAT arg_indexes[i] = ISeq.code_range_from_node(node) end # Handle keyword arguments if hash hash.children.last.children.compact.each_slice(2) do |node1, node2| # key: expression # ^^^^ ^^^^^^^^^^ # node1 node2 key = node1.children.first arg_indexes[key] = CodeRange.new( CodeLocation.new(node1.first_lineno, node1.first_lineno), CodeLocation.new(node2.last_lineno, node2.last_lineno), ) end end if arg_indexes.size >= 1 && arg_indexes.values.last.last < loc # There is the cursor after the last argument: "foo(111, 222,|)" idx = arg_indexes.size - 1 prev_cr = arg_indexes.values.last if prev_cr.last.lineno == loc.lineno line = @text.lines[prev_cr.last.lineno - 1] idx += 1 if line[prev_cr.last.column..loc.column].include?(",") end else # There is the cursor within any argument: "foo(111,|222)" or foo(111, 22|2)" prev_cr = nil arg_indexes.each do |i, cr| idx = sig_help.keys.index(i) if loc < cr.first break if !prev_cr || prev_cr.last.lineno != loc.lineno line = @text.lines[prev_cr.last.lineno - 1] idx -= 1 unless line[prev_cr.last.column..loc.column].include?(",") break end break if loc <= cr.last prev_cr = cr end end end idx end