From bac02cd9684e79b82c2e7ede0ebef495872418b3 Mon Sep 17 00:00:00 2001 From: shruti2522 Date: Sun, 23 Jun 2024 05:31:37 +0530 Subject: [PATCH 1/3] feat: add lsp inaly-hint provider Signed-off-by: shruti2522 --- kclvm/tools/src/LSP/Cargo.toml | 2 +- kclvm/tools/src/LSP/src/capabilities.rs | 13 ++- kclvm/tools/src/LSP/src/inlay_hints.rs | 106 ++++++++++++++++++++++++ kclvm/tools/src/LSP/src/lib.rs | 1 + kclvm/tools/src/LSP/src/main.rs | 2 + kclvm/tools/src/LSP/src/request.rs | 27 +++++- 6 files changed, 146 insertions(+), 5 deletions(-) create mode 100644 kclvm/tools/src/LSP/src/inlay_hints.rs diff --git a/kclvm/tools/src/LSP/Cargo.toml b/kclvm/tools/src/LSP/Cargo.toml index f396e7fed..ce73b928c 100644 --- a/kclvm/tools/src/LSP/Cargo.toml +++ b/kclvm/tools/src/LSP/Cargo.toml @@ -37,7 +37,7 @@ anyhow = { version = "1.0", default-features = false, features = ["std"] } crossbeam-channel = { version = "0.5.7", default-features = false } ra_ap_vfs = "0.0.149" ra_ap_vfs-notify = "0.0.149" -lsp-types = { version = "0.93.0", default-features = false } +lsp-types = { version = "0.93.0", features = ["proposed"]} threadpool = { version = "1.8.1", default-features = false } salsa = { version = "0.16.1", default-features = false } serde_json = { version = "1.0", default-features = false } diff --git a/kclvm/tools/src/LSP/src/capabilities.rs b/kclvm/tools/src/LSP/src/capabilities.rs index 6f0f0086c..f7f5eeeb4 100644 --- a/kclvm/tools/src/LSP/src/capabilities.rs +++ b/kclvm/tools/src/LSP/src/capabilities.rs @@ -1,8 +1,8 @@ use lsp_types::{ ClientCapabilities, CodeActionKind, CodeActionOptions, CodeActionProviderCapability, - CompletionOptions, HoverProviderCapability, OneOf, SemanticTokensFullOptions, - SemanticTokensLegend, SemanticTokensOptions, ServerCapabilities, TextDocumentSyncCapability, - TextDocumentSyncKind, WorkDoneProgressOptions, + CompletionOptions, HoverProviderCapability, InlayHintOptions, InlayHintServerCapabilities, + OneOf, SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions, + ServerCapabilities, TextDocumentSyncCapability, TextDocumentSyncKind, WorkDoneProgressOptions, }; use crate::semantic_token::LEGEND_TYPE; @@ -38,6 +38,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti work_done_progress_options: WorkDoneProgressOptions { work_done_progress: None, }, + completion_item: None, }), hover_provider: Some(HoverProviderCapability::Simple(true)), definition_provider: Some(OneOf::Left(true)), @@ -62,6 +63,12 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti document_range_formatting_provider: Some(OneOf::Left(true)), references_provider: Some(OneOf::Left(true)), rename_provider: Some(OneOf::Left(true)), + inlay_hint_provider: Some(lsp_types::OneOf::Right( + InlayHintServerCapabilities::Options(InlayHintOptions { + resolve_provider: None, + work_done_progress_options: Default::default(), + }), + )), ..Default::default() } } diff --git a/kclvm/tools/src/LSP/src/inlay_hints.rs b/kclvm/tools/src/LSP/src/inlay_hints.rs new file mode 100644 index 000000000..cd2b0dd34 --- /dev/null +++ b/kclvm/tools/src/LSP/src/inlay_hints.rs @@ -0,0 +1,106 @@ +use kclvm_error::Position as KCLPos; +use kclvm_sema::core::{global_state::GlobalState, symbol::KCLSymbol}; +use kclvm_sema::ty::TypeKind; +use lsp_types::{InlayHint, InlayHintLabelPart, Position as LspPosition, Range}; +use std::convert::TryInto; + +pub fn inlay_hints(file: &str, gs: &GlobalState) -> Option> { + let mut inlay_hints: Vec = vec![]; + let sema_db = gs.get_sema_db(); + if let Some(file_sema) = sema_db.get_file_sema(file) { + let symbols = file_sema.get_symbols(); + for symbol_ref in symbols { + if let Some(symbol) = gs.get_symbols().get_symbol(*symbol_ref) { + let (start, end) = symbol.get_range(); + if let Some(hint) = generate_inlay_hint(symbol, gs, &start, &end) { + inlay_hints.push(hint); + } + } + } + } + Some(inlay_hints) +} + +fn generate_inlay_hint( + symbol: &KCLSymbol, + gs: &GlobalState, + start: &KCLPos, + end: &KCLPos, +) -> Option { + match get_hint_label(symbol, gs) { + Some(label_parts) => { + let range = Range { + start: LspPosition::new( + (start.line - 1).try_into().unwrap(), + start.column.unwrap_or(0).try_into().unwrap(), + ), + end: LspPosition::new( + (end.line - 1).try_into().unwrap(), + end.column.unwrap_or(0).try_into().unwrap(), + ), + }; + Some(InlayHint { + position: range.end, + label: lsp_types::InlayHintLabel::LabelParts(label_parts), + kind: None, + text_edits: None, + tooltip: None, + padding_left: Some(true), + padding_right: Some(true), + data: None, + }) + } + None => None, + } +} + +fn get_hint_label(symbol: &KCLSymbol, _gs: &GlobalState) -> Option> { + if let Some(ty) = &symbol.get_sema_info().ty { + let mut label_parts = Vec::new(); + + match &ty.kind { + TypeKind::Str + | TypeKind::Bool + | TypeKind::Int + | TypeKind::Float + | TypeKind::Any + | TypeKind::None + | TypeKind::Named(_) + | TypeKind::NumberMultiplier(_) + | TypeKind::Union(_) + | TypeKind::Dict(_) + | TypeKind::List(_) => { + label_parts.push(InlayHintLabelPart { + value: format!(": {}", ty.ty_str()), + ..Default::default() + }); + } + TypeKind::Module(module_ty) => { + label_parts.push(InlayHintLabelPart { + value: format!(": {}", module_ty.pkgpath), + ..Default::default() + }); + } + TypeKind::Function(_) => { + let symbol_name = symbol.get_name().to_string(); + label_parts.push(InlayHintLabelPart { + value: format!("fn {}", symbol_name), + ..Default::default() + }); + } + TypeKind::Schema(schema_ty) => { + let fully_qualified_ty_name = format!("schema {}", schema_ty.name); + label_parts.push(InlayHintLabelPart { + value: format!(": {}", fully_qualified_ty_name), + ..Default::default() + }); + } + _ => { + return None; + } + } + Some(label_parts) + } else { + None + } +} diff --git a/kclvm/tools/src/LSP/src/lib.rs b/kclvm/tools/src/LSP/src/lib.rs index 01cac1fd1..1a43b9a68 100644 --- a/kclvm/tools/src/LSP/src/lib.rs +++ b/kclvm/tools/src/LSP/src/lib.rs @@ -9,6 +9,7 @@ mod formatting; mod from_lsp; mod goto_def; mod hover; +mod inlay_hints; mod main_loop; mod notification; mod quick_fix; diff --git a/kclvm/tools/src/LSP/src/main.rs b/kclvm/tools/src/LSP/src/main.rs index 4ff88e992..5c887f292 100644 --- a/kclvm/tools/src/LSP/src/main.rs +++ b/kclvm/tools/src/LSP/src/main.rs @@ -11,6 +11,7 @@ mod find_refs; mod from_lsp; mod goto_def; mod hover; +mod inlay_hints; mod main_loop; mod notification; mod quick_fix; @@ -73,6 +74,7 @@ fn run_server() -> anyhow::Result<()> { name: String::from("kcl-language-server"), version: None, }), + offset_encoding: None, }; let initialize_result = serde_json::to_value(initialize_result) diff --git a/kclvm/tools/src/LSP/src/request.rs b/kclvm/tools/src/LSP/src/request.rs index 48bfb3fb0..596d2810c 100644 --- a/kclvm/tools/src/LSP/src/request.rs +++ b/kclvm/tools/src/LSP/src/request.rs @@ -18,7 +18,9 @@ use crate::{ formatting::format, from_lsp::{self, file_path_from_url, kcl_pos}, goto_def::goto_def, - hover, quick_fix, + hover, + inlay_hints::inlay_hints, + quick_fix, semantic_token::semantic_tokens_full, state::{log_message, LanguageServerSnapshot, LanguageServerState, Task}, }; @@ -58,6 +60,7 @@ impl LanguageServerState { .on::(handle_range_formatting)? .on::(handle_rename)? .on::(handle_semantic_tokens_full)? + .on::(handle_inlay_hint)? .finish(); Ok(()) @@ -439,3 +442,25 @@ pub(crate) fn handle_rename( } } } + +pub(crate) fn handle_inlay_hint( + snapshot: LanguageServerSnapshot, + params: lsp_types::InlayHintParams, + _sender: Sender, +) -> anyhow::Result>> { + let file = file_path_from_url(¶ms.text_document.uri)?; + let path = from_lsp::abs_path(¶ms.text_document.uri)?; + let db = match snapshot.try_get_db(&path.clone().into()) { + Ok(option_db) => match option_db { + Some(db) => db, + None => return Err(anyhow!(LSPError::Retry)), + }, + Err(_) => return Ok(None), + }; + let res = inlay_hints(&file, &db.gs); + + if !snapshot.verify_request_version(db.version, &path)? { + return Err(anyhow!(LSPError::Retry)); + } + Ok(res) +} From dfd272df794ca40bf0db7d8b06ef9316575392cb Mon Sep 17 00:00:00 2001 From: shruti2522 Date: Fri, 28 Jun 2024 18:03:27 +0530 Subject: [PATCH 2/3] update inlay hint provider capability Signed-off-by: shruti2522 --- kclvm/tools/src/LSP/src/capabilities.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/kclvm/tools/src/LSP/src/capabilities.rs b/kclvm/tools/src/LSP/src/capabilities.rs index f7f5eeeb4..17d83f413 100644 --- a/kclvm/tools/src/LSP/src/capabilities.rs +++ b/kclvm/tools/src/LSP/src/capabilities.rs @@ -1,8 +1,8 @@ use lsp_types::{ ClientCapabilities, CodeActionKind, CodeActionOptions, CodeActionProviderCapability, - CompletionOptions, HoverProviderCapability, InlayHintOptions, InlayHintServerCapabilities, - OneOf, SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions, - ServerCapabilities, TextDocumentSyncCapability, TextDocumentSyncKind, WorkDoneProgressOptions, + CompletionOptions, HoverProviderCapability, OneOf, SemanticTokensFullOptions, + SemanticTokensLegend, SemanticTokensOptions, ServerCapabilities, TextDocumentSyncCapability, + TextDocumentSyncKind, WorkDoneProgressOptions, }; use crate::semantic_token::LEGEND_TYPE; @@ -63,12 +63,7 @@ pub fn server_capabilities(client_caps: &ClientCapabilities) -> ServerCapabiliti document_range_formatting_provider: Some(OneOf::Left(true)), references_provider: Some(OneOf::Left(true)), rename_provider: Some(OneOf::Left(true)), - inlay_hint_provider: Some(lsp_types::OneOf::Right( - InlayHintServerCapabilities::Options(InlayHintOptions { - resolve_provider: None, - work_done_progress_options: Default::default(), - }), - )), + inlay_hint_provider: Some(lsp_types::OneOf::Left(true)), ..Default::default() } } From bacd47adc064d7b4866c67933e3b6e273e1f2f3b Mon Sep 17 00:00:00 2001 From: shruti2522 Date: Tue, 2 Jul 2024 21:09:14 +0530 Subject: [PATCH 3/3] inlay hints for str, int, float, bool, any Signed-off-by: shruti2522 --- kclvm/tools/src/LSP/src/inlay_hints.rs | 60 +++++++++++--------------- kclvm/tools/src/LSP/src/request.rs | 2 +- 2 files changed, 26 insertions(+), 36 deletions(-) diff --git a/kclvm/tools/src/LSP/src/inlay_hints.rs b/kclvm/tools/src/LSP/src/inlay_hints.rs index cd2b0dd34..c2363756d 100644 --- a/kclvm/tools/src/LSP/src/inlay_hints.rs +++ b/kclvm/tools/src/LSP/src/inlay_hints.rs @@ -1,10 +1,12 @@ +use kclvm_ast::ast::{self, Program}; +use kclvm_ast::pos::GetPos; use kclvm_error::Position as KCLPos; use kclvm_sema::core::{global_state::GlobalState, symbol::KCLSymbol}; use kclvm_sema::ty::TypeKind; use lsp_types::{InlayHint, InlayHintLabelPart, Position as LspPosition, Range}; use std::convert::TryInto; -pub fn inlay_hints(file: &str, gs: &GlobalState) -> Option> { +pub fn inlay_hints(file: &str, gs: &GlobalState, program: &Program) -> Option> { let mut inlay_hints: Vec = vec![]; let sema_db = gs.get_sema_db(); if let Some(file_sema) = sema_db.get_file_sema(file) { @@ -12,8 +14,10 @@ pub fn inlay_hints(file: &str, gs: &GlobalState) -> Option> { for symbol_ref in symbols { if let Some(symbol) = gs.get_symbols().get_symbol(*symbol_ref) { let (start, end) = symbol.get_range(); - if let Some(hint) = generate_inlay_hint(symbol, gs, &start, &end) { - inlay_hints.push(hint); + if has_type_assignment(program, &start) { + if let Some(hint) = generate_inlay_hint(symbol, gs, &start, &end) { + inlay_hints.push(hint); + } } } } @@ -21,6 +25,22 @@ pub fn inlay_hints(file: &str, gs: &GlobalState) -> Option> { Some(inlay_hints) } +fn has_type_assignment(program: &Program, start: &KCLPos) -> bool { + if let Some(stmt_node) = program.pos_to_stmt(start) { + if let ast::Stmt::Assign(assign_stmt) = stmt_node.node { + if assign_stmt + .targets + .iter() + .any(|target| target.get_pos() == *start) + && assign_stmt.ty.is_none() + { + return true; + } + } + } + false +} + fn generate_inlay_hint( symbol: &KCLSymbol, gs: &GlobalState, @@ -59,39 +79,9 @@ fn get_hint_label(symbol: &KCLSymbol, _gs: &GlobalState) -> Option { - label_parts.push(InlayHintLabelPart { - value: format!(": {}", ty.ty_str()), - ..Default::default() - }); - } - TypeKind::Module(module_ty) => { - label_parts.push(InlayHintLabelPart { - value: format!(": {}", module_ty.pkgpath), - ..Default::default() - }); - } - TypeKind::Function(_) => { - let symbol_name = symbol.get_name().to_string(); - label_parts.push(InlayHintLabelPart { - value: format!("fn {}", symbol_name), - ..Default::default() - }); - } - TypeKind::Schema(schema_ty) => { - let fully_qualified_ty_name = format!("schema {}", schema_ty.name); + TypeKind::Str | TypeKind::Bool | TypeKind::Int | TypeKind::Float | TypeKind::Any => { label_parts.push(InlayHintLabelPart { - value: format!(": {}", fully_qualified_ty_name), + value: format!("[: {}]", ty.ty_str()), ..Default::default() }); } diff --git a/kclvm/tools/src/LSP/src/request.rs b/kclvm/tools/src/LSP/src/request.rs index 596d2810c..b4ad85a40 100644 --- a/kclvm/tools/src/LSP/src/request.rs +++ b/kclvm/tools/src/LSP/src/request.rs @@ -457,7 +457,7 @@ pub(crate) fn handle_inlay_hint( }, Err(_) => return Ok(None), }; - let res = inlay_hints(&file, &db.gs); + let res = inlay_hints(&file, &db.gs, &db.prog); if !snapshot.verify_request_version(db.version, &path)? { return Err(anyhow!(LSPError::Retry));