mirror of
https://github.com/nushell/nushell.git
synced 2025-05-30 22:57:07 +02:00
<!-- if this PR closes one or more issues, you can automatically link the PR with them by using one of the [*linking keywords*](https://docs.github.com/en/issues/tracking-your-work-with-issues/linking-a-pull-request-to-an-issue#linking-a-pull-request-to-an-issue-using-a-keyword), e.g. - this PR should close #xxxx - fixes #xxxx you can also mention related issues, PRs or discussions! --> # Description <!-- Thank you for improving Nushell. Please, check our [contributing guide](../CONTRIBUTING.md) and talk to the core team before making major changes. Description of your pull request goes here. **Provide examples and/or screenshots** if your changes affect the user experience. --> This PR adds inlay hints of variable types and parameter names to lsp-server <img width="547" alt="image" src="https://github.com/user-attachments/assets/07a0dd84-5ecc-47df-a8a7-732631715662" /> Some design choices I made: * for composite types like `record<foo: <record ...>>`, only a short name displayed. Full signature already available through `hover` * only parameter names of user defined commands are returned, feels too much distraction if enabled for all builtins * some information are lost in flattened expressions, so I implemented my AST traversing functions, which may seem unnecessary, but I can't find alternatives from the existing code. * another minor change: added a line separator to current hover markdown message. # User-Facing Changes <!-- List of all changes that impact the user experience here. This helps us keep track of breaking changes. --> Users who think this feature annoying now have to manually turn it off (or config the lsp client capabilities). # Tests + Formatting <!-- Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: - `cargo fmt --all -- --check` to check standard code formatting (`cargo fmt --all` applies these changes) - `cargo clippy --workspace -- -D warnings -D clippy::unwrap_used` to check that you're using the standard code style - `cargo test --workspace` to check that all tests pass (on Windows make sure to [enable developer mode](https://learn.microsoft.com/en-us/windows/apps/get-started/developer-mode-features-and-debugging)) - `cargo run -- -c "use toolkit.nu; toolkit test stdlib"` to run the tests for the standard library > **Note** > from `nushell` you can also use the `toolkit` as follows > ```bash > use toolkit.nu # or use an `env_change` hook to activate it automatically > toolkit check pr > ``` --> # After Submitting <!-- If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date. -->
187 lines
6.4 KiB
Rust
187 lines
6.4 KiB
Rust
use lsp_types::{
|
|
notification::{
|
|
DidChangeTextDocument, DidCloseTextDocument, DidOpenTextDocument, Notification,
|
|
},
|
|
DidChangeTextDocumentParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams, Uri,
|
|
};
|
|
|
|
use crate::LanguageServer;
|
|
|
|
impl LanguageServer {
|
|
pub(crate) fn handle_lsp_notification(
|
|
&mut self,
|
|
notification: lsp_server::Notification,
|
|
) -> Option<Uri> {
|
|
self.docs
|
|
.listen(notification.method.as_str(), ¬ification.params);
|
|
match notification.method.as_str() {
|
|
DidOpenTextDocument::METHOD => {
|
|
let params: DidOpenTextDocumentParams =
|
|
serde_json::from_value(notification.params.clone())
|
|
.expect("Expect receive DidOpenTextDocumentParams");
|
|
Some(params.text_document.uri)
|
|
}
|
|
DidChangeTextDocument::METHOD => {
|
|
let params: DidChangeTextDocumentParams =
|
|
serde_json::from_value(notification.params.clone())
|
|
.expect("Expect receive DidChangeTextDocumentParams");
|
|
Some(params.text_document.uri)
|
|
}
|
|
DidCloseTextDocument::METHOD => {
|
|
let params: DidCloseTextDocumentParams =
|
|
serde_json::from_value(notification.params.clone())
|
|
.expect("Expect receive DidCloseTextDocumentParams");
|
|
let uri = params.text_document.uri;
|
|
self.symbol_cache.drop(&uri);
|
|
self.inlay_hints.remove(&uri);
|
|
None
|
|
}
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use assert_json_diff::assert_json_eq;
|
|
use lsp_server::Message;
|
|
use lsp_types::Range;
|
|
use nu_test_support::fs::fixtures;
|
|
|
|
use crate::path_to_uri;
|
|
use crate::tests::{
|
|
initialize_language_server, open, open_unchecked, send_hover_request, update,
|
|
};
|
|
|
|
#[test]
|
|
fn hover_correct_documentation_on_let() {
|
|
let (client_connection, _recv) = initialize_language_server();
|
|
|
|
let mut script = fixtures();
|
|
script.push("lsp");
|
|
script.push("hover");
|
|
script.push("var.nu");
|
|
let script = path_to_uri(&script);
|
|
|
|
open_unchecked(&client_connection, script.clone());
|
|
|
|
let resp = send_hover_request(&client_connection, script.clone(), 0, 0);
|
|
let result = if let Message::Response(response) = resp {
|
|
response.result
|
|
} else {
|
|
panic!()
|
|
};
|
|
|
|
assert_json_eq!(
|
|
result,
|
|
serde_json::json!({
|
|
"contents": {
|
|
"kind": "markdown",
|
|
"value": "Create a variable and give it a value.\n\nThis command is a parser keyword. For details, check:\n https://www.nushell.sh/book/thinking_in_nu.html\n-----\n### Usage \n```nu\n let {flags} <var_name> <initial_value>\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n\n### Parameters\n\n `var_name: any` - Variable name.\n\n `initial_value: any` - Equals sign followed by value.\n\n\n### Input/output types\n\n```nu\n any | nothing\n\n```\n### Example(s)\n Set a variable to a value\n```nu\n let x = 10\n```\n Set a variable to the result of an expression\n```nu\n let x = 10 + 100\n```\n Set a variable based on the condition\n```nu\n let x = if false { -1 } else { 1 }\n```\n"
|
|
}
|
|
})
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn hover_on_command_after_full_content_change() {
|
|
let (client_connection, _recv) = initialize_language_server();
|
|
|
|
let mut script = fixtures();
|
|
script.push("lsp");
|
|
script.push("hover");
|
|
script.push("command.nu");
|
|
let script = path_to_uri(&script);
|
|
|
|
open_unchecked(&client_connection, script.clone());
|
|
update(
|
|
&client_connection,
|
|
script.clone(),
|
|
String::from(
|
|
r#"# Renders some updated greeting message
|
|
def hello [] {}
|
|
|
|
hello"#,
|
|
),
|
|
None,
|
|
);
|
|
|
|
let resp = send_hover_request(&client_connection, script.clone(), 3, 0);
|
|
let result = if let Message::Response(response) = resp {
|
|
response.result
|
|
} else {
|
|
panic!()
|
|
};
|
|
|
|
assert_json_eq!(
|
|
result,
|
|
serde_json::json!({
|
|
"contents": {
|
|
"kind": "markdown",
|
|
"value": "Renders some updated greeting message\n-----\n### Usage \n```nu\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n"
|
|
}
|
|
})
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn hover_on_command_after_partial_content_change() {
|
|
let (client_connection, _recv) = initialize_language_server();
|
|
|
|
let mut script = fixtures();
|
|
script.push("lsp");
|
|
script.push("hover");
|
|
script.push("command.nu");
|
|
let script = path_to_uri(&script);
|
|
|
|
open_unchecked(&client_connection, script.clone());
|
|
update(
|
|
&client_connection,
|
|
script.clone(),
|
|
String::from("# Renders some updated greeting message"),
|
|
Some(Range {
|
|
start: lsp_types::Position {
|
|
line: 0,
|
|
character: 0,
|
|
},
|
|
end: lsp_types::Position {
|
|
line: 0,
|
|
character: 31,
|
|
},
|
|
}),
|
|
);
|
|
|
|
let resp = send_hover_request(&client_connection, script.clone(), 3, 0);
|
|
let result = if let Message::Response(response) = resp {
|
|
response.result
|
|
} else {
|
|
panic!()
|
|
};
|
|
|
|
assert_json_eq!(
|
|
result,
|
|
serde_json::json!({
|
|
"contents": {
|
|
"kind": "markdown",
|
|
"value": "Renders some updated greeting message\n-----\n### Usage \n```nu\n hello {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n"
|
|
}
|
|
})
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn open_document_with_utf_char() {
|
|
let (client_connection, _recv) = initialize_language_server();
|
|
|
|
let mut script = fixtures();
|
|
script.push("lsp");
|
|
script.push("notifications");
|
|
script.push("issue_11522.nu");
|
|
let script = path_to_uri(&script);
|
|
|
|
let result = open(&client_connection, script);
|
|
|
|
assert_eq!(result.map(|_| ()), Ok(()))
|
|
}
|
|
}
|