Class: Ibex::LSP::SymbolIndex

Inherits:
Object
  • Object
show all
Defined in:
lib/ibex/lsp/symbol_index.rb

Overview

Answers source navigation and guarded workspace rename operations.

Constant Summary collapse

IDENTIFIER =

: Regexp

/\A[A-Za-z_][A-Za-z0-9_]*\z/
RESERVED =
%w[
  class fragment include token prechigh preclow left right nonassoc options expect start
  recover sync on_error_reduce convert display type pragma extended rule end
  separated_list separated_nonempty_list
].freeze

Instance Method Summary collapse

Constructor Details

#initialize(store, path) ⇒ SymbolIndex

Returns a new instance of SymbolIndex.



15
16
17
18
19
20
21
22
23
24
# File 'lib/ibex/lsp/symbol_index.rb', line 15

def initialize(store, path)
  @store = store
  @path = path
  files = store.files_for(path)
  @occurrences, @documents = SymbolIndexBuilder.new(store, files).build
  @snapshot_state = files.to_h do |file|
    snapshot = store.snapshot_for(file)
    [file, snapshot && [snapshot.fetch(:version), snapshot.fetch(:source)]]
  end #: Hash[String, Array[untyped]?]
end

Instance Method Details

#definition(path, position) ⇒ Object



27
28
29
30
31
32
33
# File 'lib/ibex/lsp/symbol_index.rb', line 27

def definition(path, position)
  occurrence = occurrence_at(path, position)
  return [] unless occurrence
  return [include_target_location(occurrence)] if occurrence.kind == :include

  matching(occurrence.key, role: :definition).map { |entry| location(entry) }
end

#hover(path, position) ⇒ Object



82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/ibex/lsp/symbol_index.rb', line 82

def hover(path, position)
  occurrence = occurrence_at(path, position)
  return unless occurrence

  value = hover_value(occurrence)
  return unless value

  {
    "contents" => { "kind" => "markdown", "value" => value },
    "range" => range(occurrence)
  }
end

#prepare_rename(path, position) ⇒ Object



47
48
49
50
# File 'lib/ibex/lsp/symbol_index.rb', line 47

def prepare_rename(path, position)
  occurrence = renameable_occurrence(path, position)
  { "range" => range(occurrence), "placeholder" => occurrence.name }
end

#references(path, position, include_declaration: false) ⇒ Object



37
38
39
40
41
42
43
44
# File 'lib/ibex/lsp/symbol_index.rb', line 37

def references(path, position, include_declaration: false)
  occurrence = occurrence_at(path, position)
  return [] unless occurrence

  entries = matching(occurrence.key)
  entries = entries.reject { |entry| entry.role == :definition } unless include_declaration
  entries.map { |entry| location(entry) }
end

#rename(path, position, new_name) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/ibex/lsp/symbol_index.rb', line 53

def rename(path, position, new_name)
  ensure_fresh!
  occurrence = renameable_occurrence(path, position)
  validate_new_name(occurrence, new_name)
  entries = matching(occurrence.key)
  validate_unshared(entries)
  validate_nonoverlapping(entries)
  changes = entries.group_by(&:path).transform_values do |path_entries|
    path_entries.sort_by { |entry| entry.span.start_byte }.map do |entry|
      { "range" => range(entry), "newText" => new_name }
    end
  end
  replacements = changed_sources(entries, new_name)
  unless @store.valid_replacements?(replacements)
    raise ProtocolError.new("rename would make the affected grammar closure invalid", code: -32_602)
  end

  document_changes = changes.sort.map do |changed_path, edits|
    snapshot = @store.snapshot_for(changed_path)
    version = snapshot&.fetch(:open) ? snapshot.fetch(:version) : nil
    {
      "textDocument" => { "uri" => @store.uri_for(changed_path), "version" => version },
      "edits" => edits
    }
  end
  { "documentChanges" => document_changes }
end