diff --git a/compiler-core/src/analyse.rs b/compiler-core/src/analyse.rs index 48f7b8fd9ba..ced48fee44d 100644 --- a/compiler-core/src/analyse.rs +++ b/compiler-core/src/analyse.rs @@ -867,6 +867,7 @@ impl<'a, A> ModuleAnalyzer<'a, A> { location, name_location, name, + type_name, arguments: args, documentation, deprecation: constructor_deprecation, @@ -911,6 +912,7 @@ impl<'a, A> ModuleAnalyzer<'a, A> { location, name_location, name, + type_name, arguments: args, documentation, deprecation: constructor_deprecation, diff --git a/compiler-core/src/ast.rs b/compiler-core/src/ast.rs index f3e21593829..47df1447f34 100644 --- a/compiler-core/src/ast.rs +++ b/compiler-core/src/ast.rs @@ -245,6 +245,7 @@ pub struct RecordConstructor { pub location: SrcSpan, pub name_location: SrcSpan, pub name: EcoString, + pub type_name: EcoString, pub arguments: Vec>, pub documentation: Option<(u32, EcoString)>, pub deprecation: Deprecation, @@ -912,16 +913,17 @@ impl TypedDefinition { .iter() .find(|constructor| constructor.location.contains(byte_index)) { - if let Some(annotation) = constructor + return match constructor .arguments .iter() .find(|arg| arg.location.contains(byte_index)) - .and_then(|arg| arg.ast.find_node(byte_index, arg.type_.clone())) { - return Some(annotation); - } - - return Some(Located::VariantConstructorDefinition(constructor)); + Some(arg) => match arg.ast.find_node(byte_index, arg.type_.clone()) { + Some(annotation) => Some(annotation), + None => Some(Located::Label(arg.location, arg.type_.clone())), + }, + None => Some(Located::VariantConstructorDefinition(constructor)), + }; } // Note that the custom type `.location` covers the function diff --git a/compiler-core/src/language_server/engine.rs b/compiler-core/src/language_server/engine.rs index c8dc77d7bac..be2523f52a7 100644 --- a/compiler-core/src/language_server/engine.rs +++ b/compiler-core/src/language_server/engine.rs @@ -4,6 +4,7 @@ use crate::{ ast::{ self, CustomType, Definition, DefinitionLocation, ModuleConstant, PatternUnusedArguments, SrcSpan, TypedArg, TypedExpr, TypedFunction, TypedModule, TypedPattern, + TypedRecordConstructor, }, build::{Located, Module, UnqualifiedImport, type_constructor_from_modules}, config::PackageConfig, @@ -822,6 +823,9 @@ where Located::ModuleStatement(Definition::Function(fun)) => { Some(hover_for_function_head(fun, lines, module)) } + Located::ModuleStatement(Definition::CustomType(type_)) => { + Some(hover_for_custom_type(type_, lines)) + } Located::ModuleStatement(Definition::ModuleConstant(constant)) => { Some(hover_for_module_constant(constant, lines, module)) } @@ -837,7 +841,9 @@ where )) } Located::ModuleStatement(_) => None, - Located::VariantConstructorDefinition(_) => None, + Located::VariantConstructorDefinition(constructor) => { + Some(hover_for_constructor(constructor, lines, module)) + } Located::UnqualifiedImport(UnqualifiedImport { name, module: module_name, @@ -1242,6 +1248,49 @@ fn hover_for_module_constant( } } +fn hover_for_custom_type(type_: &CustomType>, line_numbers: LineNumbers) -> Hover { + let name = &type_.name; + let empty_str = EcoString::from(""); + let documentation = type_ + .documentation + .as_ref() + .map(|(_, doc)| doc) + .unwrap_or(&empty_str); + let contents = format!("```gleam\n{name}\n```\n{documentation}"); + Hover { + contents: HoverContents::Scalar(MarkedString::String(contents)), + range: Some(src_span_to_lsp_range(type_.full_location(), &line_numbers)), + } +} + +fn hover_for_constructor( + constructor: &TypedRecordConstructor, + line_numbers: LineNumbers, + module: &Module, +) -> Hover { + let type_name = &constructor.type_name; + let mut arguments = Vec::with_capacity(constructor.arguments.len()); + let mut printer = Printer::new(&module.ast.names); + + for argument in constructor.arguments.iter() { + let type_ = printer.print_type(&argument.type_); + arguments.push(type_); + } + + let arguments = arguments.join(", "); + let empty_str = EcoString::from(""); + let documentation = constructor + .documentation + .as_ref() + .map(|(_, doc)| doc) + .unwrap_or(&empty_str); + let contents = format!("```gleam\nfn({arguments}) -> {type_name}\n```\n{documentation}"); + Hover { + contents: HoverContents::Scalar(MarkedString::String(contents)), + range: Some(src_span_to_lsp_range(constructor.location, &line_numbers)), + } +} + fn hover_for_expression( expression: &TypedExpr, line_numbers: LineNumbers, diff --git a/compiler-core/src/language_server/tests/hover.rs b/compiler-core/src/language_server/tests/hover.rs index 084200d75f4..14b598a7122 100644 --- a/compiler-core/src/language_server/tests/hover.rs +++ b/compiler-core/src/language_server/tests/hover.rs @@ -1589,3 +1589,71 @@ import wibble find_position_of("wibble") ); } + +#[test] +fn hover_custom_type() { + assert_hover!( + " +/// Exciting documentation +/// Maybe even multiple lines +type Wibble { + /// Some more exciting documentation + Wibble(String) + /// The most exciting documentation + Wobble(arg: Int) +} +", + find_position_of("Wibble") + ); +} + +#[test] +fn hover_type_constructor() { + assert_hover!( + " +/// Exciting documentation +/// Maybe even multiple lines +type Wibble { + /// Some more exciting documentation + Wibble(arg: String) + /// The most exciting documentation + Wobble(Int) +} +", + find_position_of("Wibble").nth_occurrence(2) + ); +} + +#[test] +fn hover_type_constructor_with_label() { + assert_hover!( + " +/// Exciting documentation +/// Maybe even multiple lines +type Wibble { + /// Some more exciting documentation + Wibble(arg: String) + /// The most exciting documentation + Wobble(Int) +} +", + find_position_of("Wobble") + ); +} + +#[test] +fn hover_for_label_in_type_constructor() { + assert_hover!( + " +/// Exciting documentation +/// Maybe even multiple lines +type Wibble { + /// Some more exciting documentation + Wibble(arg: String) + /// The most exciting documentation + Wobble(Int) +} +", + find_position_of("arg") + ); +} diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__hover__hover_custom_type.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__hover__hover_custom_type.snap new file mode 100644 index 00000000000..6bc27cb3ff7 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__hover__hover_custom_type.snap @@ -0,0 +1,26 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\n/// Exciting documentation\n/// Maybe even multiple lines\ntype Wibble {\n /// Some more exciting documentation\n Wibble(String)\n /// The most exciting documentation\n Wobble(arg: Int)\n}\n" +--- +/// Exciting documentation +/// Maybe even multiple lines +type Wibble { +▔▔▔▔▔↑▔▔▔▔▔▔▔ + /// Some more exciting documentation +▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Wibble(String) +▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + /// The most exciting documentation +▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + Wobble(arg: Int) +▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ +} +▔ + + +----- Hover content ----- +Scalar( + String( + "```gleam\nWibble\n```\n Exciting documentation\n Maybe even multiple lines", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__hover__hover_for_label_in_type_constructor.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__hover__hover_for_label_in_type_constructor.snap new file mode 100644 index 00000000000..c34af2dd11e --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__hover__hover_for_label_in_type_constructor.snap @@ -0,0 +1,21 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\n/// Exciting documentation\n/// Maybe even multiple lines\ntype Wibble {\n /// Some more exciting documentation\n Wibble(arg: String)\n /// The most exciting documentation\n Wobble(Int)\n}\n" +--- +/// Exciting documentation +/// Maybe even multiple lines +type Wibble { + /// Some more exciting documentation + Wibble(arg: String) + ↑▔▔▔▔▔▔▔▔▔▔ + /// The most exciting documentation + Wobble(Int) +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nString\n```", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__hover__hover_type_constructor.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__hover__hover_type_constructor.snap new file mode 100644 index 00000000000..4ea4fc4190d --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__hover__hover_type_constructor.snap @@ -0,0 +1,21 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\n/// Exciting documentation\n/// Maybe even multiple lines\ntype Wibble {\n /// Some more exciting documentation\n Wibble(arg: String)\n /// The most exciting documentation\n Wobble(Int)\n}\n" +--- +/// Exciting documentation +/// Maybe even multiple lines +type Wibble { + /// Some more exciting documentation + Wibble(arg: String) + ↑▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ + /// The most exciting documentation + Wobble(Int) +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nfn(String) -> Wibble\n```\n Some more exciting documentation", + ), +) diff --git a/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__hover__hover_type_constructor_with_label.snap b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__hover__hover_type_constructor_with_label.snap new file mode 100644 index 00000000000..390bbfbcfe7 --- /dev/null +++ b/compiler-core/src/language_server/tests/snapshots/gleam_core__language_server__tests__hover__hover_type_constructor_with_label.snap @@ -0,0 +1,21 @@ +--- +source: compiler-core/src/language_server/tests/hover.rs +expression: "\n/// Exciting documentation\n/// Maybe even multiple lines\ntype Wibble {\n /// Some more exciting documentation\n Wibble(arg: String)\n /// The most exciting documentation\n Wobble(Int)\n}\n" +--- +/// Exciting documentation +/// Maybe even multiple lines +type Wibble { + /// Some more exciting documentation + Wibble(arg: String) + /// The most exciting documentation + Wobble(Int) + ↑▔▔▔▔▔▔▔▔▔▔ +} + + +----- Hover content ----- +Scalar( + String( + "```gleam\nfn(Int) -> Wibble\n```\n The most exciting documentation", + ), +) diff --git a/compiler-core/src/parse.rs b/compiler-core/src/parse.rs index 80d06e11f1f..78219d56110 100644 --- a/compiler-core/src/parse.rs +++ b/compiler-core/src/parse.rs @@ -2325,6 +2325,7 @@ where end: c_e, }, name: c_n, + type_name: name.clone(), arguments: args, documentation, deprecation: attributes.deprecated, diff --git a/compiler-core/src/parse/snapshots/gleam_core__parse__tests__deprecation_attribute_on_type_variant.snap b/compiler-core/src/parse/snapshots/gleam_core__parse__tests__deprecation_attribute_on_type_variant.snap index 46bf2af06c0..82d56cac721 100644 --- a/compiler-core/src/parse/snapshots/gleam_core__parse__tests__deprecation_attribute_on_type_variant.snap +++ b/compiler-core/src/parse/snapshots/gleam_core__parse__tests__deprecation_attribute_on_type_variant.snap @@ -33,6 +33,7 @@ Parsed { end: 47, }, name: "Wibble1", + type_name: "Wibble", arguments: [], documentation: None, deprecation: Deprecated { @@ -49,6 +50,7 @@ Parsed { end: 59, }, name: "Wibble2", + type_name: "Wibble", arguments: [], documentation: None, deprecation: NotDeprecated, diff --git a/compiler-core/src/parse/snapshots/gleam_core__parse__tests__record_access_no_label.snap b/compiler-core/src/parse/snapshots/gleam_core__parse__tests__record_access_no_label.snap index d7fda5445da..c25556ec2fc 100644 --- a/compiler-core/src/parse/snapshots/gleam_core__parse__tests__record_access_no_label.snap +++ b/compiler-core/src/parse/snapshots/gleam_core__parse__tests__record_access_no_label.snap @@ -33,6 +33,7 @@ Parsed { end: 25, }, name: "Wibble", + type_name: "Wibble", arguments: [ RecordConstructorArg { label: Some(