|
| 1 | +use line_index::LineIndex; |
| 2 | +use log::info; |
| 3 | +use serde::Serialize; |
| 4 | +use wasm_bindgen::prelude::*; |
| 5 | +use web_sys::js_sys::Error; |
| 6 | + |
| 7 | +#[wasm_bindgen(start)] |
| 8 | +pub fn run() { |
| 9 | + use log::Level; |
| 10 | + |
| 11 | + // When the `console_error_panic_hook` feature is enabled, we can call the |
| 12 | + // `set_panic_hook` function at least once during initialization, and then |
| 13 | + // we will get better error messages if our code ever panics. |
| 14 | + // |
| 15 | + // For more details see |
| 16 | + // https://github.com/rustwasm/console_error_panic_hook#readme |
| 17 | + #[cfg(feature = "console_error_panic_hook")] |
| 18 | + console_error_panic_hook::set_once(); |
| 19 | + console_log::init_with_level(Level::Debug).expect("Initializing logger went wrong."); |
| 20 | + info!("init!"); |
| 21 | +} |
| 22 | + |
| 23 | +#[wasm_bindgen] |
| 24 | +pub fn dump_cst(text: String) -> String { |
| 25 | + let parse = squawk_syntax::SourceFile::parse(&text); |
| 26 | + format!("{:#?}", parse.syntax_node()) |
| 27 | +} |
| 28 | + |
| 29 | +#[wasm_bindgen] |
| 30 | +pub fn dump_tokens(text: String) -> String { |
| 31 | + let tokens = squawk_lexer::tokenize(&text); |
| 32 | + let mut start = 0; |
| 33 | + let mut out = String::new(); |
| 34 | + for token in tokens { |
| 35 | + let end = start + token.len; |
| 36 | + let content = &text[start as usize..(end) as usize]; |
| 37 | + out += &format!("{:?}@{start}..{end} {:?}\n", token.kind, content); |
| 38 | + start += token.len; |
| 39 | + } |
| 40 | + out |
| 41 | +} |
| 42 | + |
| 43 | +#[allow(dead_code)] |
| 44 | +#[derive(Serialize)] |
| 45 | +enum Severity { |
| 46 | + Hint, |
| 47 | + Info, |
| 48 | + Warning, |
| 49 | + Error, |
| 50 | +} |
| 51 | + |
| 52 | +#[derive(Serialize)] |
| 53 | +struct LintError { |
| 54 | + severity: Severity, |
| 55 | + code: String, |
| 56 | + message: String, |
| 57 | + start_line_number: u32, |
| 58 | + start_column: u32, |
| 59 | + end_line_number: u32, |
| 60 | + end_column: u32, |
| 61 | + // used for the linter tab |
| 62 | + range_start: usize, |
| 63 | + // used for the linter tab |
| 64 | + range_end: usize, |
| 65 | + // used for the linter tab |
| 66 | + messages: Vec<String>, |
| 67 | +} |
| 68 | + |
| 69 | +#[wasm_bindgen] |
| 70 | +pub fn lint(text: String) -> Result<JsValue, Error> { |
| 71 | + let mut linter = squawk_linter::Linter::with_all_rules(); |
| 72 | + let parse = squawk_syntax::SourceFile::parse(&text); |
| 73 | + let parse_errors = parse.errors(); |
| 74 | + |
| 75 | + let line_index = LineIndex::new(&text); |
| 76 | + |
| 77 | + // TODO: chain these with other stuff |
| 78 | + let parse_errors = parse_errors.iter().map(|x| { |
| 79 | + let range_start = x.range().start(); |
| 80 | + let range_end = x.range().end(); |
| 81 | + let start = line_index.line_col(range_start); |
| 82 | + let end = line_index.line_col(range_end); |
| 83 | + let start = line_index |
| 84 | + .to_wide(line_index::WideEncoding::Utf16, start) |
| 85 | + .unwrap(); |
| 86 | + let end = line_index |
| 87 | + .to_wide(line_index::WideEncoding::Utf16, end) |
| 88 | + .unwrap(); |
| 89 | + LintError { |
| 90 | + severity: Severity::Error, |
| 91 | + code: "syntax-error".to_string(), |
| 92 | + message: x.message().to_string(), |
| 93 | + start_line_number: start.line, |
| 94 | + start_column: start.col, |
| 95 | + end_line_number: end.line, |
| 96 | + end_column: end.col, |
| 97 | + range_start: range_start.into(), |
| 98 | + range_end: range_end.into(), |
| 99 | + messages: vec![], |
| 100 | + } |
| 101 | + }); |
| 102 | + |
| 103 | + let lint_errors = linter.lint(parse, &text); |
| 104 | + let errors = lint_errors.into_iter().map(|x| { |
| 105 | + let start = line_index.line_col(x.text_range.start()); |
| 106 | + let end = line_index.line_col(x.text_range.end()); |
| 107 | + let start = line_index |
| 108 | + .to_wide(line_index::WideEncoding::Utf16, start) |
| 109 | + .unwrap(); |
| 110 | + let end = line_index |
| 111 | + .to_wide(line_index::WideEncoding::Utf16, end) |
| 112 | + .unwrap(); |
| 113 | + LintError { |
| 114 | + code: x.code.to_string(), |
| 115 | + range_start: x.text_range.start().into(), |
| 116 | + range_end: x.text_range.end().into(), |
| 117 | + message: x.message.clone(), |
| 118 | + messages: x.messages.clone(), |
| 119 | + // parser errors should be error |
| 120 | + severity: Severity::Warning, |
| 121 | + start_line_number: start.line, |
| 122 | + start_column: start.col, |
| 123 | + end_line_number: end.line, |
| 124 | + end_column: end.col, |
| 125 | + } |
| 126 | + }); |
| 127 | + |
| 128 | + let mut errors_to_dump = errors.chain(parse_errors).collect::<Vec<_>>(); |
| 129 | + errors_to_dump.sort_by_key(|k| (k.start_line_number, k.start_column)); |
| 130 | + |
| 131 | + serde_wasm_bindgen::to_value(&errors_to_dump).map_err(into_error) |
| 132 | +} |
| 133 | + |
| 134 | +fn into_error<E: std::fmt::Display>(err: E) -> Error { |
| 135 | + Error::new(&err.to_string()) |
| 136 | +} |
0 commit comments