1use line_index::LineIndex;
2use log::info;
3use rowan::{TextRange, TextSize};
4use salsa::Setter;
5use serde::{Deserialize, Serialize};
6use squawk_ide::builtins::builtins_file;
7use squawk_ide::db::{self, Database, File};
8use squawk_ide::folding_ranges::{FoldKind, folding_ranges};
9use squawk_ide::semantic_tokens::{SemanticTokenType, semantic_tokens};
10use squawk_syntax::ast::AstNode;
11use wasm_bindgen::prelude::*;
12use web_sys::js_sys::Error;
13
14const SEMANTIC_TOKEN_TYPES: &[&str] = &[
15 "comment",
16 "function",
17 "keyword",
18 "namespace",
19 "number",
20 "operator",
21 "parameter",
22 "property",
23 "string",
24 "struct",
25 "type",
26 "variable",
27];
28
29const SEMANTIC_TOKEN_MODIFIERS: &[&str] = &["declaration", "definition", "readonly"];
30
31fn semantic_token_type_name(ty: SemanticTokenType) -> &'static str {
32 match ty {
33 SemanticTokenType::Bool | SemanticTokenType::Keyword => "keyword",
34 SemanticTokenType::Column => "variable",
35 SemanticTokenType::Comment => "comment",
36 SemanticTokenType::Function => "function",
37 SemanticTokenType::Name | SemanticTokenType::NameRef => "variable",
38 SemanticTokenType::Number => "number",
39 SemanticTokenType::Operator | SemanticTokenType::Punctuation => "operator",
40 SemanticTokenType::Parameter | SemanticTokenType::PositionalParam => "parameter",
41 SemanticTokenType::Schema => "namespace",
42 SemanticTokenType::String => "string",
43 SemanticTokenType::PropertyGraph | SemanticTokenType::Table => "struct",
44 SemanticTokenType::Type => "type",
45 }
46}
47
48fn semantic_token_type_index(ty: SemanticTokenType) -> u32 {
49 let name = semantic_token_type_name(ty);
50 SEMANTIC_TOKEN_TYPES
51 .iter()
52 .position(|it| *it == name)
53 .unwrap() as u32
54}
55
56struct EncodedSemanticToken {
57 line: u32,
58 start: u32,
59 length: u32,
60 token_type: SemanticTokenType,
61 modifiers: u32,
62}
63
64struct SemanticTokenEncoder {
65 data: Vec<u32>,
66 prev_line: u32,
67 prev_start: u32,
68}
69
70impl SemanticTokenEncoder {
71 fn with_capacity(token_count: usize) -> Self {
72 Self {
73 data: Vec::with_capacity(token_count * 5),
74 prev_line: 0,
75 prev_start: 0,
76 }
77 }
78
79 fn push(&mut self, token: EncodedSemanticToken) {
80 let delta_line = token.line - self.prev_line;
81 let delta_start = if delta_line == 0 {
82 token.start - self.prev_start
83 } else {
84 token.start
85 };
86
87 self.data.extend_from_slice(&[
88 delta_line,
89 delta_start,
90 token.length,
91 semantic_token_type_index(token.token_type),
92 token.modifiers,
93 ]);
94
95 self.prev_line = token.line;
96 self.prev_start = token.start;
97 }
98
99 fn finish(self) -> Vec<u32> {
100 self.data
101 }
102}
103
104#[wasm_bindgen(start)]
105pub fn run() {
106 use log::Level;
107
108 #[cfg(feature = "console_error_panic_hook")]
115 console_error_panic_hook::set_once();
116 console_log::init_with_level(Level::Debug).expect("Initializing logger went wrong.");
117 info!("init!");
118}
119
120#[wasm_bindgen]
121pub struct SquawkDatabase {
122 db: Database,
123 file: Option<File>,
124}
125
126#[wasm_bindgen]
127#[allow(clippy::new_without_default)]
128impl SquawkDatabase {
129 #[wasm_bindgen(constructor)]
130 pub fn new() -> SquawkDatabase {
131 SquawkDatabase {
132 db: Database::default(),
133 file: None,
134 }
135 }
136
137 pub fn open_file(&mut self, content: String) {
138 let file = File::new(&self.db, content.into());
139 self.file = Some(file);
140 }
141
142 pub fn update_file(&mut self, content: String) {
143 if let Some(file) = self.file {
144 file.set_content(&mut self.db).to(content.into());
145 }
146 }
147
148 fn file(&self) -> Result<File, Error> {
149 self.file
150 .ok_or_else(|| Error::new("No file open. Call open_file first."))
151 }
152
153 pub fn dump_cst(&self) -> Result<String, Error> {
154 let file = self.file()?;
155 let parse = db::parse(&self.db, file);
156 Ok(format!("{:#?}", parse.syntax_node()))
157 }
158
159 pub fn dump_tokens(&self) -> Result<String, Error> {
160 let file = self.file()?;
161 let content = file.content(&self.db);
162 let tokens = squawk_lexer::tokenize(content);
163 let mut start = 0;
164 let mut out = String::new();
165 for token in tokens {
166 let end = start + token.len;
167 let text = &content[start as usize..(end) as usize];
168 out += &format!("{:?}@{start}..{end} {:?}\n", token.kind, text);
169 start += token.len;
170 }
171 Ok(out)
172 }
173
174 pub fn lint(&self) -> Result<JsValue, Error> {
175 let file = self.file()?;
176 let content = file.content(&self.db);
177 let mut linter = squawk_linter::Linter::with_default_rules();
178 let parse = db::parse(&self.db, file);
179 let parse_errors = parse.errors();
180
181 let line_index = db::line_index(&self.db, file);
182
183 let parse_errors = parse_errors.iter().map(|x| {
184 let range_start = x.range().start();
185 let range_end = x.range().end();
186 let start = line_index.line_col(range_start);
187 let end = line_index.line_col(range_end);
188 let start = line_index
189 .to_wide(line_index::WideEncoding::Utf16, start)
190 .unwrap();
191 let end = line_index
192 .to_wide(line_index::WideEncoding::Utf16, end)
193 .unwrap();
194 LintError {
195 severity: Severity::Error,
196 code: "syntax-error".to_string(),
197 message: x.message().to_string(),
198 start_line_number: start.line,
199 start_column: start.col,
200 end_line_number: end.line,
201 end_column: end.col,
202 range_start: range_start.into(),
203 range_end: range_end.into(),
204 messages: vec![],
205 fix: None,
206 }
207 });
208
209 let lint_errors = linter.lint(&parse, content);
210 let errors = lint_errors.into_iter().map(|x| {
211 let start = line_index.line_col(x.text_range.start());
212 let end = line_index.line_col(x.text_range.end());
213 let start = line_index
214 .to_wide(line_index::WideEncoding::Utf16, start)
215 .unwrap();
216 let end = line_index
217 .to_wide(line_index::WideEncoding::Utf16, end)
218 .unwrap();
219
220 let messages = x.help.into_iter().collect();
221
222 let fix = x.fix.map(|fix| {
223 let edits = fix
224 .edits
225 .into_iter()
226 .map(|edit| {
227 let start_pos = line_index.line_col(edit.text_range.start());
228 let end_pos = line_index.line_col(edit.text_range.end());
229 let start_wide = line_index
230 .to_wide(line_index::WideEncoding::Utf16, start_pos)
231 .unwrap();
232 let end_wide = line_index
233 .to_wide(line_index::WideEncoding::Utf16, end_pos)
234 .unwrap();
235
236 TextEdit {
237 start_line_number: start_wide.line,
238 start_column: start_wide.col,
239 end_line_number: end_wide.line,
240 end_column: end_wide.col,
241 text: edit.text.unwrap_or_default(),
242 }
243 })
244 .collect();
245
246 Fix {
247 title: fix.title,
248 edits,
249 }
250 });
251
252 LintError {
253 code: x.code.to_string(),
254 range_start: x.text_range.start().into(),
255 range_end: x.text_range.end().into(),
256 message: x.message.clone(),
257 messages,
258 severity: Severity::Warning,
259 start_line_number: start.line,
260 start_column: start.col,
261 end_line_number: end.line,
262 end_column: end.col,
263 fix,
264 }
265 });
266
267 let mut errors_to_dump = errors.chain(parse_errors).collect::<Vec<_>>();
268 errors_to_dump.sort_by_key(|k| (k.start_line_number, k.start_column));
269
270 serde_wasm_bindgen::to_value(&errors_to_dump).map_err(into_error)
271 }
272
273 pub fn goto_definition(&self, line: u32, col: u32) -> Result<JsValue, Error> {
274 let file = self.file()?;
275 let current_line_index = db::line_index(&self.db, file);
276 let offset = position_to_offset(¤t_line_index, line, col)?;
277 let result = squawk_ide::goto_definition::goto_definition(&self.db, file, offset);
278
279 let response: Vec<LocationRange> = result
280 .into_iter()
281 .map(|loc| {
282 let range = loc.range;
283 let file = file_string(&self.db, loc.file);
284 let line_index = db::line_index(&self.db, loc.file);
285 let start = line_index.line_col(range.start());
286 let end = line_index.line_col(range.end());
287 let start_wide = line_index
288 .to_wide(line_index::WideEncoding::Utf16, start)
289 .unwrap();
290 let end_wide = line_index
291 .to_wide(line_index::WideEncoding::Utf16, end)
292 .unwrap();
293
294 LocationRange {
295 file: file.to_string(),
296 start_line: start_wide.line,
297 start_column: start_wide.col,
298 end_line: end_wide.line,
299 end_column: end_wide.col,
300 }
301 })
302 .collect();
303
304 serde_wasm_bindgen::to_value(&response).map_err(into_error)
305 }
306
307 pub fn hover(&self, line: u32, col: u32) -> Result<JsValue, Error> {
308 let file = self.file()?;
309 let line_index = db::line_index(&self.db, file);
310 let offset = position_to_offset(&line_index, line, col)?;
311 let result = squawk_ide::hover::hover(&self.db, file, offset);
312
313 let converted = result.map(|hover| WasmHover {
314 snippet: hover.snippet,
315 comment: hover.comment,
316 });
317
318 serde_wasm_bindgen::to_value(&converted).map_err(into_error)
319 }
320
321 pub fn find_references(&self, line: u32, col: u32) -> Result<JsValue, Error> {
322 let file = self.file()?;
323 let line_index = db::line_index(&self.db, file);
324 let offset = position_to_offset(&line_index, line, col)?;
325 let references = squawk_ide::find_references::find_references(&self.db, file, offset);
326 let locations: Vec<LocationRange> = references
327 .iter()
328 .map(|loc| {
329 let file = file_string(&self.db, loc.file);
330 let line_index = db::line_index(&self.db, loc.file);
331 let start = line_index.line_col(loc.range.start());
332 let end = line_index.line_col(loc.range.end());
333 let start_wide = line_index
334 .to_wide(line_index::WideEncoding::Utf16, start)
335 .unwrap();
336 let end_wide = line_index
337 .to_wide(line_index::WideEncoding::Utf16, end)
338 .unwrap();
339
340 LocationRange {
341 file: file.to_string(),
342 start_line: start_wide.line,
343 start_column: start_wide.col,
344 end_line: end_wide.line,
345 end_column: end_wide.col,
346 }
347 })
348 .collect();
349
350 serde_wasm_bindgen::to_value(&locations).map_err(into_error)
351 }
352
353 pub fn document_symbols(&self) -> Result<JsValue, Error> {
354 let file = self.file()?;
355 let line_index = db::line_index(&self.db, file);
356 let symbols = squawk_ide::document_symbols::document_symbols(&self.db, file);
357
358 let converted: Vec<WasmDocumentSymbol> = symbols
359 .into_iter()
360 .map(|s| convert_document_symbol(&line_index, s))
361 .collect();
362
363 serde_wasm_bindgen::to_value(&converted).map_err(into_error)
364 }
365
366 pub fn code_actions(&self, line: u32, col: u32) -> Result<JsValue, Error> {
367 let file = self.file()?;
368 let line_index = db::line_index(&self.db, file);
369 let offset = position_to_offset(&line_index, line, col)?;
370 let actions = squawk_ide::code_actions::code_actions(&self.db, file, offset);
371
372 let converted = actions.map(|actions| {
373 actions
374 .into_iter()
375 .map(|action| {
376 let edits = action
377 .edits
378 .into_iter()
379 .map(|edit| {
380 let start_pos = line_index.line_col(edit.text_range.start());
381 let end_pos = line_index.line_col(edit.text_range.end());
382 let start_wide = line_index
383 .to_wide(line_index::WideEncoding::Utf16, start_pos)
384 .unwrap();
385 let end_wide = line_index
386 .to_wide(line_index::WideEncoding::Utf16, end_pos)
387 .unwrap();
388
389 TextEdit {
390 start_line_number: start_wide.line,
391 start_column: start_wide.col,
392 end_line_number: end_wide.line,
393 end_column: end_wide.col,
394 text: edit.text.unwrap_or_default(),
395 }
396 })
397 .collect();
398
399 WasmCodeAction {
400 title: action.title,
401 edits,
402 kind: match action.kind {
403 squawk_ide::code_actions::ActionKind::QuickFix => "quickfix",
404 squawk_ide::code_actions::ActionKind::RefactorRewrite => {
405 "refactor.rewrite"
406 }
407 }
408 .to_string(),
409 }
410 })
411 .collect::<Vec<_>>()
412 });
413
414 serde_wasm_bindgen::to_value(&converted).map_err(into_error)
415 }
416
417 pub fn inlay_hints(&self) -> Result<JsValue, Error> {
418 let file = self.file()?;
419 let line_index = db::line_index(&self.db, file);
420 let hints = squawk_ide::inlay_hints::inlay_hints(&self.db, file);
421
422 let converted: Vec<WasmInlayHint> = hints
423 .into_iter()
424 .map(|hint| {
425 let position = line_index.line_col(hint.position);
426 let position_wide = line_index
427 .to_wide(line_index::WideEncoding::Utf16, position)
428 .unwrap();
429
430 WasmInlayHint {
431 line: position_wide.line,
432 column: position_wide.col,
433 label: hint.label,
434 kind: match hint.kind {
435 squawk_ide::inlay_hints::InlayHintKind::Type => "type",
436 squawk_ide::inlay_hints::InlayHintKind::Parameter => "parameter",
437 }
438 .to_string(),
439 }
440 })
441 .collect();
442
443 serde_wasm_bindgen::to_value(&converted).map_err(into_error)
444 }
445
446 pub fn folding_ranges(&self) -> Result<JsValue, Error> {
447 let file = self.file()?;
448 let line_index = db::line_index(&self.db, file);
449 let folds = folding_ranges(&self.db, file);
450
451 let converted: Vec<WasmFoldingRange> = folds
452 .into_iter()
453 .map(|fold| {
454 let start = line_index.line_col(fold.range.start());
455 let end = line_index.line_col(fold.range.end());
456 let start_wide = line_index
457 .to_wide(line_index::WideEncoding::Utf16, start)
458 .unwrap();
459 let end_wide = line_index
460 .to_wide(line_index::WideEncoding::Utf16, end)
461 .unwrap();
462
463 WasmFoldingRange {
464 start_line: start_wide.line,
465 end_line: end_wide.line,
466 kind: match fold.kind {
467 FoldKind::Comment => "comment",
468 _ => "region",
469 }
470 .to_string(),
471 }
472 })
473 .collect();
474
475 serde_wasm_bindgen::to_value(&converted).map_err(into_error)
476 }
477
478 pub fn selection_ranges(&self, positions: Vec<JsValue>) -> Result<JsValue, Error> {
479 let file = self.file()?;
480 let parse = db::parse(&self.db, file);
481 let line_index = db::line_index(&self.db, file);
482 let tree = parse.tree();
483 let root = tree.syntax();
484
485 let mut results: Vec<Vec<WasmSelectionRange>> = vec![];
486
487 for pos in positions {
488 let pos: Position = serde_wasm_bindgen::from_value(pos).map_err(into_error)?;
489 let offset = position_to_offset(&line_index, pos.line, pos.column)?;
490
491 let mut ranges = vec![];
492 let mut range = TextRange::new(offset, offset);
493
494 for _ in 0..20 {
495 let next = squawk_ide::expand_selection::extend_selection(root, range);
496 if next == range {
497 break;
498 }
499
500 let start = line_index.line_col(next.start());
501 let end = line_index.line_col(next.end());
502 let start_wide = line_index
503 .to_wide(line_index::WideEncoding::Utf16, start)
504 .unwrap();
505 let end_wide = line_index
506 .to_wide(line_index::WideEncoding::Utf16, end)
507 .unwrap();
508
509 ranges.push(WasmSelectionRange {
510 start_line: start_wide.line,
511 start_column: start_wide.col,
512 end_line: end_wide.line,
513 end_column: end_wide.col,
514 });
515
516 range = next;
517 }
518
519 results.push(ranges);
520 }
521
522 serde_wasm_bindgen::to_value(&results).map_err(into_error)
523 }
524
525 pub fn semantic_tokens(&self) -> Result<Vec<u32>, Error> {
526 let file = self.file()?;
527 let line_index = db::line_index(&self.db, file);
528 let content = file.content(&self.db);
529 let tokens = semantic_tokens(&self.db, file, None);
530
531 let mut encoder = SemanticTokenEncoder::with_capacity(tokens.len());
532
533 for token in &tokens {
535 for mut text_range in line_index.lines(token.range) {
539 if content[text_range].ends_with('\n') {
540 text_range =
541 TextRange::new(text_range.start(), text_range.end() - TextSize::of('\n'));
542 }
543 let start_lc = line_index.line_col(text_range.start());
544 let end_lc = line_index.line_col(text_range.end());
545 let start_wide = line_index
546 .to_wide(line_index::WideEncoding::Utf16, start_lc)
547 .unwrap();
548 let end_wide = line_index
549 .to_wide(line_index::WideEncoding::Utf16, end_lc)
550 .unwrap();
551
552 encoder.push(EncodedSemanticToken {
553 line: start_wide.line,
554 start: start_wide.col,
555 length: end_wide.col - start_wide.col,
556 token_type: token.token_type,
557 modifiers: 0,
559 });
560 }
561 }
562
563 Ok(encoder.finish())
564 }
565
566 pub fn semantic_tokens_legend() -> Result<JsValue, Error> {
567 let legend = SemanticTokensLegend {
568 token_types: SEMANTIC_TOKEN_TYPES.to_vec(),
569 token_modifiers: SEMANTIC_TOKEN_MODIFIERS.to_vec(),
570 };
571 serde_wasm_bindgen::to_value(&legend).map_err(into_error)
572 }
573
574 pub fn completion(&self, line: u32, col: u32) -> Result<JsValue, Error> {
575 let file = self.file()?;
576 let line_index = db::line_index(&self.db, file);
577 let offset = position_to_offset(&line_index, line, col)?;
578 let items = squawk_ide::completion::completion(&self.db, file, offset);
579
580 let converted: Vec<WasmCompletionItem> = items
581 .into_iter()
582 .map(|item| WasmCompletionItem {
583 label: item.label,
584 kind: match item.kind {
585 squawk_ide::completion::CompletionItemKind::Keyword => "keyword",
586 squawk_ide::completion::CompletionItemKind::Table => "table",
587 squawk_ide::completion::CompletionItemKind::Column => "column",
588 squawk_ide::completion::CompletionItemKind::Function => "function",
589 squawk_ide::completion::CompletionItemKind::Schema => "schema",
590 squawk_ide::completion::CompletionItemKind::Type => "type",
591 squawk_ide::completion::CompletionItemKind::Snippet => "snippet",
592 squawk_ide::completion::CompletionItemKind::Operator => "operator",
593 }
594 .to_string(),
595 detail: item.detail,
596 insert_text: item.insert_text,
597 insert_text_format: item.insert_text_format.map(|fmt| {
598 match fmt {
599 squawk_ide::completion::CompletionInsertTextFormat::PlainText => {
600 "plainText"
601 }
602 squawk_ide::completion::CompletionInsertTextFormat::Snippet => "snippet",
603 }
604 .to_string()
605 }),
606 trigger_completion_after_insert: item.trigger_completion_after_insert,
607 })
608 .collect();
609
610 serde_wasm_bindgen::to_value(&converted).map_err(into_error)
611 }
612}
613
614fn file_string(db: &Database, file: File) -> &'static str {
615 if file == builtins_file(db) {
617 "builtins"
618 } else {
619 "current"
620 }
621}
622
623fn into_error<E: std::fmt::Display>(err: E) -> Error {
624 Error::new(&err.to_string())
625}
626
627fn position_to_offset(
628 line_index: &LineIndex,
629 line: u32,
630 col: u32,
631) -> Result<rowan::TextSize, Error> {
632 let wide_pos = line_index::WideLineCol { line, col };
633
634 let pos = line_index
635 .to_utf8(line_index::WideEncoding::Utf16, wide_pos)
636 .ok_or_else(|| Error::new("Invalid position"))?;
637
638 line_index
639 .offset(pos)
640 .ok_or_else(|| Error::new("Invalid position offset"))
641}
642
643fn convert_document_symbol(
644 line_index: &LineIndex,
645 symbol: squawk_ide::document_symbols::DocumentSymbol,
646) -> WasmDocumentSymbol {
647 let full_start = line_index.line_col(symbol.full_range.start());
648 let full_end = line_index.line_col(symbol.full_range.end());
649 let full_start_wide = line_index
650 .to_wide(line_index::WideEncoding::Utf16, full_start)
651 .unwrap();
652 let full_end_wide = line_index
653 .to_wide(line_index::WideEncoding::Utf16, full_end)
654 .unwrap();
655
656 let focus_start = line_index.line_col(symbol.focus_range.start());
657 let focus_end = line_index.line_col(symbol.focus_range.end());
658 let focus_start_wide = line_index
659 .to_wide(line_index::WideEncoding::Utf16, focus_start)
660 .unwrap();
661 let focus_end_wide = line_index
662 .to_wide(line_index::WideEncoding::Utf16, focus_end)
663 .unwrap();
664
665 WasmDocumentSymbol {
666 name: symbol.name,
667 detail: symbol.detail,
668 kind: match symbol.kind {
669 squawk_ide::document_symbols::DocumentSymbolKind::Schema => "schema",
670 squawk_ide::document_symbols::DocumentSymbolKind::Table => "table",
671 squawk_ide::document_symbols::DocumentSymbolKind::View => "view",
672 squawk_ide::document_symbols::DocumentSymbolKind::MaterializedView => {
673 "materialized_view"
674 }
675 squawk_ide::document_symbols::DocumentSymbolKind::Function => "function",
676 squawk_ide::document_symbols::DocumentSymbolKind::Aggregate => "aggregate",
677 squawk_ide::document_symbols::DocumentSymbolKind::Procedure => "procedure",
678 squawk_ide::document_symbols::DocumentSymbolKind::EventTrigger => "event_trigger",
679 squawk_ide::document_symbols::DocumentSymbolKind::Role => "role",
680 squawk_ide::document_symbols::DocumentSymbolKind::Policy => "policy",
681 squawk_ide::document_symbols::DocumentSymbolKind::PropertyGraph => "property_graph",
682 squawk_ide::document_symbols::DocumentSymbolKind::Type => "type",
683 squawk_ide::document_symbols::DocumentSymbolKind::Enum => "enum",
684 squawk_ide::document_symbols::DocumentSymbolKind::Index => "index",
685 squawk_ide::document_symbols::DocumentSymbolKind::Domain => "domain",
686 squawk_ide::document_symbols::DocumentSymbolKind::Sequence => "sequence",
687 squawk_ide::document_symbols::DocumentSymbolKind::Trigger => "trigger",
688 squawk_ide::document_symbols::DocumentSymbolKind::Tablespace => "tablespace",
689 squawk_ide::document_symbols::DocumentSymbolKind::Database => "database",
690 squawk_ide::document_symbols::DocumentSymbolKind::Server => "server",
691 squawk_ide::document_symbols::DocumentSymbolKind::Extension => "extension",
692 squawk_ide::document_symbols::DocumentSymbolKind::Column => "column",
693 squawk_ide::document_symbols::DocumentSymbolKind::Variant => "variant",
694 squawk_ide::document_symbols::DocumentSymbolKind::Cursor => "cursor",
695 squawk_ide::document_symbols::DocumentSymbolKind::PreparedStatement => {
696 "prepared_statement"
697 }
698 squawk_ide::document_symbols::DocumentSymbolKind::Channel => "channel",
699 }
700 .to_string(),
701 start_line: full_start_wide.line,
702 start_column: full_start_wide.col,
703 end_line: full_end_wide.line,
704 end_column: full_end_wide.col,
705 selection_start_line: focus_start_wide.line,
706 selection_start_column: focus_start_wide.col,
707 selection_end_line: focus_end_wide.line,
708 selection_end_column: focus_end_wide.col,
709 children: symbol
710 .children
711 .into_iter()
712 .map(|child| convert_document_symbol(line_index, child))
713 .collect(),
714 }
715}
716
717#[expect(unused)]
718#[derive(Serialize)]
719enum Severity {
720 Hint,
721 Info,
722 Warning,
723 Error,
724}
725
726#[derive(Serialize)]
727struct LintError {
728 severity: Severity,
729 code: String,
730 message: String,
731 start_line_number: u32,
732 start_column: u32,
733 end_line_number: u32,
734 end_column: u32,
735 range_start: usize,
736 range_end: usize,
737 messages: Vec<String>,
738 fix: Option<Fix>,
739}
740
741#[derive(Serialize)]
742struct Fix {
743 title: String,
744 edits: Vec<TextEdit>,
745}
746
747#[derive(Serialize)]
748struct TextEdit {
749 start_line_number: u32,
750 start_column: u32,
751 end_line_number: u32,
752 end_column: u32,
753 text: String,
754}
755
756#[derive(Serialize)]
757struct LocationRange {
758 file: String,
759 start_line: u32,
760 start_column: u32,
761 end_line: u32,
762 end_column: u32,
763}
764
765#[derive(Serialize)]
766struct WasmCodeAction {
767 title: String,
768 edits: Vec<TextEdit>,
769 kind: String,
770}
771
772#[derive(Serialize)]
773struct WasmHover {
774 snippet: String,
775 comment: Option<String>,
776}
777
778#[derive(Serialize)]
779struct WasmDocumentSymbol {
780 name: String,
781 detail: Option<String>,
782 kind: String,
783 start_line: u32,
784 start_column: u32,
785 end_line: u32,
786 end_column: u32,
787 selection_start_line: u32,
788 selection_start_column: u32,
789 selection_end_line: u32,
790 selection_end_column: u32,
791 children: Vec<WasmDocumentSymbol>,
792}
793
794#[derive(Serialize)]
795struct WasmInlayHint {
796 line: u32,
797 column: u32,
798 label: String,
799 kind: String,
800}
801
802#[derive(Serialize)]
803struct WasmFoldingRange {
804 start_line: u32,
805 end_line: u32,
806 kind: String,
807}
808
809#[derive(Serialize)]
810struct WasmSelectionRange {
811 start_line: u32,
812 start_column: u32,
813 end_line: u32,
814 end_column: u32,
815}
816
817#[derive(Serialize)]
818struct SemanticTokensLegend {
819 #[serde(rename = "tokenTypes")]
820 token_types: Vec<&'static str>,
821 #[serde(rename = "tokenModifiers")]
822 token_modifiers: Vec<&'static str>,
823}
824
825#[derive(Serialize)]
826struct WasmCompletionItem {
827 label: String,
828 kind: String,
829 detail: Option<String>,
830 insert_text: Option<String>,
831 insert_text_format: Option<String>,
832 trigger_completion_after_insert: bool,
833}
834
835#[derive(Deserialize)]
836struct Position {
837 line: u32,
838 column: u32,
839}