WIP: workspace/configuration
This commit is contained in:
parent
2f8e397365
commit
67b70888b9
77
crates/nu-lsp/src/configuration.rs
Normal file
77
crates/nu-lsp/src/configuration.rs
Normal file
|
@ -0,0 +1,77 @@
|
|||
use crate::LanguageServer;
|
||||
use nu_cli::eval_config_contents;
|
||||
use nu_protocol::engine::EngineState;
|
||||
use serde::Deserialize;
|
||||
use std::path::PathBuf;
|
||||
|
||||
pub(crate) const CONFIG_REQUEST_ID: &'static str = "config";
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct NuConfiguration {
|
||||
include_paths: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
impl LanguageServer {
|
||||
pub(crate) fn configure_engine(engine_state: &mut EngineState, config: serde_json::Value) {
|
||||
let Ok(config) = serde_json::from_value::<NuConfiguration>(config) else {
|
||||
// TODO: warn the client? Whet does the spec recommend?
|
||||
return;
|
||||
};
|
||||
|
||||
for config_path in config.include_paths {
|
||||
let mut stack = nu_protocol::engine::Stack::new();
|
||||
eval_config_contents(config_path, engine_state, &mut stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::{complete, initialize_language_server, open_unchecked};
|
||||
use assert_json_diff::assert_json_include;
|
||||
use lsp_server::Message;
|
||||
use lsp_types::Url;
|
||||
use nu_test_support::fs::fixtures;
|
||||
|
||||
#[test]
|
||||
fn complete_with_include_paths() {
|
||||
let mut include_path = fixtures();
|
||||
include_path.push("formats");
|
||||
include_path.push("sample_def.nu");
|
||||
|
||||
let (client_connection, _recv) = initialize_language_server(Some(serde_json::json!({
|
||||
"includePaths": [
|
||||
include_path
|
||||
]
|
||||
})));
|
||||
|
||||
let mut script = fixtures();
|
||||
script.push("lsp");
|
||||
script.push("completion");
|
||||
script.push("include.nu");
|
||||
let script = Url::from_file_path(script).unwrap();
|
||||
|
||||
open_unchecked(&client_connection, script.clone());
|
||||
|
||||
let Message::Response(resp) = complete(&client_connection, script, 0, 3) else {
|
||||
panic!()
|
||||
};
|
||||
|
||||
assert_json_include!(
|
||||
actual: resp.result,
|
||||
expected: serde_json::json!([
|
||||
{
|
||||
"label": "greet",
|
||||
"textEdit": {
|
||||
"newText": "greet",
|
||||
"range": {
|
||||
"start": { "character": 0, "line": 0 },
|
||||
"end": { "character": 3, "line": 0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
|
@ -76,7 +76,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn publish_diagnostics_variable_does_not_exists() {
|
||||
let (client_connection, _recv) = initialize_language_server();
|
||||
let (client_connection, _recv) = initialize_language_server(None);
|
||||
|
||||
let mut script = fixtures();
|
||||
script.push("lsp");
|
||||
|
@ -107,7 +107,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn publish_diagnostics_fixed_unknown_variable() {
|
||||
let (client_connection, _recv) = initialize_language_server();
|
||||
let (client_connection, _recv) = initialize_language_server(None);
|
||||
|
||||
let mut script = fixtures();
|
||||
script.push("lsp");
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use lsp_server::{Connection, IoThreads, Message, Response, ResponseError};
|
||||
use lsp_server::{Connection, IoThreads, Message, RequestId, Response, ResponseError};
|
||||
use lsp_types::{
|
||||
request::{Completion, GotoDefinition, HoverRequest, Request},
|
||||
request::{Completion, GotoDefinition, HoverRequest, Request, WorkspaceConfiguration},
|
||||
CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse, CompletionTextEdit,
|
||||
GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverContents, HoverParams, Location,
|
||||
MarkupContent, MarkupKind, OneOf, Range, ServerCapabilities, TextDocumentSyncKind, TextEdit,
|
||||
Url,
|
||||
GotoDefinitionParams, GotoDefinitionResponse, Hover, HoverContents, HoverParams,
|
||||
InitializeParams, Location, MarkupContent, MarkupKind, OneOf, Range, ServerCapabilities,
|
||||
TextDocumentSyncKind, TextEdit, Url,
|
||||
};
|
||||
use miette::{IntoDiagnostic, Result};
|
||||
use nu_cli::{NuCompleter, SuggestionKind};
|
||||
|
@ -24,6 +24,7 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
mod configuration;
|
||||
mod diagnostics;
|
||||
mod notification;
|
||||
|
||||
|
@ -73,11 +74,33 @@ impl LanguageServer {
|
|||
})
|
||||
.expect("Must be serializable");
|
||||
|
||||
let _initialization_params = self
|
||||
.connection
|
||||
.initialize_while(server_capabilities, || !ctrlc.load(Ordering::SeqCst))
|
||||
.into_diagnostic()?;
|
||||
let initialize_params: InitializeParams = serde_json::from_value(
|
||||
self.connection
|
||||
.initialize_while(server_capabilities, || !ctrlc.load(Ordering::SeqCst))
|
||||
.into_diagnostic()?,
|
||||
)
|
||||
.into_diagnostic()?;
|
||||
|
||||
if initialize_params
|
||||
.capabilities
|
||||
.workspace
|
||||
.as_ref()
|
||||
.and_then(|workspace| workspace.configuration)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
self.connection
|
||||
.sender
|
||||
.send(lsp_server::Message::Request(lsp_server::Request {
|
||||
id: RequestId::from(configuration::CONFIG_REQUEST_ID.to_string()),
|
||||
method: WorkspaceConfiguration::METHOD.to_string(),
|
||||
params: serde_json::json!({
|
||||
"items": [{ "section": "nu" }]
|
||||
}),
|
||||
}))
|
||||
.into_diagnostic()?;
|
||||
}
|
||||
|
||||
let mut engine_state = engine_state;
|
||||
while !ctrlc.load(Ordering::SeqCst) {
|
||||
let msg = match self
|
||||
.connection
|
||||
|
@ -128,7 +151,14 @@ impl LanguageServer {
|
|||
.send(Message::Response(resp))
|
||||
.into_diagnostic()?;
|
||||
}
|
||||
Message::Response(_) => {}
|
||||
Message::Response(response) => {
|
||||
if response.id == RequestId::from(configuration::CONFIG_REQUEST_ID.to_string())
|
||||
{
|
||||
if let Some(config) = response.result {
|
||||
Self::configure_engine(&mut engine_state, config)
|
||||
}
|
||||
}
|
||||
}
|
||||
Message::Notification(notification) => {
|
||||
if let Some(updated_file) = self.handle_lsp_notification(notification) {
|
||||
let mut engine_state = engine_state.clone();
|
||||
|
@ -622,7 +652,9 @@ mod tests {
|
|||
use nu_test_support::fs::{fixtures, root};
|
||||
use std::sync::mpsc::Receiver;
|
||||
|
||||
pub fn initialize_language_server() -> (Connection, Receiver<Result<()>>) {
|
||||
pub fn initialize_language_server(
|
||||
config: Option<serde_json::Value>,
|
||||
) -> (Connection, Receiver<Result<()>>) {
|
||||
use std::sync::mpsc;
|
||||
let (client_connection, server_connection) = Connection::memory();
|
||||
let lsp_server = LanguageServer::initialize_connection(server_connection, None).unwrap();
|
||||
|
@ -630,7 +662,14 @@ mod tests {
|
|||
let (send, recv) = mpsc::channel();
|
||||
std::thread::spawn(move || {
|
||||
let engine_state = nu_cmd_lang::create_default_context();
|
||||
let engine_state = nu_command::add_shell_command_context(engine_state);
|
||||
let mut engine_state = nu_command::add_shell_command_context(engine_state);
|
||||
|
||||
let cwd = std::env::current_dir().expect("Could not get current working directory.");
|
||||
engine_state.add_env_var(
|
||||
"PWD".into(),
|
||||
nu_protocol::Value::test_string(cwd.to_string_lossy()),
|
||||
);
|
||||
|
||||
send.send(lsp_server.serve_requests(engine_state, Arc::new(AtomicBool::new(false))))
|
||||
});
|
||||
|
||||
|
@ -640,6 +679,13 @@ mod tests {
|
|||
id: 1.into(),
|
||||
method: Initialize::METHOD.to_string(),
|
||||
params: serde_json::to_value(InitializeParams {
|
||||
capabilities: lsp_types::ClientCapabilities {
|
||||
workspace: Some(lsp_types::WorkspaceClientCapabilities {
|
||||
configuration: Some(true),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.unwrap(),
|
||||
|
@ -658,12 +704,34 @@ mod tests {
|
|||
.recv_timeout(std::time::Duration::from_secs(2))
|
||||
.unwrap();
|
||||
|
||||
let configuration_response = client_connection
|
||||
.receiver
|
||||
.recv_timeout(std::time::Duration::from_secs(2))
|
||||
.unwrap();
|
||||
let Message::Request(request) = configuration_response else {
|
||||
panic!()
|
||||
};
|
||||
|
||||
assert_eq!(request.method, WorkspaceConfiguration::METHOD);
|
||||
|
||||
client_connection
|
||||
.sender
|
||||
.send_timeout(
|
||||
Message::Response(lsp_server::Response {
|
||||
id: request.id,
|
||||
result: config,
|
||||
error: None,
|
||||
}),
|
||||
std::time::Duration::from_secs(2),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
(client_connection, recv)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn shutdown_on_request() {
|
||||
let (client_connection, recv) = initialize_language_server();
|
||||
let (client_connection, recv) = initialize_language_server(None);
|
||||
|
||||
client_connection
|
||||
.sender
|
||||
|
@ -689,7 +757,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn goto_definition_for_none_existing_file() {
|
||||
let (client_connection, _recv) = initialize_language_server();
|
||||
let (client_connection, _recv) = initialize_language_server(None);
|
||||
|
||||
let mut none_existent_path = root();
|
||||
none_existent_path.push("none-existent.nu");
|
||||
|
@ -838,7 +906,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn goto_definition_of_variable() {
|
||||
let (client_connection, _recv) = initialize_language_server();
|
||||
let (client_connection, _recv) = initialize_language_server(None);
|
||||
|
||||
let mut script = fixtures();
|
||||
script.push("lsp");
|
||||
|
@ -869,7 +937,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn goto_definition_of_command() {
|
||||
let (client_connection, _recv) = initialize_language_server();
|
||||
let (client_connection, _recv) = initialize_language_server(None);
|
||||
|
||||
let mut script = fixtures();
|
||||
script.push("lsp");
|
||||
|
@ -900,7 +968,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn goto_definition_of_command_parameter() {
|
||||
let (client_connection, _recv) = initialize_language_server();
|
||||
let (client_connection, _recv) = initialize_language_server(None);
|
||||
|
||||
let mut script = fixtures();
|
||||
script.push("lsp");
|
||||
|
@ -954,7 +1022,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn hover_on_variable() {
|
||||
let (client_connection, _recv) = initialize_language_server();
|
||||
let (client_connection, _recv) = initialize_language_server(None);
|
||||
|
||||
let mut script = fixtures();
|
||||
script.push("lsp");
|
||||
|
@ -981,7 +1049,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn hover_on_custom_command() {
|
||||
let (client_connection, _recv) = initialize_language_server();
|
||||
let (client_connection, _recv) = initialize_language_server(None);
|
||||
|
||||
let mut script = fixtures();
|
||||
script.push("lsp");
|
||||
|
@ -1011,7 +1079,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn hover_on_str_join() {
|
||||
let (client_connection, _recv) = initialize_language_server();
|
||||
let (client_connection, _recv) = initialize_language_server(None);
|
||||
|
||||
let mut script = fixtures();
|
||||
script.push("lsp");
|
||||
|
@ -1039,7 +1107,12 @@ mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
fn complete(client_connection: &Connection, uri: Url, line: u32, character: u32) -> Message {
|
||||
pub(crate) fn complete(
|
||||
client_connection: &Connection,
|
||||
uri: Url,
|
||||
line: u32,
|
||||
character: u32,
|
||||
) -> Message {
|
||||
client_connection
|
||||
.sender
|
||||
.send(Message::Request(lsp_server::Request {
|
||||
|
@ -1066,7 +1139,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn complete_on_variable() {
|
||||
let (client_connection, _recv) = initialize_language_server();
|
||||
let (client_connection, _recv) = initialize_language_server(None);
|
||||
|
||||
let mut script = fixtures();
|
||||
script.push("lsp");
|
||||
|
@ -1103,7 +1176,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn complete_command_with_space() {
|
||||
let (client_connection, _recv) = initialize_language_server();
|
||||
let (client_connection, _recv) = initialize_language_server(None);
|
||||
|
||||
let mut script = fixtures();
|
||||
script.push("lsp");
|
||||
|
@ -1141,7 +1214,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn complete_command_with_utf_line() {
|
||||
let (client_connection, _recv) = initialize_language_server();
|
||||
let (client_connection, _recv) = initialize_language_server(None);
|
||||
|
||||
let mut script = fixtures();
|
||||
script.push("lsp");
|
||||
|
@ -1179,7 +1252,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn complete_keyword() {
|
||||
let (client_connection, _recv) = initialize_language_server();
|
||||
let (client_connection, _recv) = initialize_language_server(None);
|
||||
|
||||
let mut script = fixtures();
|
||||
script.push("lsp");
|
||||
|
|
|
@ -99,7 +99,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn hover_correct_documentation_on_let() {
|
||||
let (client_connection, _recv) = initialize_language_server();
|
||||
let (client_connection, _recv) = initialize_language_server(None);
|
||||
|
||||
let mut script = fixtures();
|
||||
script.push("lsp");
|
||||
|
@ -129,7 +129,7 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn hover_on_command_after_full_content_change() {
|
||||
let (client_connection, _recv) = initialize_language_server();
|
||||
let (client_connection, _recv) = initialize_language_server(None);
|
||||
|
||||
let mut script = fixtures();
|
||||
script.push("lsp");
|
||||
|
@ -170,7 +170,7 @@ hello"#,
|
|||
|
||||
#[test]
|
||||
fn hover_on_command_after_partial_content_change() {
|
||||
let (client_connection, _recv) = initialize_language_server();
|
||||
let (client_connection, _recv) = initialize_language_server(None);
|
||||
|
||||
let mut script = fixtures();
|
||||
script.push("lsp");
|
||||
|
@ -215,7 +215,7 @@ hello"#,
|
|||
|
||||
#[test]
|
||||
fn open_document_with_utf_char() {
|
||||
let (client_connection, _recv) = initialize_language_server();
|
||||
let (client_connection, _recv) = initialize_language_server(None);
|
||||
|
||||
let mut script = fixtures();
|
||||
script.push("lsp");
|
||||
|
|
1
tests/fixtures/lsp/completion/include.nu
vendored
Normal file
1
tests/fixtures/lsp/completion/include.nu
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
gre
|
Loading…
Reference in New Issue
Block a user