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"
|
dashmap = "5.3.4"
|
||||||
demo-lsp-language = { version = "0.0", path = "../language" }
|
demo-lsp-language = { version = "0.0", path = "../language" }
|
||||||
futures = "0.3.21"
|
futures = "0.3.21"
|
||||||
|
indoc = "1.0"
|
||||||
js-sys = "0.3.57"
|
js-sys = "0.3.57"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
lsp = { version = "0.93", package = "lsp-types" }
|
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 = "0.2.80"
|
||||||
wasm-bindgen-futures = { version = "0.4.30", features = ["futures-core-03-stream"] }
|
wasm-bindgen-futures = { version = "0.4.30", features = ["futures-core-03-stream"] }
|
||||||
wasm-streams = "0.2.3"
|
wasm-streams = "0.2.3"
|
||||||
|
web-tree-sitter-sys = "0.6"
|
||||||
|
|
||||||
[dependencies.web-sys]
|
[dependencies.web-sys]
|
||||||
version = "0.3.57"
|
version = "0.3.57"
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
pub mod text_document {
|
pub mod text_document {
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use lsp_text::RopeExt;
|
||||||
|
|
||||||
pub async fn did_open(
|
pub async fn did_open(
|
||||||
session: Arc<crate::core::Session>,
|
session: Arc<crate::core::Session>,
|
||||||
params: lsp::DidOpenTextDocumentParams,
|
params: lsp::DidOpenTextDocumentParams,
|
||||||
|
@ -38,4 +40,129 @@ pub mod text_document {
|
||||||
session.client()?.publish_diagnostics(uri, diagnostics, version).await;
|
session.client()?.publish_diagnostics(uri, diagnostics, version).await;
|
||||||
Ok(())
|
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,
|
params: lsp::DocumentSymbolParams,
|
||||||
) -> jsonrpc::Result<Option<lsp::DocumentSymbolResponse>> {
|
) -> jsonrpc::Result<Option<lsp::DocumentSymbolResponse>> {
|
||||||
web_sys::console::log_1(&"server::document_symbol".into());
|
web_sys::console::log_1(&"server::document_symbol".into());
|
||||||
let _params = params;
|
let session = self.session.clone();
|
||||||
let _session = self.session.clone();
|
let result = crate::handler::text_document::document_symbol(session, params).await;
|
||||||
let uri = lsp::Url::parse("inmemory://model.fs").expect("failed to parse url");
|
Ok(result.map_err(crate::core::IntoJsonRpcError)?)
|
||||||
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(),
|
|
||||||
},
|
|
||||||
])))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,10 +35,9 @@ export default class App {
|
||||||
const value = `
|
const value = `
|
||||||
function foo() {
|
function foo() {
|
||||||
}
|
}
|
||||||
function bar() {
|
const bar = 42;
|
||||||
}
|
var baz;
|
||||||
function baz() {
|
class Qux {}
|
||||||
}
|
|
||||||
`.replace(/^\s*\n/gm, "");
|
`.replace(/^\s*\n/gm, "");
|
||||||
const id = language.id;
|
const id = language.id;
|
||||||
const uri = monaco.Uri.parse("inmemory://demo.js");
|
const uri = monaco.Uri.parse("inmemory://demo.js");
|
||||||
|
|
Loading…
Reference in a new issue