Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support completions from import statements #4768

Merged
merged 1 commit into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/wasm-lib/kcl/src/lsp/kcl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1216,7 +1216,7 @@ impl LanguageServer for Backend {
return Ok(None);
}

// Get the completion items forem the ast.
// Get the completion items for the ast.
let Ok(variables) = ast.completion_items() else {
return Ok(Some(CompletionResponse::Array(completions)));
};
Expand Down
53 changes: 53 additions & 0 deletions src/wasm-lib/kcl/src/lsp/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -822,6 +822,59 @@ async fn test_kcl_lsp_completions_const_raw() {
}
}

#[tokio::test(flavor = "multi_thread")]
async fn test_kcl_lsp_completions_import() {
let server = kcl_lsp_server(false).await.unwrap();

// Send open file.
server
.did_open(tower_lsp::lsp_types::DidOpenTextDocumentParams {
text_document: tower_lsp::lsp_types::TextDocumentItem {
uri: "file:///test.kcl".try_into().unwrap(),
language_id: "kcl".to_string(),
version: 1,
text: r#"import boo, baz as bux from 'bar.kcl'
//import 'bar.kcl'
x = b"#
.to_string(),
},
})
.await;

// Send completion request.
let completions = server
.completion(tower_lsp::lsp_types::CompletionParams {
text_document_position: tower_lsp::lsp_types::TextDocumentPositionParams {
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
},
position: tower_lsp::lsp_types::Position { line: 2, character: 5 },
},
context: None,
partial_result_params: Default::default(),
work_done_progress_params: Default::default(),
})
.await
.unwrap()
.unwrap();

// Check the completions.
if let tower_lsp::lsp_types::CompletionResponse::Array(completions) = completions {
assert!(completions.len() > 10);
// Find the one with label "foo".
completions.iter().find(|completion| completion.label == "boo").unwrap();
// completions
// .iter()
// .find(|completion| completion.label == "bar")
// .unwrap();
completions.iter().find(|completion| completion.label == "bux").unwrap();
assert!(!completions.iter().any(|completion| completion.label == "baz"));
// Find the one with label "bar".
} else {
panic!("Expected array of completions");
}
}

#[tokio::test(flavor = "multi_thread")]
async fn test_kcl_lsp_on_hover() {
let server = kcl_lsp_server(false).await.unwrap();
Expand Down
96 changes: 60 additions & 36 deletions src/wasm-lib/kcl/src/parsing/ast/types/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
//! Data types for the AST.

use std::{
cell::RefCell,
collections::HashMap,
fmt,
ops::{Deref, DerefMut, RangeInclusive},
rc::Rc,
sync::{Arc, Mutex},
};

Expand Down Expand Up @@ -183,21 +185,24 @@
impl Node<Program> {
/// Walk the ast and get all the variables and tags as completion items.
pub fn completion_items<'a>(&'a self) -> Result<Vec<CompletionItem>> {
let completions = Arc::new(Mutex::new(vec![]));
let completions = Rc::new(RefCell::new(vec![]));
crate::walk::walk(self, |node: crate::walk::Node<'a>| {
let mut findings = completions.lock().map_err(|_| anyhow::anyhow!("mutex"))?;
let mut findings = completions.borrow_mut();
match node {
crate::walk::Node::TagDeclarator(tag) => {
findings.push(tag.into());
}
crate::walk::Node::VariableDeclaration(variable) => {
findings.extend::<Vec<CompletionItem>>(variable.into());
findings.extend::<Vec<CompletionItem>>((&variable.inner).into());
}
crate::walk::Node::ImportStatement(i) => {
findings.extend::<Vec<CompletionItem>>((&i.inner).into());
}
_ => {}
}
Ok::<bool, anyhow::Error>(true)
})?;
let x = completions.lock().unwrap();
let x = completions.take();
Ok(x.clone())
}

Expand Down Expand Up @@ -1300,6 +1305,22 @@
}
}

pub fn get_constraint_level(&self) -> ConstraintLevel {
ConstraintLevel::Full {
source_ranges: vec![self.into()],
}
}

Check warning on line 1312 in src/wasm-lib/kcl/src/parsing/ast/types/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/wasm-lib/kcl/src/parsing/ast/types/mod.rs#L1308-L1312

Added lines #L1308 - L1312 were not covered by tests

pub fn rename_symbol(&mut self, new_name: &str, pos: usize) -> Option<String> {
self.selector.rename_symbol(new_name, pos)
}

Check warning on line 1316 in src/wasm-lib/kcl/src/parsing/ast/types/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/wasm-lib/kcl/src/parsing/ast/types/mod.rs#L1314-L1316

Added lines #L1314 - L1316 were not covered by tests
}

impl ImportStatement {
pub fn rename_identifiers(&mut self, old_name: &str, new_name: &str) {
self.selector.rename_identifiers(old_name, new_name);
}

Check warning on line 1322 in src/wasm-lib/kcl/src/parsing/ast/types/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/wasm-lib/kcl/src/parsing/ast/types/mod.rs#L1320-L1322

Added lines #L1320 - L1322 were not covered by tests

/// Get the name of the module object for this import.
/// Validated during parsing and guaranteed to return `Some` if the statement imports
/// the module itself (i.e., self.selector is ImportSelector::None).
Expand All @@ -1319,21 +1340,38 @@

Some(name.to_owned())
}

pub fn get_constraint_level(&self) -> ConstraintLevel {
ConstraintLevel::Full {
source_ranges: vec![self.into()],
}
}

pub fn rename_symbol(&mut self, new_name: &str, pos: usize) -> Option<String> {
self.selector.rename_symbol(new_name, pos)
}
}

impl ImportStatement {
pub fn rename_identifiers(&mut self, old_name: &str, new_name: &str) {
self.selector.rename_identifiers(old_name, new_name);
impl From<&ImportStatement> for Vec<CompletionItem> {
fn from(import: &ImportStatement) -> Self {
match &import.selector {
ImportSelector::List { items } => {
items
.iter()
.map(|i| {
let as_str = match &i.alias {
Some(s) => format!(" as {}", s.name),
None => String::new(),
};
CompletionItem {
label: i.identifier().to_owned(),
// TODO we can only find this after opening the module
kind: None,
detail: Some(format!("{}{as_str} from '{}'", i.name.name, import.path)),
..CompletionItem::default()
}
})
.collect()
}
// TODO can't do completion for glob imports without static name resolution
ImportSelector::Glob(_) => vec![],
ImportSelector::None { .. } => vec![CompletionItem {
label: import.module_name().unwrap(),
kind: Some(CompletionItemKind::MODULE),
detail: Some(format!("from '{}'", import.path)),
..CompletionItem::default()
}],

Check warning on line 1373 in src/wasm-lib/kcl/src/parsing/ast/types/mod.rs

View check run for this annotation

Codecov / codecov/patch

src/wasm-lib/kcl/src/parsing/ast/types/mod.rs#L1367-L1373

Added lines #L1367 - L1373 were not covered by tests
}
}
}

Expand Down Expand Up @@ -1605,30 +1643,16 @@
pub digest: Option<Digest>,
}

impl From<&Node<VariableDeclaration>> for Vec<CompletionItem> {
fn from(declaration: &Node<VariableDeclaration>) -> Self {
impl From<&VariableDeclaration> for Vec<CompletionItem> {
fn from(declaration: &VariableDeclaration) -> Self {
vec![CompletionItem {
label: declaration.declaration.id.name.to_string(),
label_details: None,
kind: Some(match declaration.inner.kind {
kind: Some(match declaration.kind {
VariableKind::Const => CompletionItemKind::CONSTANT,
VariableKind::Fn => CompletionItemKind::FUNCTION,
}),
detail: Some(declaration.inner.kind.to_string()),
documentation: None,
deprecated: None,
preselect: None,
sort_text: None,
filter_text: None,
insert_text: None,
insert_text_format: None,
insert_text_mode: None,
text_edit: None,
additional_text_edits: None,
command: None,
commit_characters: None,
data: None,
tags: None,
detail: Some(declaration.kind.to_string()),
..CompletionItem::default()
}]
}
}
Expand Down
Loading