mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -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| {
|
let maybe_inlay_hints = maybe_inlay_hints.map(|hints| {
|
||||||
hints
|
hints
|
||||||
.iter()
|
.iter()
|
||||||
.map(|hint| hint.to_lsp(line_index.clone()))
|
.map(|hint| hint.to_lsp(line_index.clone(), self))
|
||||||
.collect()
|
.collect()
|
||||||
});
|
});
|
||||||
self.performance.measure(mark);
|
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)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
pub enum InlayHintKind {
|
pub enum InlayHintKind {
|
||||||
Type,
|
Type,
|
||||||
|
@ -2203,6 +2247,7 @@ impl InlayHintKind {
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct InlayHint {
|
pub struct InlayHint {
|
||||||
pub text: String,
|
pub text: String,
|
||||||
|
pub display_parts: Option<Vec<InlayHintDisplayPart>>,
|
||||||
pub position: u32,
|
pub position: u32,
|
||||||
pub kind: InlayHintKind,
|
pub kind: InlayHintKind,
|
||||||
pub whitespace_before: Option<bool>,
|
pub whitespace_before: Option<bool>,
|
||||||
|
@ -2210,10 +2255,23 @@ pub struct InlayHint {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl 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 {
|
lsp::InlayHint {
|
||||||
position: line_index.position_tsc(self.position.into()),
|
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(),
|
kind: self.kind.to_lsp(),
|
||||||
padding_left: self.whitespace_before,
|
padding_left: self.whitespace_before,
|
||||||
padding_right: self.whitespace_after,
|
padding_right: self.whitespace_after,
|
||||||
|
@ -4892,6 +4950,8 @@ pub struct UserPreferences {
|
||||||
pub allow_rename_of_import_path: Option<bool>,
|
pub allow_rename_of_import_path: Option<bool>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub auto_import_file_exclude_patterns: Option<Vec<String>>,
|
pub auto_import_file_exclude_patterns: Option<Vec<String>>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
pub interactive_inlay_hints: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UserPreferences {
|
impl UserPreferences {
|
||||||
|
@ -4909,6 +4969,7 @@ impl UserPreferences {
|
||||||
include_completions_with_snippet_text: Some(
|
include_completions_with_snippet_text: Some(
|
||||||
config.snippet_support_capable(),
|
config.snippet_support_capable(),
|
||||||
),
|
),
|
||||||
|
interactive_inlay_hints: Some(true),
|
||||||
provide_refactor_not_applicable_reason: Some(true),
|
provide_refactor_not_applicable_reason: Some(true),
|
||||||
quote_preference: Some(fmt_config.into()),
|
quote_preference: Some(fmt_config.into()),
|
||||||
use_label_details_in_completion_entries: Some(true),
|
use_label_details_in_completion_entries: Some(true),
|
||||||
|
|
|
@ -1827,15 +1827,41 @@ fn lsp_hover_disabled() {
|
||||||
fn lsp_inlay_hints() {
|
fn lsp_inlay_hints() {
|
||||||
let context = TestContextBuilder::new().use_temp_cwd().build();
|
let context = TestContextBuilder::new().use_temp_cwd().build();
|
||||||
let mut client = context.new_lsp_command().build();
|
let mut client = context.new_lsp_command().build();
|
||||||
client.initialize(|builder| {
|
client.initialize_default();
|
||||||
builder.enable_inlay_hints();
|
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!({
|
client.did_open(json!({
|
||||||
"textDocument": {
|
"textDocument": {
|
||||||
"uri": "file:///a/file.ts",
|
"uri": "file:///a/file.ts",
|
||||||
"languageId": "typescript",
|
"languageId": "typescript",
|
||||||
"version": 1,
|
"version": 1,
|
||||||
"text": r#"function a(b: string) {
|
"text": r#"
|
||||||
|
function a(b: string) {
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1854,8 +1880,19 @@ fn lsp_inlay_hints() {
|
||||||
}
|
}
|
||||||
|
|
||||||
["a"].map((v) => v + v);
|
["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(
|
let res = client.write_request(
|
||||||
"textDocument/inlayHint",
|
"textDocument/inlayHint",
|
||||||
|
@ -1864,65 +1901,130 @@ fn lsp_inlay_hints() {
|
||||||
"uri": "file:///a/file.ts",
|
"uri": "file:///a/file.ts",
|
||||||
},
|
},
|
||||||
"range": {
|
"range": {
|
||||||
"start": { "line": 0, "character": 0 },
|
"start": { "line": 1, "character": 0 },
|
||||||
"end": { "line": 19, "character": 0, }
|
"end": { "line": 31, "character": 0, },
|
||||||
}
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
res,
|
res,
|
||||||
json!([
|
json!([
|
||||||
{
|
{
|
||||||
"position": { "line": 0, "character": 21 },
|
"position": { "line": 1, "character": 29 },
|
||||||
"label": ": string",
|
"label": [{ "value": ": " }, { "value": "string" }],
|
||||||
"kind": 1,
|
"kind": 1,
|
||||||
"paddingLeft": true
|
"paddingLeft": true,
|
||||||
}, {
|
}, {
|
||||||
"position": { "line": 4, "character": 10 },
|
"position": { "line": 5, "character": 10 },
|
||||||
"label": "b:",
|
"label": [
|
||||||
|
{
|
||||||
|
"value": "b",
|
||||||
|
"location": {
|
||||||
|
"uri": "file:///a/file.ts",
|
||||||
|
"range": {
|
||||||
|
"start": { "line": 1, "character": 19 },
|
||||||
|
"end": { "line": 1, "character": 20 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ "value": ":" },
|
||||||
|
],
|
||||||
"kind": 2,
|
"kind": 2,
|
||||||
"paddingRight": true
|
"paddingRight": true,
|
||||||
}, {
|
}, {
|
||||||
"position": { "line": 7, "character": 11 },
|
"position": { "line": 8, "character": 11 },
|
||||||
"label": "= 0",
|
"label": "= 0",
|
||||||
"paddingLeft": true
|
"paddingLeft": true,
|
||||||
}, {
|
}, {
|
||||||
"position": { "line": 10, "character": 17 },
|
"position": { "line": 11, "character": 17 },
|
||||||
"label": "string:",
|
"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,
|
"kind": 2,
|
||||||
"paddingRight": true
|
"paddingRight": true,
|
||||||
}, {
|
}, {
|
||||||
"position": { "line": 10, "character": 24 },
|
"position": { "line": 11, "character": 24 },
|
||||||
"label": "radix:",
|
"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,
|
"kind": 2,
|
||||||
"paddingRight": true
|
"paddingRight": true,
|
||||||
}, {
|
}, {
|
||||||
"position": { "line": 12, "character": 15 },
|
"position": { "line": 13, "character": 15 },
|
||||||
"label": ": number",
|
"label": [{ "value": ": " }, { "value": "number" }],
|
||||||
"kind": 1,
|
"kind": 1,
|
||||||
"paddingLeft": true
|
"paddingLeft": true,
|
||||||
}, {
|
}, {
|
||||||
"position": { "line": 15, "character": 11 },
|
"position": { "line": 16, "character": 11 },
|
||||||
"label": ": number",
|
"label": [{ "value": ": " }, { "value": "number" }],
|
||||||
"kind": 1,
|
"kind": 1,
|
||||||
"paddingLeft": true
|
"paddingLeft": true,
|
||||||
}, {
|
}, {
|
||||||
"position": { "line": 18, "character": 18 },
|
"position": { "line": 19, "character": 18 },
|
||||||
"label": "callbackfn:",
|
"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,
|
"kind": 2,
|
||||||
"paddingRight": true
|
"paddingRight": true,
|
||||||
}, {
|
}, {
|
||||||
"position": { "line": 18, "character": 20 },
|
"position": { "line": 19, "character": 20 },
|
||||||
"label": ": string",
|
"label": [{ "value": ": " }, { "value": "string" }],
|
||||||
"kind": 1,
|
"kind": 1,
|
||||||
"paddingLeft": true
|
"paddingLeft": true,
|
||||||
}, {
|
}, {
|
||||||
"position": { "line": 18, "character": 21 },
|
"position": { "line": 19, "character": 21 },
|
||||||
"label": ": string",
|
"label": [{ "value": ": " }, { "value": "string" }],
|
||||||
"kind": 1,
|
"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();
|
client.shutdown();
|
||||||
}
|
}
|
||||||
|
|
|
@ -308,34 +308,6 @@ impl InitializeParamsBuilder {
|
||||||
self
|
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 {
|
pub fn disable_testing_api(&mut self) -> &mut Self {
|
||||||
let obj = self
|
let obj = self
|
||||||
.params
|
.params
|
||||||
|
|
Loading…
Reference in a new issue