Auto stash before rebase of "relpace_lsp_with_rust" onto "origin/relpace_lsp_with_rust"
This commit is contained in:
Generated
+10
@@ -20,6 +20,16 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tree-sitter",
|
"tree-sitter",
|
||||||
|
"tree-sitter-tcl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "analysis-tests"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"analysis",
|
||||||
|
"tree-sitter",
|
||||||
|
"tree-sitter-tcl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ members = [
|
|||||||
"crates/text",
|
"crates/text",
|
||||||
"crates/analysis",
|
"crates/analysis",
|
||||||
"crates/features",
|
"crates/features",
|
||||||
|
"tests/analysis-tests",
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ edition = "2021"
|
|||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
lsp-types = { workspace = true }
|
lsp-types = { workspace = true }
|
||||||
tree-sitter = { workspace = true }
|
tree-sitter = { workspace = true }
|
||||||
|
tree-sitter-tcl = { workspace = true }
|
||||||
serde = { workspace = true }
|
serde = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
use tree_sitter::Parser;
|
||||||
|
use analysis::collect_procs;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let src = r#"namespace eval foo {
|
||||||
|
proc bar {x y} { return $x }
|
||||||
|
}
|
||||||
|
proc top {} {}
|
||||||
|
"#;
|
||||||
|
let mut parser = Parser::new();
|
||||||
|
parser
|
||||||
|
.set_language(&tree_sitter_tcl::LANGUAGE.into())
|
||||||
|
.expect("load tcl grammar");
|
||||||
|
let tree = parser.parse(src, None).expect("parse");
|
||||||
|
println!("tree: {}", tree.root_node().to_sexp());
|
||||||
|
let procs = collect_procs(&tree, src);
|
||||||
|
println!("found {} procs via collect_procs", procs.len());
|
||||||
|
for p in procs {
|
||||||
|
println!("- {} {:?}", p.name, p.params);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "analysis-tests"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
publish = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tree-sitter = { workspace = true }
|
||||||
|
tree-sitter-tcl = { workspace = true }
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
analysis = { path = "../../crates/analysis" }
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
//! Helpers for workspace-level tests. Provides simple Tree-sitter Tcl utilities
|
||||||
|
//! so you can inspect the parsed tree output while writing tests.
|
||||||
|
|
||||||
|
use tree_sitter::{Parser, Tree};
|
||||||
|
|
||||||
|
/// Parse the given Tcl source and return the syntax tree.
|
||||||
|
pub fn parse_tcl(src: &str) -> Tree {
|
||||||
|
let mut parser = Parser::new();
|
||||||
|
parser
|
||||||
|
.set_language(&tree_sitter_tcl::LANGUAGE.into())
|
||||||
|
.expect("load tcl grammar");
|
||||||
|
parser.parse(src, None).expect("parse")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the s-expression (to_sexp) of the root node for quick inspection.
|
||||||
|
pub fn tcl_to_sexp(src: &str) -> String {
|
||||||
|
let tree = parse_tcl(src);
|
||||||
|
tree.root_node().to_sexp()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print the s-expression to stdout. Run tests with `-- --nocapture` to see it.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// cargo test -p analysis-tests -- --nocapture
|
||||||
|
pub fn print_tcl_sexp(src: &str) {
|
||||||
|
println!("{}", tcl_to_sexp(src));
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
//! Small CLI to inspect Tree-sitter Tcl output.
|
||||||
|
//! Examples:
|
||||||
|
//! cargo run -p analysis-tests -- "proc hello {a} { puts $a }"
|
||||||
|
//! cargo run -p analysis-tests -- --file path/to/file.tcl
|
||||||
|
|
||||||
|
use std::{ env, fs };
|
||||||
|
|
||||||
|
fn print_usage() {
|
||||||
|
eprintln!(
|
||||||
|
"Usage:\n cargo run -p analysis-tests -- [--file PATH] [SRC]\n\nExamples:\n cargo run -p analysis-tests -- \"proc hello {{a b c {{optArg 0}} {{ puts $a }}\"\n cargo run -p analysis-tests -- --file script.tcl\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let args: Vec<String> = env::args().skip(1).collect();
|
||||||
|
if args.iter().any(|a| (a == "-h" || a == "--help")) {
|
||||||
|
print_usage();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let src = if args.len() >= 2 && args[0] == "--file" {
|
||||||
|
fs::read_to_string(&args[1]).expect("read file")
|
||||||
|
} else if !args.is_empty() {
|
||||||
|
args.join(" ")
|
||||||
|
} else {
|
||||||
|
// Default sample
|
||||||
|
String::from("proc hello {a} { puts $a }")
|
||||||
|
};
|
||||||
|
|
||||||
|
// Print S-expression of the parsed tree
|
||||||
|
analysis_tests::print_tcl_sexp(&src);
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
//! Grammar-focused tests: exercise Tree-sitter Tcl directly.
|
||||||
|
//! These do not rely on the `analysis` crate — just the parser/AST.
|
||||||
|
|
||||||
|
use tree_sitter::{ Node, Parser };
|
||||||
|
|
||||||
|
fn parse_tcl(src: &str) -> tree_sitter::Tree {
|
||||||
|
let mut parser = Parser::new();
|
||||||
|
parser.set_language(&tree_sitter_tcl::LANGUAGE.into()).expect("load tcl grammar");
|
||||||
|
parser.parse(src, None).expect("parse")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text<'a>(node: Node<'a>, src: &'a str) -> &'a str {
|
||||||
|
node.utf8_text(src.as_bytes()).expect("utf8 text")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn procedure_node_and_fields() {
|
||||||
|
let src = r#"proc hello {a b} { return $a }"#;
|
||||||
|
let tree = parse_tcl(src);
|
||||||
|
let root = tree.root_node();
|
||||||
|
|
||||||
|
// The top-level child for a proc is a builtin `procedure` node
|
||||||
|
// (per grammar.js: `procedure: seq('proc', field('name', _word), field('arguments', arguments), field('body', _word))`).
|
||||||
|
// The source_file is a list of (optional _command, terminator). The first named child
|
||||||
|
// should be the `_command` node itself; for a `proc` that is `procedure`.
|
||||||
|
let proc = root.named_child(0).expect("first child");
|
||||||
|
assert_eq!(proc.kind(), "procedure");
|
||||||
|
|
||||||
|
// Fields: name, arguments, body
|
||||||
|
let name = proc.child_by_field_name("name").expect("name field");
|
||||||
|
assert_eq!(text(name, src), "hello");
|
||||||
|
|
||||||
|
let args = proc.child_by_field_name("arguments").expect("args field");
|
||||||
|
assert_eq!(args.kind(), "arguments");
|
||||||
|
// Arguments contain one or more `argument` nodes when braced.
|
||||||
|
let mut cursor = args.walk();
|
||||||
|
let mut arg_names = vec![];
|
||||||
|
for ch in args.named_children(&mut cursor) {
|
||||||
|
if ch.kind() == "argument" {
|
||||||
|
if let Some(n) = ch.child_by_field_name("name") {
|
||||||
|
arg_names.push(text(n, src).to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert_eq!(arg_names, vec!["a", "b"]);
|
||||||
|
|
||||||
|
let body = proc.child_by_field_name("body").expect("body field");
|
||||||
|
// A braced block body is parsed as `braced_word`.
|
||||||
|
assert_eq!(body.kind(), "braced_word");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generic_command_and_arguments() {
|
||||||
|
let src = "puts hello";
|
||||||
|
let tree = parse_tcl(src);
|
||||||
|
let root = tree.root_node();
|
||||||
|
let cmd = root.named_child(0).expect("first child");
|
||||||
|
assert_eq!(cmd.kind(), "command");
|
||||||
|
|
||||||
|
let name = cmd.child_by_field_name("name").expect("name field");
|
||||||
|
assert_eq!(text(name, src), "puts");
|
||||||
|
|
||||||
|
let args = cmd.child_by_field_name("arguments").expect("arguments field (word_list)");
|
||||||
|
assert_eq!(args.kind(), "word_list");
|
||||||
|
|
||||||
|
// `word_list` has word-like children; collect their surface text.
|
||||||
|
let mut cursor = args.walk();
|
||||||
|
let words: Vec<_> = args
|
||||||
|
.named_children(&mut cursor)
|
||||||
|
.map(|w| text(w, src).to_string())
|
||||||
|
.collect();
|
||||||
|
assert_eq!(words, vec!["hello"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn namespace_eval_contains_nested_proc() {
|
||||||
|
let src = r#"
|
||||||
|
namespace eval foo {
|
||||||
|
proc bar {x y} { return $x }
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let tree = parse_tcl(src);
|
||||||
|
let root = tree.root_node();
|
||||||
|
let ns = root.named_child(0).expect("first child");
|
||||||
|
assert_eq!(ns.kind(), "namespace");
|
||||||
|
|
||||||
|
// The `namespace`'s `word_list` contains: `eval`, `foo`, and a braced block.
|
||||||
|
let mut cursor = ns.walk();
|
||||||
|
let mut braced_block: Option<Node> = None;
|
||||||
|
for ch in ns.named_children(&mut cursor) {
|
||||||
|
if ch.kind() == "word_list" {
|
||||||
|
let mut cur2 = ch.walk();
|
||||||
|
for w in ch.named_children(&mut cur2) {
|
||||||
|
if w.kind() == "braced_word" {
|
||||||
|
braced_block = Some(w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let block = braced_block.expect("braced block in namespace eval");
|
||||||
|
|
||||||
|
// Inside the braced block, we expect a `procedure` command for `proc bar ...`.
|
||||||
|
let mut cur = block.walk();
|
||||||
|
let mut found_proc = false;
|
||||||
|
for n in block.named_children(&mut cur) {
|
||||||
|
if n.kind() == "procedure" {
|
||||||
|
found_proc = true;
|
||||||
|
let name = n.child_by_field_name("name").expect("proc name");
|
||||||
|
assert_eq!(text(name, src), "bar");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(found_proc, "expected nested procedure inside namespace block");
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user