diff --git a/crates/nu-lsp/src/ast.rs b/crates/nu-lsp/src/ast.rs index 33e88d2d63..200988e9ef 100644 --- a/crates/nu-lsp/src/ast.rs +++ b/crates/nu-lsp/src/ast.rs @@ -1,8 +1,8 @@ use crate::Id; use nu_protocol::{ ast::{ - Argument, Block, Call, Expr, Expression, ExternalArgument, ListItem, MatchPattern, Pattern, - PipelineRedirection, RecordItem, + Argument, Block, Call, Expr, Expression, ExternalArgument, ListItem, MatchPattern, + PathMember, Pattern, PipelineRedirection, RecordItem, }, engine::StateWorkingSet, Span, @@ -389,6 +389,11 @@ fn try_find_id_in_overlay( return None; } let check_location = |span: &Span| location.map_or(true, |pos| span.contains(*pos)); + let module_from_overlay_name = |name: &str, span: Span| { + let found_id = Id::Module(working_set.find_overlay(name.as_bytes())?.origin); + id.map_or(true, |id_r| found_id == *id_r) + .then_some((found_id, strip_quotes(span, working_set))) + }; for arg in call.arguments.iter() { let Argument::Positional(expr) = arg else { continue; @@ -397,21 +402,11 @@ fn try_find_id_in_overlay( continue; }; let matched = match &expr.expr { - Expr::String(name) => { - let name = name.as_bytes(); - get_matched_module_id(working_set, expr.span, id).or_else(|| { - let found_id = Id::Module(working_set.find_overlay(name)?.origin); - id.map_or(true, |id_r| found_id == *id_r) - .then_some((found_id, strip_quotes(expr.span, working_set))) - }) - } + Expr::String(name) => get_matched_module_id(working_set, expr.span, id) + .or(module_from_overlay_name(name, expr.span)), // keyword 'as' Expr::Keyword(kwd) => match &kwd.expr.expr { - Expr::String(name) => { - let found_id = Id::Module(working_set.find_overlay(name.as_bytes())?.origin); - id.map_or(true, |id_r| found_id == *id_r) - .then_some((found_id, strip_quotes(kwd.expr.span, working_set))) - } + Expr::String(name) => module_from_overlay_name(name, kwd.expr.span), _ => None, }, _ => None, @@ -470,6 +465,27 @@ fn find_id_in_expr( .map(|p| vec![p]) } } + Expr::FullCellPath(fcp) => { + if fcp.head.span.contains(*location) { + None + } else { + let Expression { + expr: Expr::Var(var_id), + .. + } = fcp.head + else { + return None; + }; + let tail: Vec = fcp + .tail + .clone() + .into_iter() + .take_while(|pm| pm.span().start <= *location) + .collect(); + let span = tail.last()?.span(); + Some(vec![(Id::CellPath(var_id, tail), span)]) + } + } Expr::Overlay(Some(module_id)) => Some(vec![(Id::Module(*module_id), span)]), // terminal value expressions Expr::Bool(_) diff --git a/crates/nu-lsp/src/goto.rs b/crates/nu-lsp/src/goto.rs index 1c761e4f82..1a384dbce9 100644 --- a/crates/nu-lsp/src/goto.rs +++ b/crates/nu-lsp/src/goto.rs @@ -21,6 +21,16 @@ impl LanguageServer { let module = working_set.get_module(*module_id); module.span } + Id::CellPath(var_id, cell_path) => { + let var = working_set.get_variable(*var_id); + Some( + var.const_val + .clone() + .and_then(|val| val.follow_cell_path(cell_path, false).ok()) + .map(|val| val.span()) + .unwrap_or(var.declaration_span), + ) + } _ => None, } } @@ -54,8 +64,8 @@ impl LanguageServer { #[cfg(test)] mod tests { use crate::path_to_uri; - use crate::tests::{initialize_language_server, open_unchecked}; - use assert_json_diff::{assert_json_eq, assert_json_include}; + use crate::tests::{initialize_language_server, open_unchecked, result_from_message}; + use assert_json_diff::assert_json_eq; use lsp_server::{Connection, Message}; use lsp_types::{ request::{GotoDefinition, Request}, @@ -99,40 +109,10 @@ mod tests { let mut none_existent_path = root(); none_existent_path.push("none-existent.nu"); + let script = path_to_uri(&none_existent_path); + let resp = send_goto_definition_request(&client_connection, script.clone(), 0, 0); - client_connection - .sender - .send(Message::Request(lsp_server::Request { - id: 2.into(), - method: GotoDefinition::METHOD.to_string(), - params: serde_json::to_value(GotoDefinitionParams { - text_document_position_params: TextDocumentPositionParams { - text_document: TextDocumentIdentifier { - uri: path_to_uri(&none_existent_path), - }, - position: Position { - line: 0, - character: 0, - }, - }, - work_done_progress_params: WorkDoneProgressParams::default(), - partial_result_params: PartialResultParams::default(), - }) - .unwrap(), - })) - .unwrap(); - - let resp = client_connection - .receiver - .recv_timeout(std::time::Duration::from_secs(2)) - .unwrap(); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; - - assert_json_eq!(result, serde_json::json!(null)); + assert_json_eq!(result_from_message(resp), serde_json::json!(null)); } #[test] @@ -146,16 +126,10 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_goto_definition_request(&client_connection, script.clone(), 2, 12); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, + result_from_message(resp), serde_json::json!({ "uri": script, "range": { @@ -166,6 +140,31 @@ mod tests { ); } + #[test] + fn goto_definition_of_cell_path() { + let (client_connection, _recv) = initialize_language_server(None); + + let mut script = fixtures(); + script.push("lsp"); + script.push("hover"); + script.push("cell_path.nu"); + let script = path_to_uri(&script); + + open_unchecked(&client_connection, script.clone()); + + let resp = send_goto_definition_request(&client_connection, script.clone(), 4, 7); + assert_json_eq!( + result_from_message(resp).pointer("/range/start").unwrap(), + serde_json::json!({ "line": 1, "character": 10 }) + ); + + let resp = send_goto_definition_request(&client_connection, script.clone(), 4, 9); + assert_json_eq!( + result_from_message(resp).pointer("/range/start").unwrap(), + serde_json::json!({ "line": 1, "character": 17 }) + ); + } + #[test] fn goto_definition_of_command() { let (client_connection, _recv) = initialize_language_server(None); @@ -177,16 +176,10 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_goto_definition_request(&client_connection, script.clone(), 4, 1); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, + result_from_message(resp), serde_json::json!({ "uri": script, "range": { @@ -208,16 +201,10 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_goto_definition_request(&client_connection, script.clone(), 4, 2); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, + result_from_message(resp), serde_json::json!({ "uri": script, "range": { @@ -239,16 +226,10 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_goto_definition_request(&client_connection, script.clone(), 1, 14); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, + result_from_message(resp), serde_json::json!({ "uri": script, "range": { @@ -270,16 +251,10 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_goto_definition_request(&client_connection, script.clone(), 1, 21); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, + result_from_message(resp), serde_json::json!({ "uri": script, "range": { @@ -301,16 +276,10 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_goto_definition_request(&client_connection, script.clone(), 2, 9); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, + result_from_message(resp), serde_json::json!({ "uri": script, "range": { @@ -332,16 +301,10 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_goto_definition_request(&client_connection, script.clone(), 1, 16); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, + result_from_message(resp), serde_json::json!({ "uri": script, "range": { @@ -363,16 +326,10 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_goto_definition_request(&client_connection, script.clone(), 3, 15); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, + result_from_message(resp), serde_json::json!({ "uri": script, "range": { @@ -394,16 +351,10 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_goto_definition_request(&client_connection, script.clone(), 0, 23); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, + result_from_message(resp), serde_json::json!({ "uri": script.to_string().replace("use_module", "module"), "range": { @@ -425,16 +376,10 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_goto_definition_request(&client_connection, script.clone(), 3, 6); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, + result_from_message(resp), serde_json::json!({ "uri": script.to_string().replace("use_module", "module"), "range": { @@ -458,57 +403,21 @@ mod tests { open_unchecked(&client_connection, script.clone()); let resp = send_goto_definition_request(&client_connection, script.clone(), 1, 20); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; - - assert_json_include!( - actual: result, - expected: serde_json::json!({ - "uri": script.to_string().replace("use_module", "module"), - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 3 } - } - }) + assert_json_eq!( + result_from_message(resp).pointer("/range/start").unwrap(), + serde_json::json!({ "line": 0, "character": 0 }) ); let resp = send_goto_definition_request(&client_connection, script.clone(), 1, 25); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; - - assert_json_include!( - actual: result, - expected: serde_json::json!({ - "uri": script.to_string().replace("use_module", "module"), - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 3 } - } - }) + assert_json_eq!( + result_from_message(resp).pointer("/range/start").unwrap(), + serde_json::json!({ "line": 0, "character": 0 }) ); let resp = send_goto_definition_request(&client_connection, script.clone(), 2, 30); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; - - assert_json_include!( - actual: result, - expected: serde_json::json!({ - "uri": script.to_string().replace("use_module", "module"), - "range": { - "start": { "line": 0, "character": 0 }, - "end": { "line": 3 } - } - }) + assert_json_eq!( + result_from_message(resp).pointer("/range/start").unwrap(), + serde_json::json!({ "line": 0, "character": 0 }) ); } } diff --git a/crates/nu-lsp/src/hints.rs b/crates/nu-lsp/src/hints.rs index a3ee410cf4..96c191c7db 100644 --- a/crates/nu-lsp/src/hints.rs +++ b/crates/nu-lsp/src/hints.rs @@ -163,7 +163,7 @@ impl LanguageServer { #[cfg(test)] mod tests { use crate::path_to_uri; - use crate::tests::{initialize_language_server, open_unchecked}; + use crate::tests::{initialize_language_server, open_unchecked, result_from_message}; use assert_json_diff::assert_json_eq; use lsp_server::{Connection, Message}; use lsp_types::{ @@ -213,16 +213,10 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_inlay_hint_request(&client_connection, script.clone()); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, + result_from_message(resp), serde_json::json!([ { "position": { "line": 0, "character": 9 }, "label": ": int", "kind": 1 }, { "position": { "line": 1, "character": 7 }, "label": ": string", "kind": 1 }, @@ -246,16 +240,10 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_inlay_hint_request(&client_connection, script.clone()); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, + result_from_message(resp), serde_json::json!([ { "position": { "line": 0, "character": 8 }, "label": ": int", "kind": 1 }, { "position": { "line": 1, "character": 10 }, "label": ": float", "kind": 1 }, @@ -280,16 +268,10 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_inlay_hint_request(&client_connection, script.clone()); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, + result_from_message(resp), serde_json::json!([ { "position": { "line": 9, "character": 9 }, diff --git a/crates/nu-lsp/src/lib.rs b/crates/nu-lsp/src/lib.rs index 14862fb6c5..f053cfb9ac 100644 --- a/crates/nu-lsp/src/lib.rs +++ b/crates/nu-lsp/src/lib.rs @@ -13,7 +13,7 @@ use lsp_types::{ use miette::{miette, IntoDiagnostic, Result}; use nu_cli::{NuCompleter, SuggestionKind}; use nu_protocol::{ - ast::Block, + ast::{Block, PathMember}, engine::{CachedFile, Command, EngineState, Stack, StateDelta, StateWorkingSet}, DeclId, ModuleId, Span, Type, VarId, }; @@ -36,12 +36,13 @@ mod notification; mod symbols; mod workspace; -#[derive(Debug, Clone, Eq, PartialEq)] +#[derive(Debug, Clone, PartialEq)] pub(crate) enum Id { Variable(VarId), Declaration(DeclId), Value(Type), Module(ModuleId), + CellPath(VarId, Vec), } pub struct LanguageServer { @@ -423,19 +424,20 @@ impl LanguageServer { } } - fn get_decl_description(decl: &dyn Command) -> String { + fn get_decl_description(decl: &dyn Command, skip_description: bool) -> String { let mut description = String::new(); - // First description - description.push_str(&format!("{}\n", decl.description().replace('\r', ""))); + if !skip_description { + // First description + description.push_str(&format!("{}\n", decl.description().replace('\r', ""))); - // Additional description - if !decl.extra_description().is_empty() { - description.push_str(&format!("\n{}\n", decl.extra_description())); + // Additional description + if !decl.extra_description().is_empty() { + description.push_str(&format!("\n{}\n", decl.extra_description())); + } } - // Usage - description.push_str("-----\n### Usage \n```nu\n"); + description.push_str("---\n### Usage \n```nu\n"); let signature = decl.signature(); description.push_str(&format!(" {}", signature.name)); if !signature.named.is_empty() { @@ -588,29 +590,49 @@ impl LanguageServer { match id { Id::Variable(var_id) => { let var = working_set.get_variable(var_id); + let value = var + .const_val + .clone() + .and_then(|v| v.coerce_into_string().ok()) + .map(|s| format!("\n---\n{}", s)) + .unwrap_or_default(); let contents = format!( - "{}{} `{}`", - if var.const_val.is_some() { - "const " - } else { - "" - }, + "{} ```\n{}\n``` {}", if var.mutable { "mutable " } else { "" }, var.ty, + value ); markdown_hover(contents) } - Id::Declaration(decl_id) => { - markdown_hover(Self::get_decl_description(working_set.get_decl(decl_id))) + Id::CellPath(var_id, cell_path) => { + let var = working_set.get_variable(var_id); + markdown_hover( + var.const_val + .clone() + .and_then(|val| val.follow_cell_path(&cell_path, false).ok()) + .map(|val| { + let ty = val.get_type().clone(); + let value_string = val + .coerce_into_string() + .ok() + .map(|s| format!("\n---\n{}", s)) + .unwrap_or_default(); + format!("```\n{}\n```{}", ty, value_string) + }) + .unwrap_or("`unknown`".into()), + ) } + Id::Declaration(decl_id) => markdown_hover(Self::get_decl_description( + working_set.get_decl(decl_id), + false, + )), Id::Module(module_id) => { - let mut description = String::new(); - for cmt_span in working_set.get_module_comments(module_id)? { - description.push_str( - String::from_utf8_lossy(working_set.get_span_contents(*cmt_span)).as_ref(), - ); - description.push('\n'); - } + let description = working_set + .get_module_comments(module_id)? + .iter() + .map(|sp| String::from_utf8_lossy(working_set.get_span_contents(*sp)).into()) + .collect::>() + .join("\n"); markdown_hover(description) } Id::Value(t) => markdown_hover(format!("`{}`", t)), @@ -657,7 +679,7 @@ impl LanguageServer { .extra .map(|ex| ex.join("\n")) .or(decl_id.map(|decl_id| { - Self::get_decl_description(engine_state.get_decl(decl_id)) + Self::get_decl_description(engine_state.get_decl(decl_id), true) })) .map(|value| { Documentation::MarkupContent(MarkupContent { @@ -855,6 +877,13 @@ mod tests { } } + pub(crate) fn result_from_message(message: lsp_server::Message) -> serde_json::Value { + match message { + Message::Response(Response { result, .. }) => result.expect("Empty result!"), + _ => panic!("Unexpected message type!"), + } + } + pub(crate) fn send_hover_request( client_connection: &Connection, uri: Uri, @@ -894,17 +923,45 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_hover_request(&client_connection, script.clone(), 2, 0); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, - serde_json::json!({ "contents": { "kind": "markdown", "value": " `table`" } }) + result_from_message(resp), + serde_json::json!({ "contents": { "kind": "markdown", "value": " ```\ntable\n``` " } }) + ); + } + + #[test] + fn hover_on_cell_path() { + let (client_connection, _recv) = initialize_language_server(None); + + let mut script = fixtures(); + script.push("lsp"); + script.push("hover"); + script.push("cell_path.nu"); + let script = path_to_uri(&script); + + open_unchecked(&client_connection, script.clone()); + + let resp = send_hover_request(&client_connection, script.clone(), 4, 3); + let result = result_from_message(resp); + assert_json_eq!( + result.pointer("/contents/value").unwrap(), + serde_json::json!("```\nlist\n```") + ); + + let resp = send_hover_request(&client_connection, script.clone(), 4, 7); + let result = result_from_message(resp); + assert_json_eq!( + result.pointer("/contents/value").unwrap(), + serde_json::json!("```\nrecord\n```") + ); + + let resp = send_hover_request(&client_connection, script.clone(), 4, 11); + let result = result_from_message(resp); + assert_json_eq!( + result.pointer("/contents/value").unwrap(), + serde_json::json!("```\nint\n```\n---\n2") ); } @@ -919,20 +976,14 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - 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, + result_from_message(resp), serde_json::json!({ "contents": { "kind": "markdown", - "value": "Renders some 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" + "value": "Renders some 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" } }) ); @@ -949,20 +1000,14 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_hover_request(&client_connection, script.clone(), 5, 8); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, + result_from_message(resp), serde_json::json!({ "contents": { "kind": "markdown", - "value": "Concatenate multiple strings into a single string, with an optional separator between each.\n-----\n### Usage \n```nu\n str join {flags} \n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n\n### Parameters\n\n `separator: string` - Optional separator to use when creating string.\n\n\n### Input/output types\n\n```nu\n list | string\n string | string\n\n```\n### Example(s)\n Create a string from input\n```nu\n ['nu', 'shell'] | str join\n```\n Create a string from input with a separator\n```nu\n ['nu', 'shell'] | str join '-'\n```\n" + "value": "Concatenate multiple strings into a single string, with an optional separator between each.\n---\n### Usage \n```nu\n str join {flags} \n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n\n### Parameters\n\n `separator: string` - Optional separator to use when creating string.\n\n\n### Input/output types\n\n```nu\n list | string\n string | string\n\n```\n### Example(s)\n Create a string from input\n```nu\n ['nu', 'shell'] | str join\n```\n Create a string from input with a separator\n```nu\n ['nu', 'shell'] | str join '-'\n```\n" } }) ); @@ -979,22 +1024,16 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_hover_request(&client_connection, script.clone(), 3, 12); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; + let result = result_from_message(resp); assert_eq!( result - .unwrap() .pointer("/contents/value") .unwrap() .to_string() .replace("\\r", ""), - "\"# module doc\\n\"" + "\"# module doc\"" ); } @@ -1039,16 +1078,10 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_complete_request(&client_connection, script, 2, 9); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_include!( - actual: result, + actual: result_from_message(resp), expected: serde_json::json!([ { "label": "$greeting", @@ -1074,16 +1107,10 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_complete_request(&client_connection, script, 0, 8); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_include!( - actual: result, + actual: result_from_message(resp), expected: serde_json::json!([ { "label": "config nu", @@ -1108,16 +1135,10 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_complete_request(&client_connection, script, 0, 13); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_include!( - actual: result, + actual: result_from_message(resp), expected: serde_json::json!([ { "label": "str trim", @@ -1144,16 +1165,10 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_complete_request(&client_connection, script, 0, 2); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_include!( - actual: result, + actual: result_from_message(resp), expected: serde_json::json!([ { "label": "overlay", diff --git a/crates/nu-lsp/src/notification.rs b/crates/nu-lsp/src/notification.rs index d64e5412ac..123fae7728 100644 --- a/crates/nu-lsp/src/notification.rs +++ b/crates/nu-lsp/src/notification.rs @@ -137,10 +137,10 @@ impl LanguageServer { mod tests { use crate::path_to_uri; use crate::tests::{ - initialize_language_server, open, open_unchecked, send_hover_request, update, + initialize_language_server, open, open_unchecked, result_from_message, send_hover_request, + update, }; use assert_json_diff::assert_json_eq; - use lsp_server::Message; use lsp_types::Range; use nu_test_support::fs::fixtures; @@ -155,20 +155,14 @@ mod tests { 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, + result_from_message(resp), 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} \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" + "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} \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" } }) ); @@ -196,20 +190,14 @@ 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, + result_from_message(resp), 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" + "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" } }) ); @@ -241,20 +229,14 @@ hello"#, }, }), ); - 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, + result_from_message(resp), 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" + "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" } }) ); diff --git a/crates/nu-lsp/src/symbols.rs b/crates/nu-lsp/src/symbols.rs index 480acf6d40..4383e33738 100644 --- a/crates/nu-lsp/src/symbols.rs +++ b/crates/nu-lsp/src/symbols.rs @@ -298,7 +298,7 @@ impl LanguageServer { #[cfg(test)] mod tests { use crate::path_to_uri; - use crate::tests::{initialize_language_server, open_unchecked, update}; + use crate::tests::{initialize_language_server, open_unchecked, result_from_message, update}; use assert_json_diff::assert_json_eq; use lsp_server::{Connection, Message}; use lsp_types::{ @@ -361,15 +361,9 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_document_symbol_request(&client_connection, script.clone()); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; - assert_json_eq!(result, serde_json::json!([])); + assert_json_eq!(result_from_message(resp), serde_json::json!([])); } #[test] @@ -383,16 +377,10 @@ mod tests { let script = path_to_uri(&script); open_unchecked(&client_connection, script.clone()); - let resp = send_document_symbol_request(&client_connection, script.clone()); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, + result_from_message(resp), serde_json::json!([ { "name": "def_foo", @@ -446,16 +434,10 @@ mod tests { }, }), ); - let resp = send_document_symbol_request(&client_connection, script.clone()); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, + result_from_message(resp), serde_json::json!([ { "name": "var_bar", @@ -490,16 +472,10 @@ mod tests { open_unchecked(&client_connection, script_foo.clone()); open_unchecked(&client_connection, script_bar.clone()); - let resp = send_workspace_symbol_request(&client_connection, "br".to_string()); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, + result_from_message(resp), serde_json::json!([ { "name": "def_bar", @@ -556,16 +532,10 @@ mod tests { open_unchecked(&client_connection, script_foo.clone()); open_unchecked(&client_connection, script_bar.clone()); - let resp = send_workspace_symbol_request(&client_connection, "foo".to_string()); - let result = if let Message::Response(response) = resp { - response.result - } else { - panic!() - }; assert_json_eq!( - result, + result_from_message(resp), serde_json::json!([ { "name": "def_foo", diff --git a/crates/nu-lsp/src/workspace.rs b/crates/nu-lsp/src/workspace.rs index ed6d0bba8f..9a8a943a03 100644 --- a/crates/nu-lsp/src/workspace.rs +++ b/crates/nu-lsp/src/workspace.rs @@ -846,7 +846,7 @@ mod tests { serde_json::json!({ "contents": { "kind": "markdown", - "value": "\n-----\n### Usage \n```nu\n foo str {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n" + "value": "\n---\n### Usage \n```nu\n foo str {flags}\n```\n\n### Flags\n\n `-h`, `--help` - Display the help message for this command\n\n" } }), ), diff --git a/crates/nu-protocol/src/ast/cell_path.rs b/crates/nu-protocol/src/ast/cell_path.rs index 3c2e9b3dd7..a1c66ddd7e 100644 --- a/crates/nu-protocol/src/ast/cell_path.rs +++ b/crates/nu-protocol/src/ast/cell_path.rs @@ -68,6 +68,13 @@ impl PathMember { } => *optional = true, } } + + pub fn span(&self) -> Span { + match self { + PathMember::String { span, .. } => *span, + PathMember::Int { span, .. } => *span, + } + } } impl PartialEq for PathMember { diff --git a/tests/fixtures/lsp/hover/cell_path.nu b/tests/fixtures/lsp/hover/cell_path.nu new file mode 100644 index 0000000000..0f11259edc --- /dev/null +++ b/tests/fixtures/lsp/hover/cell_path.nu @@ -0,0 +1,5 @@ +const r = { + foo: [1 {bar : 2}] +} + +$r.foo.1.bar