aboutsummaryrefslogtreecommitdiff
path: root/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs150
1 files changed, 150 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..37ac261
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,150 @@
1mod utils;
2
3use js_sys::Array;
4use rnix::{parser::ParseError, NodeOrToken, SyntaxElement};
5use wasm_bindgen::prelude::*;
6
7use std::{convert::From, str::FromStr};
8
9#[cfg(feature = "wee_alloc")]
10#[global_allocator]
11static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
12
13// wrapper type to pass syntax elements to JS
14#[wasm_bindgen]
15#[derive(Debug, Clone)]
16pub struct SynNode {
17 node: SyntaxElement,
18}
19
20impl SynNode {
21 pub fn new(node: SyntaxElement) -> Self {
22 Self { node }
23 }
24}
25
26#[wasm_bindgen]
27impl SynNode {
28 pub fn children(&self) -> Vec<JsValue> {
29 match &self.node {
30 NodeOrToken::Node(n) => n
31 .children_with_tokens()
32 .map(SynNode::new)
33 .map(JsValue::from)
34 .collect(),
35 NodeOrToken::Token(_) => vec![],
36 }
37 }
38
39 pub fn is_token(&self) -> bool {
40 self.node.as_token().is_some()
41 }
42
43 pub fn to_string(&self) -> String {
44 format!("{:?} {:?}", self.node.kind(), self.node.text_range(),)
45 }
46
47 pub fn range(&self) -> TextRange {
48 let r = self.node.text_range();
49 (r.start().into(), r.end().into()).into()
50 }
51
52 pub fn kind(&self) -> String {
53 format!("{:?}", self.node.kind())
54 }
55
56 pub fn text(&self) -> String {
57 match &self.node {
58 NodeOrToken::Node(_) => "".into(),
59 NodeOrToken::Token(t) => format!("{:?}", t.text()),
60 }
61 }
62
63 pub fn from_str(s: &str) -> Result<JsValue, JsValue> {
64 FromStr::from_str(s)
65 .map(|p: SynNode| JsValue::from(p))
66 .map_err(JsValue::from)
67 }
68}
69
70impl FromStr for SynNode {
71 type Err = Array;
72 fn from_str(s: &str) -> Result<Self, Array> {
73 let source_file = rnix::parse(s);
74 if source_file.errors().is_empty() {
75 Ok(Self {
76 node: NodeOrToken::Node(source_file.as_result().unwrap().node().clone()),
77 })
78 } else {
79 Err(source_file
80 .errors()
81 .iter()
82 .map(SynErr::new)
83 .map(JsValue::from)
84 .collect())
85 }
86 }
87}
88
89#[wasm_bindgen]
90#[derive(Debug, Clone)]
91struct SynErr {
92 err: ParseError,
93}
94
95impl SynErr {
96 pub fn new(err: &ParseError) -> Self {
97 Self { err: err.clone() }
98 }
99}
100
101#[wasm_bindgen]
102impl SynErr {
103 pub fn to_string(&self) -> String {
104 self.err.to_string()
105 }
106}
107
108#[wasm_bindgen]
109pub struct TextRange {
110 start: u32,
111 end: u32,
112}
113
114impl From<(u32, u32)> for TextRange {
115 fn from((start, end): (u32, u32)) -> Self {
116 TextRange { start, end }
117 }
118}
119
120impl TextRange {
121 pub fn to_line_col(&self, source: &str) -> (u32, u32) {
122 let end = self.end() as usize;
123 let line = &source[..end].chars().filter(|&c| c == '\n').count() + 1;
124 let col = &source[..end].rfind('\n').map(|c| end - c).unwrap_or(end);
125 (line as u32, *col as u32)
126 }
127}
128
129#[wasm_bindgen]
130impl TextRange {
131 pub fn start(&self) -> u32 {
132 self.start
133 }
134
135 pub fn end(&self) -> u32 {
136 self.end
137 }
138
139 pub fn line(&self, source: &str) -> u32 {
140 self.to_line_col(source).0
141 }
142
143 pub fn col(&self, source: &str) -> u32 {
144 self.to_line_col(source).1
145 }
146
147 pub fn to_string(&self) -> String {
148 format!("{}..{}", self.start, self.end)
149 }
150}