Class: Ibex::LSP::ParserConfigurationAssistance

Inherits:
Object
  • Object
show all
Defined in:
lib/ibex/lsp/parser_configuration_assistance.rb,
sig/ibex/lsp/parser_configuration_assistance.rbs

Overview

Provides static editor assistance for the root parser declaration without evaluating grammar code.

Constant Summary collapse

SETTING_VALUES =

Signature:

  • Hash[String, Array[String]]

Returns:

  • (Hash[String, Array[String]])
Frontend::ParserConfigurationSupport::PARSER_SETTING_VALUES
SETTING_DOCUMENTATION =

Returns:

  • (Hash[String, String])
{
  "algorithm" => "`parser.algorithm` selects parser-table construction. Values: `slr`, `lalr`, `ielr`, `lr1`.",
  "entries" => "`parser.entries` selects shared or isolated construction for multiple start symbols. " \
               "Values: `shared`, `isolated`.",
  "cst_trivia" => "`cst.trivia` selects CST trivia ownership. Values: `leading`, `balanced`, `drop`; " \
                  "requires `pragma cst`."
}.freeze
VALUE_DOCUMENTATION =

Signature:

  • Hash[String, String]

Returns:

  • (Hash[String, String])
{
  "slr" => "`slr` — SLR parser-table construction.",
  "lalr" => "`lalr` — LALR parser-table construction.",
  "ielr" => "`ielr` — IELR parser-table construction with canonical LR(1) verification support.",
  "lr1" => "`lr1` — canonical LR(1) parser-table construction.",
  "shared" => "`shared` — construct multiple entries in one shared automaton.",
  "isolated" => "`isolated` — construct each of multiple entries independently.",
  "leading" => "`leading` — attach leading trivia to the following CST node.",
  "balanced" => "`balanced` — attach trivia between the surrounding CST nodes.",
  "drop" => "`drop` — omit CST trivia; location and incremental APIs are unavailable."
}.freeze

Instance Method Summary collapse

Constructor Details

#initialize(store, path) ⇒ ParserConfigurationAssistance

Returns a new instance of ParserConfigurationAssistance.

RBS:

  • (DocumentStore store, String path) -> void

Parameters:



28
29
30
31
# File 'lib/ibex/lsp/parser_configuration_assistance.rb', line 28

def initialize(store, path)
  @store = store
  @path = path
end

Instance Method Details

#completion(position) ⇒ lsp_object

RBS:

  • (lsp_object position) -> lsp_object

Parameters:

  • position (lsp_object)

Returns:

  • (lsp_object)


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/ibex/lsp/parser_configuration_assistance.rb', line 34

def completion(position)
  source = source!
  codec = PositionCodec.new(source)
  offset = codec.byte_offset(position)
  line_start = line_start(source, offset)
  return completion_list([]) unless parser_block_open?(source.byteslice(0, line_start) || "")

  prefix = source.byteslice(line_start, offset - line_start) || ""
  value_match = prefix.match(/\A\s*(algorithm|entries|cst_trivia)\s+([A-Za-z_]*)\z/)
  if value_match
    setting = value_match[1] || raise("parser setting capture is missing")
    return completion_list(value_items(setting))
  end
  return completion_list(setting_items) if prefix.match?(/\A\s*[A-Za-z_]*\z/)

  completion_list([])
end

#completion_item(label, kind, documentation) ⇒ lsp_object

RBS:

  • (String label, Integer kind, String documentation) -> lsp_object

Parameters:

  • label (String)
  • kind (Integer)
  • documentation (String)

Returns:

  • (lsp_object)


125
126
127
# File 'lib/ibex/lsp/parser_configuration_assistance.rb', line 125

def completion_item(label, kind, documentation)
  { "label" => label, "kind" => kind, "documentation" => { "kind" => "markdown", "value" => documentation } }
end

#completion_list(items) ⇒ lsp_object

RBS:

  • (Array[lsp_object] items) -> lsp_object

Parameters:

  • items (Array[lsp_object])

Returns:

  • (lsp_object)


130
131
132
# File 'lib/ibex/lsp/parser_configuration_assistance.rb', line 130

def completion_list(items)
  { "isIncomplete" => false, "items" => items }
end

#hover(position) ⇒ lsp_object?

RBS:

  • (lsp_object position) -> lsp_object?

Parameters:

  • position (lsp_object)

Returns:

  • (lsp_object, nil)


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

def hover(position)
  source = source!
  codec = PositionCodec.new(source)
  offset = codec.byte_offset(position)
  start = line_start(source, offset)
  return unless parser_block_open?(source.byteslice(0, start) || "")

  finish = source.index("\n", start) || source.bytesize
  line = source.byteslice(start, finish - start) || ""
  match = line.match(
    /\A\s*(algorithm|entries|cst_trivia)\s+(slr|lalr|ielr|lr1|shared|isolated|leading|balanced|drop)\s*(?:#.*)?\z/
  )
  return unless match

  token, token_start, token_end = hovered_token(match, line, start, offset)
  return unless token

  documentation = SETTING_DOCUMENTATION[token] || VALUE_DOCUMENTATION[token]
  {
    "contents" => { "kind" => "markdown", "value" => documentation },
    "range" => { "start" => codec.position(token_start), "end" => codec.position(token_end) }
  }
end

#hovered_token(match, line, line_start, offset) ⇒ [ String?, Integer, Integer ]

RBS:

  • (MatchData match, String line, Integer line_start, Integer offset) -> [String?, Integer, Integer]

Parameters:

  • match (MatchData)
  • line (String)
  • line_start (Integer)
  • offset (Integer)

Returns:

  • ([ String?, Integer, Integer ])


135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/ibex/lsp/parser_configuration_assistance.rb', line 135

def hovered_token(match, line, line_start, offset)
  [1, 2].each do |capture|
    token = match[capture]
    next unless token

    before = line[0...match.begin(capture)] || ""
    start = line_start + before.bytesize
    finish = start + token.bytesize
    return [token, start, finish] if offset.between?(start, finish - 1)
  end
  [nil, offset, offset]
end

#line_start(source, offset) ⇒ Integer

RBS:

  • (String source, Integer offset) -> Integer

Parameters:

  • source (String)
  • offset (Integer)

Returns:

  • (Integer)


88
89
90
91
# File 'lib/ibex/lsp/parser_configuration_assistance.rb', line 88

def line_start(source, offset)
  newline = offset.positive? ? source.rindex("\n", offset - 1) : nil
  newline ? newline + 1 : 0
end

#parser_block_open?(source) ⇒ Boolean

RBS:

  • (String source) -> bool

Parameters:

  • source (String)

Returns:

  • (Boolean)


94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/ibex/lsp/parser_configuration_assistance.rb', line 94

def parser_block_open?(source)
  root = false
  open = false
  source.each_line do |line|
    content = line.sub(/#.*/, "").strip
    next if content.empty?

    root = true if content.match?(/\Aclass\b/)
    root = false if content.match?(/\Afragment\b/)
    open = true if root && content == "parser"
    open = false if open && content == "end"
    return false if content.match?(/\Arule\b/)
  end
  root && open
end

#setting_itemsArray[lsp_object]

RBS:

  • () -> Array[lsp_object]

Returns:

  • (Array[lsp_object])


111
112
113
114
115
# File 'lib/ibex/lsp/parser_configuration_assistance.rb', line 111

def setting_items
  SETTING_VALUES.keys.map do |name|
    completion_item(name, 10, SETTING_DOCUMENTATION.fetch(name))
  end
end

#source!String

RBS:

  • () -> String

Returns:

  • (String)


80
81
82
83
84
85
# File 'lib/ibex/lsp/parser_configuration_assistance.rb', line 80

def source!
  snapshot = @store.snapshot_for(@path)
  raise ArgumentError, "document is unavailable" unless snapshot

  snapshot.fetch(:source)
end

#value_items(setting) ⇒ Array[lsp_object]

RBS:

  • (String setting) -> Array[lsp_object]

Parameters:

  • setting (String)

Returns:

  • (Array[lsp_object])


118
119
120
121
122
# File 'lib/ibex/lsp/parser_configuration_assistance.rb', line 118

def value_items(setting)
  SETTING_VALUES.fetch(setting).map do |name|
    completion_item(name, 12, VALUE_DOCUMENTATION.fetch(name))
  end
end