In Files

  • typeprof-0.21.1/lib/typeprof/lsp.rb

Class/Module Index [+]

Quicksearch

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.1/lib/typeprof/lsp.rb, line 64
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.1/lib/typeprof/lsp.rb, line 275
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.1/lib/typeprof/lsp.rb, line 98
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.1/lib/typeprof/lsp.rb, line 159
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.1/lib/typeprof/lsp.rb, line 94
def lines
  @text.lines
end
            
new_code_completion_session(row, start_offset, end_offset) click to toggle source
 
               # File typeprof-0.21.1/lib/typeprof/lsp.rb, line 141
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.1/lib/typeprof/lsp.rb, line 295
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.1/lib/typeprof/lsp.rb, line 313
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) click to toggle source
 
               # File typeprof-0.21.1/lib/typeprof/lsp.rb, line 291
def push_analysis_queue(&work)
  @analysis_queue.push(work)
end
            
signature_help(loc, trigger_kind) click to toggle source
 
               # File typeprof-0.21.1/lib/typeprof/lsp.rb, line 248
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