Skip to content

Commit ced4252

Browse files
committed
Simplify argument extraction
1 parent 1f9af53 commit ced4252

File tree

3 files changed

+92
-42
lines changed

3 files changed

+92
-42
lines changed

crates/ark/src/lsp/diagnostics.rs

Lines changed: 15 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@ use tower_lsp::lsp_types::DiagnosticSeverity;
1919
use tree_sitter::Node;
2020
use tree_sitter::Range;
2121

22+
use crate::lsp;
2223
use crate::lsp::declarations::top_level_declare;
2324
use crate::lsp::diagnostics_syntax::syntax_diagnostics;
2425
use crate::lsp::documents::Document;
2526
use crate::lsp::encoding::convert_tree_sitter_range_to_lsp_range;
2627
use crate::lsp::indexer;
2728
use crate::lsp::inputs::library::Library;
2829
use crate::lsp::state::WorldState;
30+
use crate::lsp::traits::node::NodeExt;
2931
use crate::lsp::traits::rope::RopeExt;
3032
use crate::treesitter::node_has_error_or_missing;
3133
use crate::treesitter::BinaryOperatorType;
@@ -799,59 +801,34 @@ fn recurse_call(
799801
match fun {
800802
"library" => {
801803
// Track symbols exported by `library()` calls
802-
handle_library_call(node, context, diagnostics)?;
804+
handle_library_call(node, context)?;
803805
},
804-
// default case: recurse into each argument
805-
_ => recurse_call_like_arguments_default(node, context, diagnostics)?,
806+
_ => {},
806807
};
807808

809+
// Continue with default recursion to handle any other arguments
810+
recurse_call_like_arguments_default(node, context, diagnostics)?;
811+
808812
().ok()
809813
}
810814

811-
fn handle_library_call(
812-
node: Node,
813-
context: &mut DiagnosticContext,
814-
diagnostics: &mut Vec<Diagnostic>,
815-
) -> anyhow::Result<()> {
816-
// Get the arguments node
817-
let Some(arguments) = node.child_by_field_name("arguments") else {
818-
return Ok(());
819-
};
820-
821-
// Find the first argument (package name)
822-
let mut cursor = arguments.walk();
823-
let mut children = arguments.children_by_field_name("argument", &mut cursor);
824-
825-
let Some(first_arg) = children.next() else {
826-
return Ok(());
827-
};
828-
829-
// Get the package name from the argument value
830-
let Some(value) = first_arg.child_by_field_name("value") else {
831-
return Ok(());
815+
fn handle_library_call(node: Node, context: &mut DiagnosticContext) -> anyhow::Result<()> {
816+
// Find the first argument (package name). Positionally for now.
817+
let Some(value) = node.arguments_values().nth(0) else {
818+
return Err(anyhow::anyhow!("Can't unpack `library()` argument"));
832819
};
833820

834-
let package_name = if value.is_identifier() {
835-
context.contents.node_slice(&value)?.to_string()
836-
} else if value.is_string() {
837-
// Remove quotes from string literal
838-
let raw = context.contents.node_slice(&value)?.to_string();
839-
raw.trim_matches('"').trim_matches('\'').to_string()
840-
} else {
841-
return Ok(());
842-
};
821+
let package_name = value.get_identifier_or_string_text(context.contents)?;
843822

844-
// Try to get the package from the library
823+
// Insert exports globablly for now
845824
if let Some(package) = context.library.get(&package_name) {
846-
// Add all exported symbols to library_symbols
847825
for symbol in &package.namespace.exports {
848826
context.library_symbols.insert(symbol.clone());
849827
}
828+
} else {
829+
lsp::log_warn!("Can't get exports from package {package_name} because it is not installed.")
850830
}
851831

852-
// Continue with default recursion to handle any other arguments
853-
recurse_call_like_arguments_default(node, context, diagnostics)?;
854-
855832
Ok(())
856833
}
857834

crates/ark/src/lsp/traits/node.rs

Lines changed: 62 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ pub trait NodeExt: Sized {
9090
fn bwd_leaf_iter(&self) -> BwdLeafIterator<'_>;
9191

9292
fn ancestors(&self) -> impl Iterator<Item = Self>;
93+
fn children_of(node: Self) -> impl Iterator<Item = Self>;
94+
fn next_siblings(&self) -> impl Iterator<Item = Self>;
95+
fn arguments_values(&self) -> impl Iterator<Item = Self>;
9396
}
9497

9598
impl<'tree> NodeExt for Node<'tree> {
@@ -199,13 +202,68 @@ impl<'tree> NodeExt for Node<'tree> {
199202
BwdLeafIterator { node: *self }
200203
}
201204

202-
// From rowan. Note that until we switch to rowan, each `parent()` call
203-
// causes a linear traversal of the whole tree to find the parent node.
204-
// We could do better in the future:
205-
// https://github.com/tree-sitter/tree-sitter/pull/3214
206205
fn ancestors(&self) -> impl Iterator<Item = Node<'tree>> {
206+
// We'd ideally use the cursor API here too but
207+
// `ts_tree_cursor_goto_parent()` doesn't behave like
208+
// `ts_node_parent()`: the latter traverses `ERROR` nodes but not the
209+
// former. So for now we accept the performance hit of tree traversal at
210+
// each `parent()` call.
207211
std::iter::successors(Some(*self), |p| p.parent())
208212
}
213+
214+
fn next_siblings(&self) -> impl Iterator<Item = Node<'tree>> {
215+
let mut cursor = self.walk();
216+
217+
let first = if cursor.goto_next_sibling() {
218+
Some(cursor.node())
219+
} else {
220+
None
221+
};
222+
223+
std::iter::successors(first, move |_| {
224+
if cursor.goto_next_sibling() {
225+
Some(cursor.node())
226+
} else {
227+
None
228+
}
229+
})
230+
}
231+
232+
fn children_of(node: Node<'tree>) -> impl Iterator<Item = Node<'tree>> {
233+
let mut cursor = node.walk();
234+
let mut first = true;
235+
236+
std::iter::from_fn(move || {
237+
let advanced = if first {
238+
first = false;
239+
cursor.goto_first_child()
240+
} else {
241+
cursor.goto_next_sibling()
242+
};
243+
244+
if advanced {
245+
Some(cursor.node())
246+
} else {
247+
None
248+
}
249+
})
250+
}
251+
252+
/// Takes a call node and iterates over the values of its arguments
253+
fn arguments_values(&self) -> impl Iterator<Item = Node<'tree>> {
254+
self.child_by_field_name("arguments")
255+
// Create iterator that unpacks Option with `flat_map()`
256+
.into_iter()
257+
.flat_map(Self::children_of)
258+
.filter_map(|node| {
259+
// This takes care of non-argument nodes like `(` and `)`
260+
if node.kind() == "argument" {
261+
node.child_by_field_name("value")
262+
} else {
263+
None
264+
}
265+
})
266+
}
209267
}
210268

211269
/// First, recurse through children to find the smallest

crates/ark/src/treesitter.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,7 @@ pub trait NodeTypeExt: Sized {
283283
fn is_identifier(&self) -> bool;
284284
fn is_string(&self) -> bool;
285285
fn is_identifier_or_string(&self) -> bool;
286+
fn get_identifier_or_string_text(&self, contents: &ropey::Rope) -> anyhow::Result<String>;
286287
fn is_keyword(&self) -> bool;
287288
fn is_call(&self) -> bool;
288289
fn is_subset(&self) -> bool;
@@ -325,6 +326,20 @@ impl NodeTypeExt for Node<'_> {
325326
matches!(self.node_type(), NodeType::Identifier | NodeType::String)
326327
}
327328

329+
fn get_identifier_or_string_text(&self, contents: &ropey::Rope) -> anyhow::Result<String> {
330+
match self.node_type() {
331+
NodeType::Identifier => return Ok(contents.node_slice(self)?.to_string()),
332+
NodeType::String => {
333+
// Remove quotes from string literal
334+
let string = contents.node_slice(self)?.to_string();
335+
Ok(string.trim_matches('"').trim_matches('\'').to_string())
336+
},
337+
_ => {
338+
return Err(anyhow::anyhow!("Not an identifier or string"));
339+
},
340+
}
341+
}
342+
328343
fn is_keyword(&self) -> bool {
329344
matches!(
330345
self.node_type(),

0 commit comments

Comments
 (0)