Class: TypeProf::LSP::Text
Relationships & Source Files | |
Namespace Children | |
Classes:
| |
Inherits: | Object |
Defined in: | lib/typeprof/lsp.rb |
Class Method Summary
- .new(server, uri, text, version) ⇒ Text constructor
Instance Attribute Summary
- #caller_table readonly
- #definition_table rw
- #sigs readonly
- #text readonly
- #version readonly
Instance Method Summary
- #analyze(uri, text, cancel_token: nil, signature_help_loc: nil)
- #apply_changes(changes, version)
- #code_complete(loc, trigger_kind)
- #lines
- #new_code_completion_session(row, start_offset, end_offset)
- #on_text_changed
- #on_text_changed_analysis(res, definition_table, caller_table)
- #push_analysis_queue(&work)
- #signature_help(loc, trigger_kind)
- #locate_arg_index_in_signature_help(node, loc, sig_help) private
Constructor Details
.new(server, uri, text, version) ⇒ Text
# File '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 $!. 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
Instance Attribute Details
#caller_table (readonly)
[ GitHub ]#definition_table (rw)
[ GitHub ]# File 'lib/typeprof/lsp.rb', line 94
attr_accessor :definition_table
#sigs (readonly)
[ GitHub ]# File 'lib/typeprof/lsp.rb', line 93
attr_reader :text, :version, :sigs, :caller_table
#text (readonly)
[ GitHub ]# File 'lib/typeprof/lsp.rb', line 93
attr_reader :text, :version, :sigs, :caller_table
#version (readonly)
[ GitHub ]# File 'lib/typeprof/lsp.rb', line 93
attr_reader :text, :version, :sigs, :caller_table
Instance Method Details
#analyze(uri, text, cancel_token: nil, signature_help_loc: nil)
[ GitHub ]# File '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. [:show_errors] = true config. [:show_indicator] = false config. [:lsp] = true config. [:signature_help_loc] = [path, signature_help_loc] if signature_help_loc TypeProf.analyze(config, cancel_token) rescue SyntaxError end
#apply_changes(changes, version)
[ GitHub ]# File '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)
[ GitHub ]# File '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
[ GitHub ]# File 'lib/typeprof/lsp.rb', line 96
def lines @text.lines end
#locate_arg_index_in_signature_help(node, loc, sig_help) (private)
[ GitHub ]# File 'lib/typeprof/lsp.rb', line 184
private 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
#new_code_completion_session(row, start_offset, end_offset)
[ GitHub ]# File '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
[ GitHub ]# File '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)
[ GitHub ]# File '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_request("workspace/codeLens/refresh") @server.send_notification( "textDocument/publishDiagnostics", { uri: @uri, version: version, diagnostics: diagnostics[@uri] || [], } ) end
#push_analysis_queue(&work)
[ GitHub ]# File 'lib/typeprof/lsp.rb', line 293
def push_analysis_queue(&work) @analysis_queue.push(work) end
#signature_help(loc, trigger_kind)
[ GitHub ]# File '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