Skip to content

Commit 1fee06b

Browse files
committed
Initial support for workspace symbols
1 parent 9505274 commit 1fee06b

File tree

6 files changed

+154
-74
lines changed

6 files changed

+154
-74
lines changed

compiler-bin/src/lsp.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ fn initialize(
9898
definition_provider: Some(OneOf::Left(true)),
9999
hover_provider: Some(HoverProviderCapability::Simple(true)),
100100
references_provider: Some(OneOf::Left(true)),
101+
workspace_symbol_provider: Some(OneOf::Left(true)),
101102
text_document_sync: Some(TextDocumentSyncCapability::Kind(
102103
TextDocumentSyncKind::FULL,
103104
)),
@@ -244,6 +245,13 @@ fn references(
244245
.on_non_fatal(None)
245246
}
246247

248+
fn workspace_symbols(
249+
snapshot: StateSnapshot,
250+
p: WorkspaceSymbolParams,
251+
) -> Result<Option<WorkspaceSymbolResponse>, ResponseError> {
252+
analyzer::symbols::workspace(&snapshot.engine, &snapshot.files(), &p.query).on_non_fatal(None)
253+
}
254+
247255
fn did_change(state: &mut State, p: DidChangeTextDocumentParams) -> Result<(), LspError> {
248256
let uri = p.text_document.uri.as_str();
249257

@@ -325,6 +333,7 @@ pub async fn start(config: Arc<cli::Config>) {
325333
.request_snapshot::<request::Completion>(completion)
326334
.request_snapshot::<request::ResolveCompletionItem>(resolve_completion_item)
327335
.request_snapshot::<request::References>(references)
336+
.request_snapshot::<request::WorkspaceSymbolRequest>(workspace_symbols)
328337
.notification::<notification::Initialized>(adapt_notification(initialized))
329338
.notification::<notification::DidOpenTextDocument>(|_, _| ControlFlow::Continue(()))
330339
.notification::<notification::DidSaveTextDocument>(|_, _| ControlFlow::Continue(()))
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use async_lsp::lsp_types::*;
2+
use building::{QueryEngine, prim};
3+
use files::{FileId, Files};
4+
use indexing::{TermItemId, TypeItemId};
5+
use syntax::{SyntaxNode, SyntaxNodePtr};
6+
7+
use crate::{AnalyzerError, locate};
8+
9+
pub fn file_term_location(
10+
engine: &QueryEngine,
11+
files: &Files,
12+
file_id: FileId,
13+
term_id: TermItemId,
14+
) -> Result<Location, AnalyzerError> {
15+
let uri = file_uri(engine, files, file_id)?;
16+
17+
let content = engine.content(file_id);
18+
let (parsed, _) = engine.parsed(file_id)?;
19+
20+
let stabilized = engine.stabilized(file_id)?;
21+
let indexed = engine.indexed(file_id)?;
22+
23+
let root = parsed.syntax_node();
24+
let pointers = indexed.term_item_ptr(&stabilized, term_id);
25+
26+
let range = pointers_range(&content, root, pointers)?;
27+
Ok(Location { uri, range })
28+
}
29+
30+
pub fn file_type_location(
31+
engine: &QueryEngine,
32+
files: &Files,
33+
file_id: FileId,
34+
type_id: TypeItemId,
35+
) -> Result<Location, AnalyzerError> {
36+
let uri = file_uri(engine, files, file_id)?;
37+
38+
let content = engine.content(file_id);
39+
let (parsed, _) = engine.parsed(file_id)?;
40+
41+
let stabilized = engine.stabilized(file_id)?;
42+
let indexed = engine.indexed(file_id)?;
43+
44+
let root = parsed.syntax_node();
45+
let pointers = indexed.type_item_ptr(&stabilized, type_id);
46+
47+
let range = pointers_range(&content, root, pointers)?;
48+
49+
Ok(Location { uri, range })
50+
}
51+
52+
pub fn file_uri(
53+
engine: &QueryEngine,
54+
files: &Files,
55+
file_id: FileId,
56+
) -> Result<Url, AnalyzerError> {
57+
let path = files.path(file_id);
58+
let content = engine.content(file_id);
59+
60+
let uri = Url::parse(&path)?;
61+
prim::handle_generated(uri, &content).ok_or(AnalyzerError::NonFatal)
62+
}
63+
64+
fn pointers_range(
65+
content: &str,
66+
root: SyntaxNode,
67+
pointers: impl Iterator<Item = SyntaxNodePtr>,
68+
) -> Result<Range, AnalyzerError> {
69+
pointers
70+
.filter_map(|ptr| locate::syntax_range(&content, &root, &ptr))
71+
.reduce(|start, end| Range { start: start.start, end: end.end })
72+
.ok_or(AnalyzerError::NonFatal)
73+
}

compiler-lsp/analyzer/src/definition.rs

Lines changed: 7 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use async_lsp::lsp_types::*;
2-
use building::{QueryEngine, prim};
2+
use building::QueryEngine;
33
use files::{FileId, Files};
44
use indexing::{ImportItemId, TermItemId, TypeItemId};
55
use lowering::{
@@ -10,7 +10,7 @@ use rowan::ast::{AstNode, AstPtr};
1010
use smol_str::ToSmolStr;
1111
use syntax::cst;
1212

13-
use crate::{AnalyzerError, locate};
13+
use crate::{AnalyzerError, common, locate};
1414

1515
pub fn implementation(
1616
engine: &QueryEngine,
@@ -77,7 +77,6 @@ fn definition_module_name(
7777
let module_name = module_name.syntax().text().to_smolstr();
7878
let module_id = engine.module_file(&module_name).ok_or(AnalyzerError::NonFatal)?;
7979

80-
let path = files.path(module_id);
8180
let content = engine.content(module_id);
8281

8382
let (parsed, _) = engine.parsed(module_id)?;
@@ -87,8 +86,7 @@ fn definition_module_name(
8786
let start = locate::offset_to_position(&content, range.start());
8887
let end = locate::offset_to_position(&content, range.end());
8988

90-
let uri = Url::parse(&path)?;
91-
let uri = prim::handle_generated(uri, &content).ok_or(AnalyzerError::NonFatal)?;
89+
let uri = common::file_uri(engine, files, module_id)?;
9290

9391
let range = Range { start, end };
9492

@@ -294,31 +292,8 @@ fn definition_file_term(
294292
file_id: FileId,
295293
term_id: TermItemId,
296294
) -> Result<Option<GotoDefinitionResponse>, AnalyzerError> {
297-
let uri = {
298-
let path = files.path(file_id);
299-
let content = files.content(file_id);
300-
301-
let uri = Url::parse(&path)?;
302-
prim::handle_generated(uri, &content).ok_or(AnalyzerError::NonFatal)?
303-
};
304-
305-
let content = engine.content(file_id);
306-
let (parsed, _) = engine.parsed(file_id)?;
307-
308-
let stabilized = engine.stabilized(file_id)?;
309-
let indexed = engine.indexed(file_id)?;
310-
311-
// TODO: Once we implement textDocument/typeDefinition, we
312-
// should probably also add a term_item_type_ptr function.
313-
let root = parsed.syntax_node();
314-
let pointers = indexed.term_item_ptr(&stabilized, term_id);
315-
316-
let range = pointers
317-
.filter_map(|ptr| locate::syntax_range(&content, &root, &ptr))
318-
.reduce(|start, end| Range { start: start.start, end: end.end })
319-
.ok_or(AnalyzerError::NonFatal)?;
320-
321-
Ok(Some(GotoDefinitionResponse::Scalar(Location { uri, range })))
295+
let location = common::file_term_location(engine, files, file_id, term_id)?;
296+
Ok(Some(GotoDefinitionResponse::Scalar(location)))
322297
}
323298

324299
fn definition_file_type(
@@ -327,27 +302,6 @@ fn definition_file_type(
327302
file_id: FileId,
328303
type_id: TypeItemId,
329304
) -> Result<Option<GotoDefinitionResponse>, AnalyzerError> {
330-
let uri = {
331-
let path = files.path(file_id);
332-
let content = files.content(file_id);
333-
334-
let uri = Url::parse(&path)?;
335-
prim::handle_generated(uri, &content).ok_or(AnalyzerError::NonFatal)?
336-
};
337-
338-
let content = engine.content(file_id);
339-
let (parsed, _) = engine.parsed(file_id)?;
340-
341-
let stabilized = engine.stabilized(file_id)?;
342-
let indexed = engine.indexed(file_id)?;
343-
344-
let root = parsed.syntax_node();
345-
let pointers = indexed.type_item_ptr(&stabilized, type_id);
346-
347-
let range = pointers
348-
.filter_map(|ptr| locate::syntax_range(&content, &root, &ptr))
349-
.reduce(|start, end| Range { start: start.start, end: end.end })
350-
.ok_or(AnalyzerError::NonFatal)?;
351-
352-
Ok(Some(GotoDefinitionResponse::Scalar(Location { uri, range })))
305+
let location = common::file_type_location(engine, files, file_id, type_id)?;
306+
Ok(Some(GotoDefinitionResponse::Scalar(location)))
353307
}

compiler-lsp/analyzer/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
pub mod common;
12
pub mod completion;
23
pub mod definition;
34
pub mod error;
45
pub mod extract;
56
pub mod hover;
67
pub mod locate;
78
pub mod references;
9+
pub mod symbols;
810

911
pub use building::{QueryEngine, QueryError, prim};
1012
pub use error::AnalyzerError;

compiler-lsp/analyzer/src/references.rs

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use async_lsp::lsp_types::*;
2-
use building::{QueryEngine, prim};
2+
use building::QueryEngine;
33
use files::{FileId, Files};
44
use indexing::{ImportId, ImportItemId, ImportKind, TermItemId, TypeItemId};
55
use lowering::{
@@ -13,7 +13,7 @@ use smol_str::ToSmolStr;
1313
use stabilizing::{AstId, StabilizedModule};
1414
use syntax::{PureScript, cst};
1515

16-
use crate::{AnalyzerError, locate};
16+
use crate::{AnalyzerError, common, locate};
1717

1818
pub fn implementation(
1919
engine: &QueryEngine,
@@ -82,12 +82,9 @@ fn references_module_name(
8282

8383
let mut locations = vec![];
8484
for (candidate_id, import_id) in candidates {
85-
let path = files.path(candidate_id);
86-
let content = files.content(candidate_id);
87-
88-
let uri = Url::parse(&path)?;
89-
let uri = prim::handle_generated(uri, &content).ok_or(AnalyzerError::NonFatal)?;
85+
let uri = common::file_uri(engine, files, candidate_id)?;
9086

87+
let content = engine.content(candidate_id);
9188
let (parsed, _) = engine.parsed(candidate_id)?;
9289
let root = parsed.syntax_node();
9390

@@ -263,13 +260,7 @@ fn references_file_term(
263260

264261
let mut locations = vec![];
265262
for candidate_id in candidates {
266-
let uri = {
267-
let path = files.path(candidate_id);
268-
let content = files.content(candidate_id);
269-
270-
let uri = Url::parse(&path)?;
271-
prim::handle_generated(uri, &content).ok_or(AnalyzerError::NonFatal)?
272-
};
263+
let uri = common::file_uri(engine, files, candidate_id)?;
273264

274265
let content = engine.content(candidate_id);
275266
let (parsed, _) = engine.parsed(candidate_id)?;
@@ -334,13 +325,7 @@ fn references_file_type(
334325

335326
let mut locations = vec![];
336327
for candidate_id in candidates {
337-
let uri = {
338-
let path = files.path(candidate_id);
339-
let content = files.content(candidate_id);
340-
341-
let uri = Url::parse(&path)?;
342-
prim::handle_generated(uri, &content).ok_or(AnalyzerError::NonFatal)?
343-
};
328+
let uri = common::file_uri(engine, files, candidate_id)?;
344329

345330
let content = engine.content(candidate_id);
346331
let (parsed, _) = engine.parsed(candidate_id)?;
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
use async_lsp::lsp_types::*;
2+
use building::QueryEngine;
3+
use files::Files;
4+
5+
use crate::{AnalyzerError, common};
6+
7+
pub fn workspace(
8+
engine: &QueryEngine,
9+
files: &Files,
10+
query: &str,
11+
) -> Result<Option<WorkspaceSymbolResponse>, AnalyzerError> {
12+
let mut symbols = vec![];
13+
14+
let allow_query = |name: &str| query.is_empty() || name.starts_with(query);
15+
16+
for file_id in files.iter_id() {
17+
let resolved = engine.resolved(file_id)?;
18+
19+
let terms = resolved
20+
.locals
21+
.iter_terms()
22+
.filter_map(|(name, _, id)| if allow_query(&name) { Some((name, id)) } else { None });
23+
24+
let types = resolved
25+
.locals
26+
.iter_types()
27+
.filter_map(|(name, _, id)| if allow_query(&name) { Some((name, id)) } else { None });
28+
29+
for (name, term_id) in terms {
30+
let location = common::file_term_location(engine, files, file_id, term_id)?;
31+
symbols.push(SymbolInformation {
32+
name: name.to_string(),
33+
kind: SymbolKind::FUNCTION,
34+
tags: None,
35+
#[allow(deprecated)]
36+
deprecated: None,
37+
location,
38+
container_name: None,
39+
})
40+
}
41+
42+
for (name, type_id) in types {
43+
let location = common::file_type_location(engine, files, file_id, type_id)?;
44+
symbols.push(SymbolInformation {
45+
name: name.to_string(),
46+
kind: SymbolKind::CLASS,
47+
tags: None,
48+
#[allow(deprecated)]
49+
deprecated: None,
50+
location,
51+
container_name: None,
52+
})
53+
}
54+
}
55+
56+
Ok(Some(WorkspaceSymbolResponse::Flat(symbols)))
57+
}

0 commit comments

Comments
 (0)