mirror of
https://github.com/silvanshade/tower-lsp-web-demo.git
synced 2025-01-22 11:47:51 +00:00
Implement textDocument/documentSymbol feature
This commit is contained in:
parent
01b836d598
commit
db38b5ed74
4 changed files with 135 additions and 45 deletions
|
@ -17,6 +17,7 @@ console_error_panic_hook = "0.1.7"
|
|||
dashmap = "5.3.4"
|
||||
demo-lsp-language = { version = "0.0", path = "../language" }
|
||||
futures = "0.3.21"
|
||||
indoc = "1.0"
|
||||
js-sys = "0.3.57"
|
||||
log = "0.4"
|
||||
lsp = { version = "0.93", package = "lsp-types" }
|
||||
|
@ -29,6 +30,7 @@ tree-sitter = { package = "tree-sitter-facade", version = "0.4" }
|
|||
wasm-bindgen = "0.2.80"
|
||||
wasm-bindgen-futures = { version = "0.4.30", features = ["futures-core-03-stream"] }
|
||||
wasm-streams = "0.2.3"
|
||||
web-tree-sitter-sys = "0.6"
|
||||
|
||||
[dependencies.web-sys]
|
||||
version = "0.3.57"
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
pub mod text_document {
|
||||
use std::sync::Arc;
|
||||
|
||||
use lsp_text::RopeExt;
|
||||
|
||||
pub async fn did_open(
|
||||
session: Arc<crate::core::Session>,
|
||||
params: lsp::DidOpenTextDocumentParams,
|
||||
|
@ -38,4 +40,129 @@ pub mod text_document {
|
|||
session.client()?.publish_diagnostics(uri, diagnostics, version).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn document_symbol(
|
||||
session: Arc<crate::core::Session>,
|
||||
params: lsp::DocumentSymbolParams,
|
||||
) -> anyhow::Result<Option<lsp::DocumentSymbolResponse>> {
|
||||
use wasm_bindgen::JsCast;
|
||||
|
||||
fn make_symbol(
|
||||
uri: &lsp::Url,
|
||||
content: &ropey::Rope,
|
||||
declaration: tree_sitter::Node,
|
||||
identifier: tree_sitter::Node,
|
||||
kind: lsp::SymbolKind,
|
||||
) -> lsp::SymbolInformation {
|
||||
let name = content.utf8_text_for_tree_sitter_node(&identifier).into();
|
||||
let range = content.tree_sitter_range_to_lsp_range(declaration.range());
|
||||
#[allow(deprecated)]
|
||||
lsp::SymbolInformation {
|
||||
name,
|
||||
kind,
|
||||
tags: Default::default(),
|
||||
deprecated: Default::default(),
|
||||
location: lsp::Location::new(uri.clone(), range),
|
||||
container_name: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
let uri = ¶ms.text_document.uri;
|
||||
|
||||
let text = session.get_text(uri).await?;
|
||||
let content = &text.content;
|
||||
|
||||
let tree = session.get_tree(uri).await?;
|
||||
let tree = tree.lock().await.clone();
|
||||
|
||||
// NOTE: transmutes here because we do not yet support query functionality in
|
||||
// tree-sitter-facade; thus we use the raw bindings from web-tree-sitter-sys.
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
let node = unsafe { std::mem::transmute::<_, web_tree_sitter_sys::SyntaxNode>(tree.root_node()) };
|
||||
#[allow(unsafe_code)]
|
||||
let language = unsafe { std::mem::transmute::<_, web_tree_sitter_sys::Language>(session.language.clone()) };
|
||||
|
||||
static QUERY: &str = indoc::indoc! {r"
|
||||
(function_declaration
|
||||
name: (identifier) @identifier) @function_declaration
|
||||
(lexical_declaration
|
||||
(variable_declarator
|
||||
name: (identifier) @identifier)) @class_declaration
|
||||
(variable_declaration
|
||||
(variable_declarator
|
||||
name: (identifier) @identifier)) @variable_declaration
|
||||
(class_declaration
|
||||
name: (identifier) @identifier) @class_declaration
|
||||
"};
|
||||
let query = language.query(&QUERY.into()).expect("failed to create query");
|
||||
let matches = {
|
||||
let start_position = None;
|
||||
let end_position = None;
|
||||
query
|
||||
.matches(&node, start_position, end_position)
|
||||
.into_vec()
|
||||
.into_iter()
|
||||
.map(JsCast::unchecked_into::<web_tree_sitter_sys::QueryMatch>)
|
||||
};
|
||||
|
||||
let mut symbols = vec![];
|
||||
|
||||
for r#match in matches {
|
||||
let captures = r#match
|
||||
.captures()
|
||||
.into_vec()
|
||||
.into_iter()
|
||||
.map(JsCast::unchecked_into::<web_tree_sitter_sys::QueryCapture>)
|
||||
.collect::<Vec<_>>();
|
||||
if let [declaration, identifier] = captures.as_slice() {
|
||||
// NOTE: reverse the transmutes from above so we can use tree-sitter-facade bindings for Node
|
||||
#[allow(unsafe_code)]
|
||||
let declaration_node = unsafe { std::mem::transmute::<_, tree_sitter::Node>(declaration.node()) };
|
||||
#[allow(unsafe_code)]
|
||||
let identifier_node = unsafe { std::mem::transmute::<_, tree_sitter::Node>(identifier.node()) };
|
||||
match String::from(declaration.name()).as_str() {
|
||||
"function_declaration" => {
|
||||
symbols.push(make_symbol(
|
||||
uri,
|
||||
content,
|
||||
declaration_node,
|
||||
identifier_node,
|
||||
lsp::SymbolKind::FUNCTION,
|
||||
));
|
||||
},
|
||||
"lexical_declaration" => {
|
||||
symbols.push(make_symbol(
|
||||
uri,
|
||||
content,
|
||||
declaration_node,
|
||||
identifier_node,
|
||||
lsp::SymbolKind::VARIABLE,
|
||||
));
|
||||
},
|
||||
"variable_declaration" => {
|
||||
symbols.push(make_symbol(
|
||||
uri,
|
||||
content,
|
||||
declaration_node,
|
||||
identifier_node,
|
||||
lsp::SymbolKind::VARIABLE,
|
||||
));
|
||||
},
|
||||
"class_declaration" => {
|
||||
symbols.push(make_symbol(
|
||||
uri,
|
||||
content,
|
||||
declaration_node,
|
||||
identifier_node,
|
||||
lsp::SymbolKind::VARIABLE,
|
||||
));
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Some(lsp::DocumentSymbolResponse::Flat(symbols)))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,46 +81,8 @@ impl LanguageServer for Server {
|
|||
params: lsp::DocumentSymbolParams,
|
||||
) -> jsonrpc::Result<Option<lsp::DocumentSymbolResponse>> {
|
||||
web_sys::console::log_1(&"server::document_symbol".into());
|
||||
let _params = params;
|
||||
let _session = self.session.clone();
|
||||
let uri = lsp::Url::parse("inmemory://model.fs").expect("failed to parse url");
|
||||
Ok(Some(lsp::DocumentSymbolResponse::Flat(vec![
|
||||
#[allow(deprecated)]
|
||||
lsp::SymbolInformation {
|
||||
name: "foo".into(),
|
||||
kind: lsp::SymbolKind::FUNCTION,
|
||||
tags: Default::default(),
|
||||
deprecated: Default::default(),
|
||||
location: lsp::Location {
|
||||
range: lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(1, 1)),
|
||||
uri: uri.clone(),
|
||||
},
|
||||
container_name: Default::default(),
|
||||
},
|
||||
#[allow(deprecated)]
|
||||
lsp::SymbolInformation {
|
||||
name: "bar".into(),
|
||||
kind: lsp::SymbolKind::FUNCTION,
|
||||
tags: Default::default(),
|
||||
deprecated: Default::default(),
|
||||
location: lsp::Location {
|
||||
range: lsp::Range::new(lsp::Position::new(2, 0), lsp::Position::new(3, 1)),
|
||||
uri: uri.clone(),
|
||||
},
|
||||
container_name: Default::default(),
|
||||
},
|
||||
#[allow(deprecated)]
|
||||
lsp::SymbolInformation {
|
||||
name: "baz".into(),
|
||||
kind: lsp::SymbolKind::FUNCTION,
|
||||
tags: Default::default(),
|
||||
deprecated: Default::default(),
|
||||
location: lsp::Location {
|
||||
range: lsp::Range::new(lsp::Position::new(4, 0), lsp::Position::new(5, 1)),
|
||||
uri: uri.clone(),
|
||||
},
|
||||
container_name: Default::default(),
|
||||
},
|
||||
])))
|
||||
let session = self.session.clone();
|
||||
let result = crate::handler::text_document::document_symbol(session, params).await;
|
||||
Ok(result.map_err(crate::core::IntoJsonRpcError)?)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,10 +35,9 @@ export default class App {
|
|||
const value = `
|
||||
function foo() {
|
||||
}
|
||||
function bar() {
|
||||
}
|
||||
function baz() {
|
||||
}
|
||||
const bar = 42;
|
||||
var baz;
|
||||
class Qux {}
|
||||
`.replace(/^\s*\n/gm, "");
|
||||
const id = language.id;
|
||||
const uri = monaco.Uri.parse("inmemory://demo.js");
|
||||
|
|
Loading…
Reference in a new issue