tree-sitter fun 👀

This commit is contained in:
Noah Santschi-Cooney 2022-03-19 22:58:01 +00:00
parent 86100aa008
commit d3c0869288
No known key found for this signature in database
GPG key ID: 3B22282472C8AE48
9 changed files with 198 additions and 5 deletions

22
server/Cargo.lock generated
View file

@ -765,6 +765,8 @@ dependencies = [
"slog-term",
"tempdir",
"thiserror",
"tree-sitter",
"tree-sitter-glsl",
"url",
"walkdir",
]
@ -1631,6 +1633,26 @@ dependencies = [
"serde",
]
[[package]]
name = "tree-sitter"
version = "0.20.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09b3b781640108d29892e8b9684642d2cda5ea05951fd58f0fea1db9edeb9b71"
dependencies = [
"cc",
"regex",
]
[[package]]
name = "tree-sitter-glsl"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d20c93432c782c4f66618ffb06669870ac6231a86b1777813a6c97148f1f0fb"
dependencies = [
"cc",
"tree-sitter",
]
[[package]]
name = "treeline"
version = "0.1.0"

View file

@ -30,6 +30,8 @@ slog-atomic = "3.1"
once_cell = "1.7"
rand = "0.8"
arc-swap = "1.5.0"
tree-sitter = "0.20.6"
tree-sitter-glsl = "0.1.2"
[dev-dependencies]
tempdir = "0.3"

View file

@ -3,9 +3,11 @@ use std::{collections::HashMap, path::Path};
use serde_json::Value;
use anyhow::{format_err, Result};
use slog_scope::info;
pub mod graph_dot;
pub mod merged_includes;
pub mod parse_tree;
pub struct CustomCommandProvider {
commands: HashMap<String, Box<dyn Invokeable>>,
@ -20,6 +22,9 @@ impl CustomCommandProvider {
pub fn execute(&self, command: &str, args: &[Value], root_path: &Path) -> Result<Value> {
if self.commands.contains_key(command) {
info!("running command";
"command" => command,
"args" => format!("[{}]", args.iter().map(|v| serde_json::to_string(v).unwrap()).collect::<Vec<String>>().join(", ")));
return self.commands.get(command).unwrap().run_command(root_path, args);
}
Err(format_err!("command doesn't exist"))

View file

@ -0,0 +1,94 @@
use std::{
cell::RefCell,
fs,
path::{Path, PathBuf},
rc::Rc,
};
use anyhow::{format_err, Result};
use serde_json::Value;
use slog_scope::warn;
use tree_sitter::{Parser, TreeCursor};
use crate::url_norm::FromJson;
use super::Invokeable;
pub struct TreeSitterSExpr {
pub tree_sitter: Rc<RefCell<Parser>>,
}
impl Invokeable for TreeSitterSExpr {
fn run_command(&self, _: &Path, arguments: &[Value]) -> Result<Value> {
let path = PathBuf::from_json(arguments.get(0).unwrap())?;
warn!("parsing"; "path" => path.to_str().unwrap().to_string());
let source = fs::read_to_string(path)?;
let tree = match self.tree_sitter.borrow_mut().parse(source, None) {
Some(tree) => tree,
None => return Err(format_err!("tree-sitter parsing resulted in no parse tree")),
};
let mut cursor = tree.walk();
let rendered = render_parse_tree(&mut cursor);
Ok(serde_json::value::Value::String(rendered))
}
}
fn render_parse_tree(cursor: &mut TreeCursor) -> String {
let mut string = String::new();
let mut indent = 0;
let mut visited_children = false;
loop {
let node = cursor.node();
let display_name = if node.is_missing() {
format!("MISSING {}", node.kind())
} else if node.is_named() {
node.kind().to_string()
} else {
"".to_string()
};
if visited_children {
if cursor.goto_next_sibling() {
visited_children = false;
} else if cursor.goto_parent() {
visited_children = true;
indent -= 1;
} else {
break;
}
} else {
if !display_name.is_empty() {
let start = node.start_position();
let end = node.end_position();
let field_name = match cursor.field_name() {
Some(name) => name.to_string() + ": ",
None => "".to_string(),
};
string += (" ".repeat(indent)
+ format!("{}{} [{}, {}] - [{}, {}]\n", field_name, display_name, start.row, start.column, end.row, end.column)
.trim_start())
.as_str();
}
if cursor.goto_first_child() {
visited_children = false;
indent += 1;
} else {
visited_children = true;
}
}
}
string
}

View file

@ -7,6 +7,7 @@ use petgraph::stable_graph::NodeIndex;
use serde::Deserialize;
use serde_json::{from_value, Value};
use tree_sitter::Parser;
use url_norm::FromUrl;
use walkdir::WalkDir;
@ -65,12 +66,16 @@ fn main() {
let cache_graph = graph::CachedStableGraph::new();
let mut parser = Parser::new();
parser.set_language(tree_sitter_glsl::language()).unwrap();
let mut langserver = MinecraftShaderLanguageServer {
endpoint: endpoint_output.clone(),
graph: Rc::new(RefCell::new(cache_graph)),
root: "".into(),
command_provider: None,
opengl_context: Rc::new(opengl::OpenGlContext::new()),
tree_sitter: Rc::new(RefCell::new(parser)),
log_guard: Some(guard),
};
@ -87,6 +92,12 @@ fn main() {
graph: langserver.graph.clone(),
}),
),
(
"parseTree",
Box::new(commands::parse_tree::TreeSitterSExpr {
tree_sitter: langserver.tree_sitter.clone(),
}),
),
]));
LSPEndpoint::run_server_from_input(&mut stdin().lock(), endpoint_output, langserver);
@ -98,6 +109,7 @@ struct MinecraftShaderLanguageServer {
root: PathBuf,
command_provider: Option<commands::CustomCommandProvider>,
opengl_context: Rc<dyn opengl::ShaderValidator>,
tree_sitter: Rc<RefCell<Parser>>,
log_guard: Option<slog_scope::GlobalLoggerGuard>,
}

View file

@ -50,6 +50,7 @@ fn new_temp_server(opengl_context: Option<Box<dyn opengl::ShaderValidator>>) ->
command_provider: None,
opengl_context: context.into(),
log_guard: Some(guard),
tree_sitter: Rc::new(RefCell::new(Parser::new())),
}
}