Module: Jade::LSP::Converters
Constant Summary collapse
- SEVERITY =
{ error: 1, warning: 2, info: 3, hint: 4 }.freeze
- SYMBOL_KIND =
{ function: 12, enum: 10, enum_member: 22, struct: 23, interface: 11, }.freeze
- HOVERABLE_SYMBOLS =
[ Jade::Symbol::Function, Jade::Symbol::StdlibFunction, Jade::Symbol::InteropFunction, Jade::Symbol::InterfaceFunction, Jade::Symbol::Constructor, Jade::Symbol::Variant, Jade::Symbol::Union, Jade::Symbol::Struct, ].freeze
- COMPLETION_KIND_SNIPPET =
LSP CompletionItemKind values we use.
15- INSERT_FORMAT_SNIPPET =
LSP InsertTextFormat: 1 = PlainText, 2 = Snippet (with tab stops).
2- INLAY_HINT_TYPE =
InlayHintKind: 1 = Type, 2 = Parameter
1- INLAY_HINT_PARAMETER =
2
Instance Method Summary collapse
- #completion_items ⇒ Object
- #definition_for_path(path, registry, entry, source_root) ⇒ Object
- #diagnostic_to_lsp(diagnostic) ⇒ Object
-
#format_edits(text) ⇒ Object
Re-runs the parse + format pipeline on the buffer text.
- #hover_for_path(path, registry, entry) ⇒ Object
- #inlay_hints_for(entry, range_offsets) ⇒ Object
- #lsp_uri(relative_path, source_root) ⇒ Object
-
#offset_to_position(source, offset) ⇒ Object
Byte offsets, not UTF-16 code units.
- #position_to_offset(source, line, character) ⇒ Object
- #prepare_rename_for_path(path, registry, entry, offset) ⇒ Object
- #references_for_path(path, registry, entry, source_root, include_declaration:) ⇒ Object
- #relative_path(uri, source_root) ⇒ Object
- #rename_for_path(path, registry, entry, source_root, new_name) ⇒ Object
- #span_to_range(source, span) ⇒ Object
- #to_document_symbol(node, source) ⇒ Object
- #whole_document_edit(source, new_text) ⇒ Object
Instance Method Details
#completion_items ⇒ Object
68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/jade/lsp/converters.rb', line 68 def completion_items Snippets::ALL.map do |snippet| { label: snippet.label, kind: COMPLETION_KIND_SNIPPET, detail: snippet.detail, insertText: snippet.body, insertTextFormat: INSERT_FORMAT_SNIPPET, } end end |
#definition_for_path(path, registry, entry, source_root) ⇒ Object
53 54 55 56 |
# File 'lib/jade/lsp/converters.rb', line 53 def definition_for_path(path, registry, entry, source_root) innermost_resolved(path, registry, entry) .then { definition_location(it, registry, entry, source_root) } end |
#diagnostic_to_lsp(diagnostic) ⇒ Object
175 176 177 178 179 180 181 182 |
# File 'lib/jade/lsp/converters.rb', line 175 def diagnostic_to_lsp(diagnostic) { range: diagnostic.primary.then { span_to_range(it.source, it.span) }, severity: SEVERITY.fetch(diagnostic.severity, 3), source: 'jade', message: (diagnostic), } end |
#format_edits(text) ⇒ Object
Re-runs the parse + format pipeline on the buffer text. Returns nil if parsing fails (don’t overwrite the user’s broken buffer with garbage), an empty array if the text is already formatted, or a single whole-document TextEdit otherwise.
94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/jade/lsp/converters.rb', line 94 def format_edits(text) source = Jade::Source.new(uri: 'buffer', text:) case Jade::Parsing.parse(Jade::Lexer.tokenize(source), source:) in Jade::Ok([ast, comments]) formatted = Jade::Formatter.format(ast, comments:, source:) + "\n" formatted == text ? [] : [whole_document_edit(source, formatted)] in Jade::Err nil end end |
#hover_for_path(path, registry, entry) ⇒ Object
58 59 60 61 62 63 64 65 66 |
# File 'lib/jade/lsp/converters.rb', line 58 def hover_for_path(path, registry, entry) path .reverse .lazy .filter_map { hover_for_node(it, registry, entry) } .first rescue StandardError nil end |
#inlay_hints_for(entry, range_offsets) ⇒ Object
84 85 86 87 88 |
# File 'lib/jade/lsp/converters.rb', line 84 def inlay_hints_for(entry, range_offsets) return [] unless entry.env collect_inlay_hints(entry.ast, entry, range_offsets) end |
#lsp_uri(relative_path, source_root) ⇒ Object
193 194 195 196 197 198 |
# File 'lib/jade/lsp/converters.rb', line 193 def lsp_uri(relative_path, source_root) File .(File.join(source_root, relative_path)) .then { URI::DEFAULT_PARSER.escape(it) } .then { "file://#{it}" } end |
#offset_to_position(source, offset) ⇒ Object
Byte offsets, not UTF-16 code units. Matches the ‘utf-8` position encoding negotiated at initialize; under the default `utf-16`, columns drift right by N for each multi-byte char on the line.
11 12 13 14 |
# File 'lib/jade/lsp/converters.rb', line 11 def offset_to_position(source, offset) line = source.line_starts.rindex { it <= offset } || 0 { line:, character: offset - source.line_starts[line] } end |
#position_to_offset(source, line, character) ⇒ Object
16 17 18 |
# File 'lib/jade/lsp/converters.rb', line 16 def position_to_offset(source, line, character) source.line_starts[line] + character end |
#prepare_rename_for_path(path, registry, entry, offset) ⇒ Object
129 130 131 132 133 134 135 136 137 138 |
# File 'lib/jade/lsp/converters.rb', line 129 def prepare_rename_for_path(path, registry, entry, offset) node = innermost_resolvable_node(path, registry, entry) return nil unless node symbol = resolve_symbol(node, registry, entry) return nil unless renameable?(symbol, registry) identifier_span(node, symbol, entry.source) .then { it.cover?(offset) ? span_to_range(entry.source, it) : nil } end |
#references_for_path(path, registry, entry, source_root, include_declaration:) ⇒ Object
117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/jade/lsp/converters.rb', line 117 def references_for_path( path, registry, entry, source_root, include_declaration: ) symbol = innermost_resolved(path, registry, entry) return nil unless symbol refs = usage_locations(symbol, registry, source_root) return refs unless include_declaration refs + declaration_locations(symbol, registry, entry, source_root) end |
#relative_path(uri, source_root) ⇒ Object
184 185 186 187 188 189 190 191 |
# File 'lib/jade/lsp/converters.rb', line 184 def relative_path(uri, source_root) uri .sub(%r{\Afile://}, '') .then { URI::DEFAULT_PARSER.unescape(it) } .then { Pathname.new(it) } .then { it.relative_path_from(Pathname.new(source_root)) } .then { it.to_s } end |
#rename_for_path(path, registry, entry, source_root, new_name) ⇒ Object
140 141 142 143 144 145 146 147 148 |
# File 'lib/jade/lsp/converters.rb', line 140 def rename_for_path(path, registry, entry, source_root, new_name) symbol = innermost_resolved(path, registry, entry) return nil unless renameable?(symbol, registry) rename_edits(symbol, registry, entry, source_root) .group_by { it[:uri] } .transform_values { it.map { |e| { range: e[:range], newText: new_name } } } .then { { changes: it } } end |
#span_to_range(source, span) ⇒ Object
20 21 22 23 24 25 |
# File 'lib/jade/lsp/converters.rb', line 20 def span_to_range(source, span) { start: offset_to_position(source, span.begin), end: offset_to_position(source, span.end), } end |
#to_document_symbol(node, source) ⇒ Object
150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/jade/lsp/converters.rb', line 150 def to_document_symbol(node, source) case node in AST::FunctionDeclaration(name:, range:) document_symbol(name, :function, source, range) in AST::TypeDeclaration(name:, range:, variants:) document_symbol( name, :enum, source, range, children: variants.map { variant_symbol(it, source) }, ) in AST::StructDeclaration(name:, range:) document_symbol(name, :struct, source, range) in AST::InterfaceDeclaration(name:, range:, functions:) document_symbol( name, :interface, source, range, children: functions.map { interface_fn_symbol(it, source) }, ) else nil end end |
#whole_document_edit(source, new_text) ⇒ Object
106 107 108 109 110 111 112 113 114 115 |
# File 'lib/jade/lsp/converters.rb', line 106 def whole_document_edit(source, new_text) last_line = source.line_starts.size - 1 { range: { start: { line: 0, character: 0 }, end: { line: last_line, character: source.text.bytesize - source.line_starts[last_line] }, }, newText: new_text, } end |