mirror of
https://github.com/denoland/deno.git
synced 2024-12-21 23:04:45 -05:00
feat(lsp): interactive inlay hints (#26382)
This commit is contained in:
parent
afb33b3c25
commit
9fe2bf42dc
4 changed files with 206 additions and 71 deletions
|
@ -3812,7 +3812,7 @@ impl Inner {
|
|||
let maybe_inlay_hints = maybe_inlay_hints.map(|hints| {
|
||||
hints
|
||||
.iter()
|
||||
.map(|hint| hint.to_lsp(line_index.clone()))
|
||||
.map(|hint| hint.to_lsp(line_index.clone(), self))
|
||||
.collect()
|
||||
});
|
||||
self.performance.measure(mark);
|
||||
|
|
|
@ -2182,6 +2182,50 @@ impl NavigateToItem {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct InlayHintDisplayPart {
|
||||
pub text: String,
|
||||
pub span: Option<TextSpan>,
|
||||
pub file: Option<String>,
|
||||
}
|
||||
|
||||
impl InlayHintDisplayPart {
|
||||
pub fn to_lsp(
|
||||
&self,
|
||||
language_server: &language_server::Inner,
|
||||
) -> lsp::InlayHintLabelPart {
|
||||
let location = self.file.as_ref().map(|f| {
|
||||
let specifier =
|
||||
resolve_url(f).unwrap_or_else(|_| INVALID_SPECIFIER.clone());
|
||||
let file_referrer =
|
||||
language_server.documents.get_file_referrer(&specifier);
|
||||
let uri = language_server
|
||||
.url_map
|
||||
.specifier_to_uri(&specifier, file_referrer.as_deref())
|
||||
.unwrap_or_else(|_| INVALID_URI.clone());
|
||||
let range = self
|
||||
.span
|
||||
.as_ref()
|
||||
.and_then(|s| {
|
||||
let asset_or_doc =
|
||||
language_server.get_asset_or_document(&specifier).ok()?;
|
||||
Some(s.to_range(asset_or_doc.line_index()))
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
lsp::Range::new(lsp::Position::new(0, 0), lsp::Position::new(0, 0))
|
||||
});
|
||||
lsp::Location { uri, range }
|
||||
});
|
||||
lsp::InlayHintLabelPart {
|
||||
value: self.text.clone(),
|
||||
tooltip: None,
|
||||
location,
|
||||
command: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub enum InlayHintKind {
|
||||
Type,
|
||||
|
@ -2203,6 +2247,7 @@ impl InlayHintKind {
|
|||
#[serde(rename_all = "camelCase")]
|
||||
pub struct InlayHint {
|
||||
pub text: String,
|
||||
pub display_parts: Option<Vec<InlayHintDisplayPart>>,
|
||||
pub position: u32,
|
||||
pub kind: InlayHintKind,
|
||||
pub whitespace_before: Option<bool>,
|
||||
|
@ -2210,10 +2255,23 @@ pub struct InlayHint {
|
|||
}
|
||||
|
||||
impl InlayHint {
|
||||
pub fn to_lsp(&self, line_index: Arc<LineIndex>) -> lsp::InlayHint {
|
||||
pub fn to_lsp(
|
||||
&self,
|
||||
line_index: Arc<LineIndex>,
|
||||
language_server: &language_server::Inner,
|
||||
) -> lsp::InlayHint {
|
||||
lsp::InlayHint {
|
||||
position: line_index.position_tsc(self.position.into()),
|
||||
label: lsp::InlayHintLabel::String(self.text.clone()),
|
||||
label: if let Some(display_parts) = &self.display_parts {
|
||||
lsp::InlayHintLabel::LabelParts(
|
||||
display_parts
|
||||
.iter()
|
||||
.map(|p| p.to_lsp(language_server))
|
||||
.collect(),
|
||||
)
|
||||
} else {
|
||||
lsp::InlayHintLabel::String(self.text.clone())
|
||||
},
|
||||
kind: self.kind.to_lsp(),
|
||||
padding_left: self.whitespace_before,
|
||||
padding_right: self.whitespace_after,
|
||||
|
@ -4892,6 +4950,8 @@ pub struct UserPreferences {
|
|||
pub allow_rename_of_import_path: Option<bool>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub auto_import_file_exclude_patterns: Option<Vec<String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub interactive_inlay_hints: Option<bool>,
|
||||
}
|
||||
|
||||
impl UserPreferences {
|
||||
|
@ -4909,6 +4969,7 @@ impl UserPreferences {
|
|||
include_completions_with_snippet_text: Some(
|
||||
config.snippet_support_capable(),
|
||||
),
|
||||
interactive_inlay_hints: Some(true),
|
||||
provide_refactor_not_applicable_reason: Some(true),
|
||||
quote_preference: Some(fmt_config.into()),
|
||||
use_label_details_in_completion_entries: Some(true),
|
||||
|
|
|
@ -1827,15 +1827,41 @@ fn lsp_hover_disabled() {
|
|||
fn lsp_inlay_hints() {
|
||||
let context = TestContextBuilder::new().use_temp_cwd().build();
|
||||
let mut client = context.new_lsp_command().build();
|
||||
client.initialize(|builder| {
|
||||
builder.enable_inlay_hints();
|
||||
});
|
||||
client.initialize_default();
|
||||
client.change_configuration(json!({
|
||||
"deno": {
|
||||
"enable": true,
|
||||
},
|
||||
"typescript": {
|
||||
"inlayHints": {
|
||||
"parameterNames": {
|
||||
"enabled": "all",
|
||||
},
|
||||
"parameterTypes": {
|
||||
"enabled": true,
|
||||
},
|
||||
"variableTypes": {
|
||||
"enabled": true,
|
||||
},
|
||||
"propertyDeclarationTypes": {
|
||||
"enabled": true,
|
||||
},
|
||||
"functionLikeReturnTypes": {
|
||||
"enabled": true,
|
||||
},
|
||||
"enumMemberValues": {
|
||||
"enabled": true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}));
|
||||
client.did_open(json!({
|
||||
"textDocument": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"languageId": "typescript",
|
||||
"version": 1,
|
||||
"text": r#"function a(b: string) {
|
||||
"text": r#"
|
||||
function a(b: string) {
|
||||
return b;
|
||||
}
|
||||
|
||||
|
@ -1854,8 +1880,19 @@ fn lsp_inlay_hints() {
|
|||
}
|
||||
|
||||
["a"].map((v) => v + v);
|
||||
"#
|
||||
}
|
||||
|
||||
interface Bar {
|
||||
someField: string;
|
||||
}
|
||||
function getBar(): Bar {
|
||||
return { someField: "foo" };
|
||||
}
|
||||
// This shouldn't have a type hint because the variable name makes it
|
||||
// redundant.
|
||||
const bar = getBar();
|
||||
const someValue = getBar();
|
||||
"#,
|
||||
},
|
||||
}));
|
||||
let res = client.write_request(
|
||||
"textDocument/inlayHint",
|
||||
|
@ -1864,65 +1901,130 @@ fn lsp_inlay_hints() {
|
|||
"uri": "file:///a/file.ts",
|
||||
},
|
||||
"range": {
|
||||
"start": { "line": 0, "character": 0 },
|
||||
"end": { "line": 19, "character": 0, }
|
||||
}
|
||||
"start": { "line": 1, "character": 0 },
|
||||
"end": { "line": 31, "character": 0, },
|
||||
},
|
||||
}),
|
||||
);
|
||||
assert_eq!(
|
||||
res,
|
||||
json!([
|
||||
{
|
||||
"position": { "line": 0, "character": 21 },
|
||||
"label": ": string",
|
||||
"position": { "line": 1, "character": 29 },
|
||||
"label": [{ "value": ": " }, { "value": "string" }],
|
||||
"kind": 1,
|
||||
"paddingLeft": true
|
||||
"paddingLeft": true,
|
||||
}, {
|
||||
"position": { "line": 4, "character": 10 },
|
||||
"label": "b:",
|
||||
"position": { "line": 5, "character": 10 },
|
||||
"label": [
|
||||
{
|
||||
"value": "b",
|
||||
"location": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"range": {
|
||||
"start": { "line": 1, "character": 19 },
|
||||
"end": { "line": 1, "character": 20 },
|
||||
},
|
||||
},
|
||||
},
|
||||
{ "value": ":" },
|
||||
],
|
||||
"kind": 2,
|
||||
"paddingRight": true
|
||||
"paddingRight": true,
|
||||
}, {
|
||||
"position": { "line": 7, "character": 11 },
|
||||
"position": { "line": 8, "character": 11 },
|
||||
"label": "= 0",
|
||||
"paddingLeft": true
|
||||
"paddingLeft": true,
|
||||
}, {
|
||||
"position": { "line": 10, "character": 17 },
|
||||
"label": "string:",
|
||||
"position": { "line": 11, "character": 17 },
|
||||
"label": [
|
||||
{
|
||||
"value": "string",
|
||||
"location": {
|
||||
"uri": "deno:/asset/lib.es5.d.ts",
|
||||
"range": {
|
||||
"start": { "line": 41, "character": 26 },
|
||||
"end": { "line": 41, "character": 32 },
|
||||
},
|
||||
},
|
||||
},
|
||||
{ "value": ":" },
|
||||
],
|
||||
"kind": 2,
|
||||
"paddingRight": true
|
||||
"paddingRight": true,
|
||||
}, {
|
||||
"position": { "line": 10, "character": 24 },
|
||||
"label": "radix:",
|
||||
"position": { "line": 11, "character": 24 },
|
||||
"label": [
|
||||
{
|
||||
"value": "radix",
|
||||
"location": {
|
||||
"uri": "deno:/asset/lib.es5.d.ts",
|
||||
"range": {
|
||||
"start": { "line": 41, "character": 42 },
|
||||
"end": { "line": 41, "character": 47 },
|
||||
},
|
||||
},
|
||||
},
|
||||
{ "value": ":" },
|
||||
],
|
||||
"kind": 2,
|
||||
"paddingRight": true
|
||||
"paddingRight": true,
|
||||
}, {
|
||||
"position": { "line": 12, "character": 15 },
|
||||
"label": ": number",
|
||||
"position": { "line": 13, "character": 15 },
|
||||
"label": [{ "value": ": " }, { "value": "number" }],
|
||||
"kind": 1,
|
||||
"paddingLeft": true
|
||||
"paddingLeft": true,
|
||||
}, {
|
||||
"position": { "line": 15, "character": 11 },
|
||||
"label": ": number",
|
||||
"position": { "line": 16, "character": 11 },
|
||||
"label": [{ "value": ": " }, { "value": "number" }],
|
||||
"kind": 1,
|
||||
"paddingLeft": true
|
||||
"paddingLeft": true,
|
||||
}, {
|
||||
"position": { "line": 18, "character": 18 },
|
||||
"label": "callbackfn:",
|
||||
"position": { "line": 19, "character": 18 },
|
||||
"label": [
|
||||
{
|
||||
"value": "callbackfn",
|
||||
"location": {
|
||||
"uri": "deno:/asset/lib.es5.d.ts",
|
||||
"range": {
|
||||
"start": { "line": 1462, "character": 11 },
|
||||
"end": { "line": 1462, "character": 21 },
|
||||
},
|
||||
},
|
||||
},
|
||||
{ "value": ":" },
|
||||
],
|
||||
"kind": 2,
|
||||
"paddingRight": true
|
||||
"paddingRight": true,
|
||||
}, {
|
||||
"position": { "line": 18, "character": 20 },
|
||||
"label": ": string",
|
||||
"position": { "line": 19, "character": 20 },
|
||||
"label": [{ "value": ": " }, { "value": "string" }],
|
||||
"kind": 1,
|
||||
"paddingLeft": true
|
||||
"paddingLeft": true,
|
||||
}, {
|
||||
"position": { "line": 18, "character": 21 },
|
||||
"label": ": string",
|
||||
"position": { "line": 19, "character": 21 },
|
||||
"label": [{ "value": ": " }, { "value": "string" }],
|
||||
"kind": 1,
|
||||
"paddingLeft": true
|
||||
}
|
||||
])
|
||||
"paddingLeft": true,
|
||||
}, {
|
||||
"position": { "line": 30, "character": 23 },
|
||||
"label": [
|
||||
{ "value": ": " },
|
||||
{
|
||||
"value": "Bar",
|
||||
"location": {
|
||||
"uri": "file:///a/file.ts",
|
||||
"range": {
|
||||
"start": { "line": 21, "character": 18 },
|
||||
"end": { "line": 21, "character": 21 },
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
"kind": 1,
|
||||
"paddingLeft": true,
|
||||
},
|
||||
]),
|
||||
);
|
||||
client.shutdown();
|
||||
}
|
||||
|
|
|
@ -308,34 +308,6 @@ impl InitializeParamsBuilder {
|
|||
self
|
||||
}
|
||||
|
||||
pub fn enable_inlay_hints(&mut self) -> &mut Self {
|
||||
let options = self.initialization_options_mut();
|
||||
options.insert(
|
||||
"inlayHints".to_string(),
|
||||
json!({
|
||||
"parameterNames": {
|
||||
"enabled": "all"
|
||||
},
|
||||
"parameterTypes": {
|
||||
"enabled": true
|
||||
},
|
||||
"variableTypes": {
|
||||
"enabled": true
|
||||
},
|
||||
"propertyDeclarationTypes": {
|
||||
"enabled": true
|
||||
},
|
||||
"functionLikeReturnTypes": {
|
||||
"enabled": true
|
||||
},
|
||||
"enumMemberValues": {
|
||||
"enabled": true
|
||||
}
|
||||
}),
|
||||
);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn disable_testing_api(&mut self) -> &mut Self {
|
||||
let obj = self
|
||||
.params
|
||||
|
|
Loading…
Reference in a new issue