More lsp endpoints we were missing (#6612)
* add prepare rename Signed-off-by: Jess Frazelle <github@jessfraz.com> * add document color Signed-off-by: Jess Frazelle <github@jessfraz.com> * updates Signed-off-by: Jess Frazelle <github@jessfraz.com> --------- Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
30
flake.lock
generated
30
flake.lock
generated
@ -5,11 +5,11 @@
|
|||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1743800763,
|
"lastModified": 1745925850,
|
||||||
"narHash": "sha256-YFKV+fxEpMgP5VsUcM6Il28lI0NlpM7+oB1XxbBAYCw=",
|
"narHash": "sha256-cyAAMal0aPrlb1NgzMxZqeN1mAJ2pJseDhm2m6Um8T0=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "naersk",
|
"repo": "naersk",
|
||||||
"rev": "ed0232117731a4c19d3ee93aa0c382a8fe754b01",
|
"rev": "38bc60bbc157ae266d4a0c96671c6c742ee17a5f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -20,11 +20,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1744316434,
|
"lastModified": 1745998881,
|
||||||
"narHash": "sha256-lzFCg/1C39pyY2hMB2gcuHV79ozpOz/Vu15hdjiFOfI=",
|
"narHash": "sha256-vonyYAKJSlsX4n9GCsS0pHxR6yCrfqBIuGvANlkwG6U=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "d19cf9dfc633816a437204555afeb9e722386b76",
|
"rev": "423d2df5b04b4ee7688c3d71396e872afa236a89",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -36,11 +36,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1744316434,
|
"lastModified": 1745998881,
|
||||||
"narHash": "sha256-lzFCg/1C39pyY2hMB2gcuHV79ozpOz/Vu15hdjiFOfI=",
|
"narHash": "sha256-vonyYAKJSlsX4n9GCsS0pHxR6yCrfqBIuGvANlkwG6U=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "d19cf9dfc633816a437204555afeb9e722386b76",
|
"rev": "423d2df5b04b4ee7688c3d71396e872afa236a89",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -52,11 +52,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_3": {
|
"nixpkgs_3": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1736320768,
|
"lastModified": 1744536153,
|
||||||
"narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=",
|
"narHash": "sha256-awS2zRgF4uTwrOKwwiJcByDzDOdo3Q1rPZbiHQg/N38=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "4bc9c909d9ac828a039f288cf872d16d38185db8",
|
"rev": "18dd725c29603f582cf1900e0d25f9f1063dbf11",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -78,11 +78,11 @@
|
|||||||
"nixpkgs": "nixpkgs_3"
|
"nixpkgs": "nixpkgs_3"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1744338850,
|
"lastModified": 1745980514,
|
||||||
"narHash": "sha256-pwMIVmsb8fjjT92n5XFDqCsplcX70qVMMT7NulumPXs=",
|
"narHash": "sha256-CITAeiuXGjDvT5iZBXr6vKVWQwsUQLJUMFO91bfJFC4=",
|
||||||
"owner": "oxalica",
|
"owner": "oxalica",
|
||||||
"repo": "rust-overlay",
|
"repo": "rust-overlay",
|
||||||
"rev": "5e64aecc018e6f775572609e7d7485fdba6985a7",
|
"rev": "7fbdae44b0f40ea432e46fd152ad8be0f8f41ad6",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@ -547,19 +547,10 @@ export class LanguageServerPlugin implements PluginValue {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// First check if rename is possible at this position
|
// First check if rename is possible at this position
|
||||||
const prepareResult = await this.client
|
let prepareResult = this.prepareRenameFallback(view, {
|
||||||
.textDocumentPrepareRename({
|
|
||||||
textDocument: { uri: this.getDocUri() },
|
|
||||||
position: { line, character },
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
// In case prepareRename is not supported,
|
|
||||||
// we fallback to the default implementation
|
|
||||||
return this.prepareRenameFallback(view, {
|
|
||||||
line,
|
line,
|
||||||
character,
|
character,
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
if (!prepareResult || 'defaultBehavior' in prepareResult) {
|
if (!prepareResult || 'defaultBehavior' in prepareResult) {
|
||||||
showErrorMessage(view, 'Cannot rename this symbol')
|
showErrorMessage(view, 'Cannot rename this symbol')
|
||||||
|
58
rust/Cargo.lock
generated
58
rust/Cargo.lock
generated
@ -713,6 +713,15 @@ dependencies = [
|
|||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "csscolorparser"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46f9a16a848a7fb95dd47ce387ac1ee9a6df879ba784b815537fcd388a1a8288"
|
||||||
|
dependencies = [
|
||||||
|
"phf",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.20.10"
|
version = "0.20.10"
|
||||||
@ -1899,6 +1908,7 @@ dependencies = [
|
|||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"convert_case",
|
"convert_case",
|
||||||
"criterion",
|
"criterion",
|
||||||
|
"csscolorparser",
|
||||||
"dashmap 6.1.0",
|
"dashmap 6.1.0",
|
||||||
"dhat",
|
"dhat",
|
||||||
"expectorate",
|
"expectorate",
|
||||||
@ -2630,6 +2640,48 @@ dependencies = [
|
|||||||
"sha2",
|
"sha2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078"
|
||||||
|
dependencies = [
|
||||||
|
"phf_macros",
|
||||||
|
"phf_shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_generator"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
|
||||||
|
dependencies = [
|
||||||
|
"phf_shared",
|
||||||
|
"rand 0.8.5",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_macros"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216"
|
||||||
|
dependencies = [
|
||||||
|
"phf_generator",
|
||||||
|
"phf_shared",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.100",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "phf_shared"
|
||||||
|
version = "0.11.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
|
||||||
|
dependencies = [
|
||||||
|
"siphasher",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "phonenumber"
|
name = "phonenumber"
|
||||||
version = "0.3.7+8.13.52"
|
version = "0.3.7+8.13.52"
|
||||||
@ -3575,6 +3627,12 @@ version = "2.7.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
|
checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "siphasher"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.9"
|
version = "0.4.9"
|
||||||
|
@ -31,6 +31,7 @@ clap = { version = "4.5.36", default-features = false, optional = true, features
|
|||||||
"derive",
|
"derive",
|
||||||
] }
|
] }
|
||||||
convert_case = "0.8.0"
|
convert_case = "0.8.0"
|
||||||
|
csscolorparser = "0.7.0"
|
||||||
dashmap = { workspace = true }
|
dashmap = { workspace = true }
|
||||||
dhat = { version = "0.3", optional = true }
|
dhat = { version = "0.3", optional = true }
|
||||||
fnv = "1.0.7"
|
fnv = "1.0.7"
|
||||||
|
@ -8,12 +8,11 @@ use parse_display::{Display, FromStr};
|
|||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::engine::{PlaneName, DEFAULT_PLANE_INFO};
|
|
||||||
use crate::errors::KclErrorDetails;
|
|
||||||
#[cfg(feature = "artifact-graph")]
|
#[cfg(feature = "artifact-graph")]
|
||||||
use crate::execution::ArtifactId;
|
use crate::execution::ArtifactId;
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::KclError,
|
engine::{PlaneName, DEFAULT_PLANE_INFO},
|
||||||
|
errors::{KclError, KclErrorDetails},
|
||||||
execution::{types::NumericType, ExecState, ExecutorContext, Metadata, TagEngineInfo, TagIdentifier, UnitLen},
|
execution::{types::NumericType, ExecState, ExecutorContext, Metadata, TagEngineInfo, TagIdentifier, UnitLen},
|
||||||
parsing::ast::types::{Node, NodeRef, TagDeclarator, TagNode},
|
parsing::ast::types::{Node, NodeRef, TagDeclarator, TagNode},
|
||||||
std::{args::TyF64, sketch::PlaneData},
|
std::{args::TyF64, sketch::PlaneData},
|
||||||
|
@ -2,13 +2,14 @@ use anyhow::Result;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::Suggestion,
|
errors::Suggestion,
|
||||||
lint::rule::{def_finding, Discovered, Finding},
|
lint::{
|
||||||
|
checks::offset_plane::start_sketch_on_check_specific_plane,
|
||||||
|
rule::{def_finding, Discovered, Finding},
|
||||||
|
},
|
||||||
parsing::ast::types::{Node as AstNode, Program},
|
parsing::ast::types::{Node as AstNode, Program},
|
||||||
walk::Node,
|
walk::Node,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::offset_plane::start_sketch_on_check_specific_plane;
|
|
||||||
|
|
||||||
def_finding!(
|
def_finding!(
|
||||||
Z0002,
|
Z0002,
|
||||||
"default plane should be called versus explicitly defined",
|
"default plane should be called versus explicitly defined",
|
||||||
|
@ -18,23 +18,24 @@ use tower_lsp::{
|
|||||||
jsonrpc::Result as RpcResult,
|
jsonrpc::Result as RpcResult,
|
||||||
lsp_types::{
|
lsp_types::{
|
||||||
CodeAction, CodeActionKind, CodeActionOptions, CodeActionOrCommand, CodeActionParams,
|
CodeAction, CodeActionKind, CodeActionOptions, CodeActionOrCommand, CodeActionParams,
|
||||||
CodeActionProviderCapability, CodeActionResponse, CompletionItem, CompletionItemKind, CompletionOptions,
|
CodeActionProviderCapability, CodeActionResponse, ColorInformation, ColorPresentation, ColorPresentationParams,
|
||||||
CompletionParams, CompletionResponse, CreateFilesParams, DeleteFilesParams, Diagnostic, DiagnosticOptions,
|
ColorProviderCapability, CompletionItem, CompletionItemKind, CompletionOptions, CompletionParams,
|
||||||
|
CompletionResponse, CreateFilesParams, DeleteFilesParams, Diagnostic, DiagnosticOptions,
|
||||||
DiagnosticServerCapabilities, DiagnosticSeverity, DidChangeConfigurationParams, DidChangeTextDocumentParams,
|
DiagnosticServerCapabilities, DiagnosticSeverity, DidChangeConfigurationParams, DidChangeTextDocumentParams,
|
||||||
DidChangeWatchedFilesParams, DidChangeWorkspaceFoldersParams, DidCloseTextDocumentParams,
|
DidChangeWatchedFilesParams, DidChangeWorkspaceFoldersParams, DidCloseTextDocumentParams,
|
||||||
DidOpenTextDocumentParams, DidSaveTextDocumentParams, DocumentDiagnosticParams, DocumentDiagnosticReport,
|
DidOpenTextDocumentParams, DidSaveTextDocumentParams, DocumentColorParams, DocumentDiagnosticParams,
|
||||||
DocumentDiagnosticReportResult, DocumentFilter, DocumentFormattingParams, DocumentSymbol, DocumentSymbolParams,
|
DocumentDiagnosticReport, DocumentDiagnosticReportResult, DocumentFilter, DocumentFormattingParams,
|
||||||
DocumentSymbolResponse, Documentation, FoldingRange, FoldingRangeParams, FoldingRangeProviderCapability,
|
DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse, Documentation, FoldingRange, FoldingRangeParams,
|
||||||
FullDocumentDiagnosticReport, Hover as LspHover, HoverContents, HoverParams, HoverProviderCapability,
|
FoldingRangeProviderCapability, FullDocumentDiagnosticReport, Hover as LspHover, HoverContents, HoverParams,
|
||||||
InitializeParams, InitializeResult, InitializedParams, InlayHint, InlayHintParams, InsertTextFormat,
|
HoverProviderCapability, InitializeParams, InitializeResult, InitializedParams, InlayHint, InlayHintParams,
|
||||||
MarkupContent, MarkupKind, MessageType, OneOf, Position, RelatedFullDocumentDiagnosticReport,
|
InsertTextFormat, MarkupContent, MarkupKind, MessageType, OneOf, Position, PrepareRenameResponse,
|
||||||
RenameFilesParams, RenameParams, SemanticToken, SemanticTokenModifier, SemanticTokenType, SemanticTokens,
|
RelatedFullDocumentDiagnosticReport, RenameFilesParams, RenameParams, SemanticToken, SemanticTokenModifier,
|
||||||
SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions, SemanticTokensParams,
|
SemanticTokenType, SemanticTokens, SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions,
|
||||||
SemanticTokensRegistrationOptions, SemanticTokensResult, SemanticTokensServerCapabilities, ServerCapabilities,
|
SemanticTokensParams, SemanticTokensRegistrationOptions, SemanticTokensResult,
|
||||||
SignatureHelp, SignatureHelpOptions, SignatureHelpParams, StaticRegistrationOptions, TextDocumentItem,
|
SemanticTokensServerCapabilities, ServerCapabilities, SignatureHelp, SignatureHelpOptions, SignatureHelpParams,
|
||||||
TextDocumentRegistrationOptions, TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
|
StaticRegistrationOptions, TextDocumentItem, TextDocumentPositionParams, TextDocumentRegistrationOptions,
|
||||||
TextEdit, WorkDoneProgressOptions, WorkspaceEdit, WorkspaceFolder, WorkspaceFoldersServerCapabilities,
|
TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, TextEdit, WorkDoneProgressOptions,
|
||||||
WorkspaceServerCapabilities,
|
WorkspaceEdit, WorkspaceFolder, WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities,
|
||||||
},
|
},
|
||||||
Client, LanguageServer,
|
Client, LanguageServer,
|
||||||
};
|
};
|
||||||
@ -827,6 +828,39 @@ impl Backend {
|
|||||||
|
|
||||||
Ok(custom_notifications::UpdateCanExecuteResponse {})
|
Ok(custom_notifications::UpdateCanExecuteResponse {})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the new string for the code after rename.
|
||||||
|
pub fn inner_prepare_rename(
|
||||||
|
&self,
|
||||||
|
params: &TextDocumentPositionParams,
|
||||||
|
new_name: &str,
|
||||||
|
) -> RpcResult<Option<(String, String)>> {
|
||||||
|
let filename = params.text_document.uri.to_string();
|
||||||
|
|
||||||
|
let Some(current_code) = self.code_map.get(&filename) else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
let Ok(current_code) = std::str::from_utf8(¤t_code) else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Parse the ast.
|
||||||
|
// I don't know if we need to do this again since it should be updated in the context.
|
||||||
|
// But I figure better safe than sorry since this will write back out to the file.
|
||||||
|
let module_id = ModuleId::default();
|
||||||
|
let Ok(mut ast) = crate::parsing::parse_str(current_code, module_id).parse_errs_as_err() else {
|
||||||
|
return Ok(None);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Let's convert the position to a character index.
|
||||||
|
let pos = position_to_char_index(params.position, current_code);
|
||||||
|
// Now let's perform the rename on the ast.
|
||||||
|
ast.rename_symbol(new_name, pos);
|
||||||
|
// Now recast it.
|
||||||
|
let recast = ast.recast(&Default::default(), 0);
|
||||||
|
|
||||||
|
Ok(Some((current_code.to_string(), recast)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tower_lsp::async_trait]
|
#[tower_lsp::async_trait]
|
||||||
@ -838,6 +872,7 @@ impl LanguageServer for Backend {
|
|||||||
|
|
||||||
Ok(InitializeResult {
|
Ok(InitializeResult {
|
||||||
capabilities: ServerCapabilities {
|
capabilities: ServerCapabilities {
|
||||||
|
color_provider: Some(ColorProviderCapability::Simple(true)),
|
||||||
code_action_provider: Some(CodeActionProviderCapability::Options(CodeActionOptions {
|
code_action_provider: Some(CodeActionProviderCapability::Options(CodeActionOptions {
|
||||||
code_action_kinds: Some(vec![CodeActionKind::QUICKFIX]),
|
code_action_kinds: Some(vec![CodeActionKind::QUICKFIX]),
|
||||||
resolve_provider: Some(false),
|
resolve_provider: Some(false),
|
||||||
@ -1459,36 +1494,19 @@ impl LanguageServer for Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn rename(&self, params: RenameParams) -> RpcResult<Option<WorkspaceEdit>> {
|
async fn rename(&self, params: RenameParams) -> RpcResult<Option<WorkspaceEdit>> {
|
||||||
let filename = params.text_document_position.text_document.uri.to_string();
|
let Some((current_code, new_code)) =
|
||||||
|
self.inner_prepare_rename(¶ms.text_document_position, ¶ms.new_name)?
|
||||||
let Some(current_code) = self.code_map.get(&filename) else {
|
else {
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
let Ok(current_code) = std::str::from_utf8(¤t_code) else {
|
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse the ast.
|
let source_range = SourceRange::new(0, current_code.len(), ModuleId::default());
|
||||||
// I don't know if we need to do this again since it should be updated in the context.
|
let range = source_range.to_lsp_range(¤t_code);
|
||||||
// But I figure better safe than sorry since this will write back out to the file.
|
|
||||||
let module_id = ModuleId::default();
|
|
||||||
let Ok(mut ast) = crate::parsing::parse_str(current_code, module_id).parse_errs_as_err() else {
|
|
||||||
return Ok(None);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Let's convert the position to a character index.
|
|
||||||
let pos = position_to_char_index(params.text_document_position.position, current_code);
|
|
||||||
// Now let's perform the rename on the ast.
|
|
||||||
ast.rename_symbol(¶ms.new_name, pos);
|
|
||||||
// Now recast it.
|
|
||||||
let recast = ast.recast(&Default::default(), 0);
|
|
||||||
let source_range = SourceRange::new(0, current_code.len(), module_id);
|
|
||||||
let range = source_range.to_lsp_range(current_code);
|
|
||||||
Ok(Some(WorkspaceEdit {
|
Ok(Some(WorkspaceEdit {
|
||||||
changes: Some(HashMap::from([(
|
changes: Some(HashMap::from([(
|
||||||
params.text_document_position.text_document.uri,
|
params.text_document_position.text_document.uri,
|
||||||
vec![TextEdit {
|
vec![TextEdit {
|
||||||
new_text: recast,
|
new_text: new_code,
|
||||||
range,
|
range,
|
||||||
}],
|
}],
|
||||||
)])),
|
)])),
|
||||||
@ -1497,6 +1515,18 @@ impl LanguageServer for Backend {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn prepare_rename(&self, params: TextDocumentPositionParams) -> RpcResult<Option<PrepareRenameResponse>> {
|
||||||
|
if self
|
||||||
|
.inner_prepare_rename(¶ms, "someNameNoOneInTheirRightMindWouldEverUseForTesting")?
|
||||||
|
.is_none()
|
||||||
|
{
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return back to the client, that it is safe to use the rename behavior.
|
||||||
|
Ok(Some(PrepareRenameResponse::DefaultBehavior { default_behavior: true }))
|
||||||
|
}
|
||||||
|
|
||||||
async fn folding_range(&self, params: FoldingRangeParams) -> RpcResult<Option<Vec<FoldingRange>>> {
|
async fn folding_range(&self, params: FoldingRangeParams) -> RpcResult<Option<Vec<FoldingRange>>> {
|
||||||
let filename = params.text_document.uri.to_string();
|
let filename = params.text_document.uri.to_string();
|
||||||
|
|
||||||
@ -1552,6 +1582,55 @@ impl LanguageServer for Backend {
|
|||||||
|
|
||||||
Ok(Some(actions))
|
Ok(Some(actions))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn document_color(&self, params: DocumentColorParams) -> RpcResult<Vec<ColorInformation>> {
|
||||||
|
let filename = params.text_document.uri.to_string();
|
||||||
|
|
||||||
|
let Some(current_code) = self.code_map.get(&filename) else {
|
||||||
|
return Ok(vec![]);
|
||||||
|
};
|
||||||
|
let Ok(current_code) = std::str::from_utf8(¤t_code) else {
|
||||||
|
return Ok(vec![]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the ast from our map.
|
||||||
|
let Some(ast) = self.ast_map.get(&filename) else {
|
||||||
|
return Ok(vec![]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the colors from the ast.
|
||||||
|
let Ok(colors) = ast.ast.document_color(current_code) else {
|
||||||
|
return Ok(vec![]);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(colors)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn color_presentation(&self, params: ColorPresentationParams) -> RpcResult<Vec<ColorPresentation>> {
|
||||||
|
let filename = params.text_document.uri.to_string();
|
||||||
|
|
||||||
|
let Some(current_code) = self.code_map.get(&filename) else {
|
||||||
|
return Ok(vec![]);
|
||||||
|
};
|
||||||
|
let Ok(current_code) = std::str::from_utf8(¤t_code) else {
|
||||||
|
return Ok(vec![]);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the ast from our map.
|
||||||
|
let Some(ast) = self.ast_map.get(&filename) else {
|
||||||
|
return Ok(vec![]);
|
||||||
|
};
|
||||||
|
|
||||||
|
let pos_start = position_to_char_index(params.range.start, current_code);
|
||||||
|
let pos_end = position_to_char_index(params.range.end, current_code);
|
||||||
|
|
||||||
|
// Get the colors from the ast.
|
||||||
|
let Ok(Some(presentation)) = ast.ast.color_presentation(¶ms.color, pos_start, pos_end) else {
|
||||||
|
return Ok(vec![]);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(vec![presentation])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get completions from our stdlib.
|
/// Get completions from our stdlib.
|
||||||
@ -1662,3 +1741,48 @@ async fn with_cached_var<T>(name: &str, f: impl Fn(&KclValue) -> T) -> Option<T>
|
|||||||
|
|
||||||
Some(f(value))
|
Some(f(value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_position_to_char_index_first_line() {
|
||||||
|
let code = r#"def foo():
|
||||||
|
return 42"#;
|
||||||
|
let position = Position::new(0, 3);
|
||||||
|
let index = position_to_char_index(position, code);
|
||||||
|
assert_eq!(index, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_position_to_char_index() {
|
||||||
|
let code = r#"def foo():
|
||||||
|
return 42"#;
|
||||||
|
let position = Position::new(1, 4);
|
||||||
|
let index = position_to_char_index(position, code);
|
||||||
|
assert_eq!(index, 15);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_position_to_char_index_with_newline() {
|
||||||
|
let code = r#"def foo():
|
||||||
|
|
||||||
|
return 42"#;
|
||||||
|
let position = Position::new(2, 0);
|
||||||
|
let index = position_to_char_index(position, code);
|
||||||
|
assert_eq!(index, 12);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_position_to_char_at_end() {
|
||||||
|
let code = r#"def foo():
|
||||||
|
return 42"#;
|
||||||
|
|
||||||
|
let position = Position::new(1, 8);
|
||||||
|
let index = position_to_char_index(position, code);
|
||||||
|
assert_eq!(index, 19);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -3,8 +3,8 @@ use std::collections::{BTreeMap, HashMap};
|
|||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
use tower_lsp::{
|
use tower_lsp::{
|
||||||
lsp_types::{
|
lsp_types::{
|
||||||
CodeActionKind, CodeActionOrCommand, Diagnostic, SemanticTokenModifier, SemanticTokenType, TextEdit,
|
CodeActionKind, CodeActionOrCommand, Diagnostic, PrepareRenameResponse, SemanticTokenModifier,
|
||||||
WorkspaceEdit,
|
SemanticTokenType, TextEdit, WorkspaceEdit,
|
||||||
},
|
},
|
||||||
LanguageServer,
|
LanguageServer,
|
||||||
};
|
};
|
||||||
@ -4146,3 +4146,173 @@ async fn kcl_test_kcl_lsp_code_actions_lint_offset_planes() {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_kcl_lsp_prepare_rename() {
|
||||||
|
let server = kcl_lsp_server(false).await.unwrap();
|
||||||
|
|
||||||
|
// Send open file.
|
||||||
|
server
|
||||||
|
.did_open(tower_lsp::lsp_types::DidOpenTextDocumentParams {
|
||||||
|
text_document: tower_lsp::lsp_types::TextDocumentItem {
|
||||||
|
uri: "file:///test.kcl".try_into().unwrap(),
|
||||||
|
language_id: "kcl".to_string(),
|
||||||
|
version: 1,
|
||||||
|
text: r#"thing= 1"#.to_string(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Send rename request.
|
||||||
|
let result = server
|
||||||
|
.prepare_rename(tower_lsp::lsp_types::TextDocumentPositionParams {
|
||||||
|
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
|
||||||
|
uri: "file:///test.kcl".try_into().unwrap(),
|
||||||
|
},
|
||||||
|
position: tower_lsp::lsp_types::Position { line: 0, character: 2 },
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Check the result.
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
PrepareRenameResponse::DefaultBehavior { default_behavior: true }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_kcl_lsp_document_color() {
|
||||||
|
let server = kcl_lsp_server(false).await.unwrap();
|
||||||
|
|
||||||
|
// Send open file.
|
||||||
|
server
|
||||||
|
.did_open(tower_lsp::lsp_types::DidOpenTextDocumentParams {
|
||||||
|
text_document: tower_lsp::lsp_types::TextDocumentItem {
|
||||||
|
uri: "file:///test.kcl".try_into().unwrap(),
|
||||||
|
language_id: "kcl".to_string(),
|
||||||
|
version: 1,
|
||||||
|
text: r#"// Add color to a revolved solid.
|
||||||
|
sketch001 = startSketchOn(XY)
|
||||||
|
|> circle(center = [15, 0], radius = 5)
|
||||||
|
|> revolve(angle = 360, axis = Y)
|
||||||
|
|> appearance(color = '#ff0000', metalness = 90, roughness = 90)"#
|
||||||
|
.to_string(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Send document color request.
|
||||||
|
let result = server
|
||||||
|
.document_color(tower_lsp::lsp_types::DocumentColorParams {
|
||||||
|
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
|
||||||
|
uri: "file:///test.kcl".try_into().unwrap(),
|
||||||
|
},
|
||||||
|
work_done_progress_params: Default::default(),
|
||||||
|
partial_result_params: Default::default(),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Check the result.
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
vec![tower_lsp::lsp_types::ColorInformation {
|
||||||
|
range: tower_lsp::lsp_types::Range {
|
||||||
|
start: tower_lsp::lsp_types::Position { line: 4, character: 24 },
|
||||||
|
end: tower_lsp::lsp_types::Position { line: 4, character: 33 },
|
||||||
|
},
|
||||||
|
color: tower_lsp::lsp_types::Color {
|
||||||
|
red: 1.0,
|
||||||
|
green: 0.0,
|
||||||
|
blue: 0.0,
|
||||||
|
alpha: 1.0,
|
||||||
|
},
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_kcl_lsp_color_presentation() {
|
||||||
|
let server = kcl_lsp_server(false).await.unwrap();
|
||||||
|
|
||||||
|
let text = r#"// Add color to a revolved solid.
|
||||||
|
sketch001 = startSketchOn(XY)
|
||||||
|
|> circle(center = [15, 0], radius = 5)
|
||||||
|
|> revolve(angle = 360, axis = Y)
|
||||||
|
|> appearance(color = '#ff0000', metalness = 90, roughness = 90)"#;
|
||||||
|
|
||||||
|
// Send open file.
|
||||||
|
server
|
||||||
|
.did_open(tower_lsp::lsp_types::DidOpenTextDocumentParams {
|
||||||
|
text_document: tower_lsp::lsp_types::TextDocumentItem {
|
||||||
|
uri: "file:///test.kcl".try_into().unwrap(),
|
||||||
|
language_id: "kcl".to_string(),
|
||||||
|
version: 1,
|
||||||
|
text: text.to_string(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.await;
|
||||||
|
|
||||||
|
// Send document color request.
|
||||||
|
let result = server
|
||||||
|
.document_color(tower_lsp::lsp_types::DocumentColorParams {
|
||||||
|
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
|
||||||
|
uri: "file:///test.kcl".try_into().unwrap(),
|
||||||
|
},
|
||||||
|
work_done_progress_params: Default::default(),
|
||||||
|
partial_result_params: Default::default(),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Check the result.
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
vec![tower_lsp::lsp_types::ColorInformation {
|
||||||
|
range: tower_lsp::lsp_types::Range {
|
||||||
|
start: tower_lsp::lsp_types::Position { line: 4, character: 24 },
|
||||||
|
end: tower_lsp::lsp_types::Position { line: 4, character: 33 },
|
||||||
|
},
|
||||||
|
color: tower_lsp::lsp_types::Color {
|
||||||
|
red: 1.0,
|
||||||
|
green: 0.0,
|
||||||
|
blue: 0.0,
|
||||||
|
alpha: 1.0,
|
||||||
|
},
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
|
||||||
|
// Send color presentation request.
|
||||||
|
let result = server
|
||||||
|
.color_presentation(tower_lsp::lsp_types::ColorPresentationParams {
|
||||||
|
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
|
||||||
|
uri: "file:///test.kcl".try_into().unwrap(),
|
||||||
|
},
|
||||||
|
range: tower_lsp::lsp_types::Range {
|
||||||
|
start: tower_lsp::lsp_types::Position { line: 4, character: 24 },
|
||||||
|
end: tower_lsp::lsp_types::Position { line: 4, character: 33 },
|
||||||
|
},
|
||||||
|
color: tower_lsp::lsp_types::Color {
|
||||||
|
red: 1.0,
|
||||||
|
green: 0.0,
|
||||||
|
blue: 1.0,
|
||||||
|
alpha: 1.0,
|
||||||
|
},
|
||||||
|
work_done_progress_params: Default::default(),
|
||||||
|
partial_result_params: Default::default(),
|
||||||
|
})
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Check the result.
|
||||||
|
assert_eq!(
|
||||||
|
result,
|
||||||
|
vec![tower_lsp::lsp_types::ColorPresentation {
|
||||||
|
label: "#ff00ff".to_string(),
|
||||||
|
text_edit: None,
|
||||||
|
additional_text_edits: None,
|
||||||
|
}]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use std::fmt;
|
use std::{fmt, str::FromStr};
|
||||||
|
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -32,6 +32,21 @@ impl LiteralValue {
|
|||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_color(&self) -> Option<csscolorparser::Color> {
|
||||||
|
if let Self::String(s) = self {
|
||||||
|
// Check if the string is a color.
|
||||||
|
if s.starts_with('#') && s.len() == 7 {
|
||||||
|
let Ok(c) = csscolorparser::Color::from_str(s) else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
|
||||||
|
return Some(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for LiteralValue {
|
impl fmt::Display for LiteralValue {
|
||||||
|
@ -14,7 +14,8 @@ use parse_display::{Display, FromStr};
|
|||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tower_lsp::lsp_types::{
|
use tower_lsp::lsp_types::{
|
||||||
CompletionItem, CompletionItemKind, DocumentSymbol, FoldingRange, FoldingRangeKind, SymbolKind,
|
Color, ColorInformation, ColorPresentation, CompletionItem, CompletionItemKind, DocumentSymbol, FoldingRange,
|
||||||
|
FoldingRangeKind, SymbolKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use crate::parsing::ast::types::{
|
pub use crate::parsing::ast::types::{
|
||||||
@ -389,6 +390,99 @@ impl Node<Program> {
|
|||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find all the color strings in the program.
|
||||||
|
/// For example `appearance(color = "#ff0000")`
|
||||||
|
/// This is to fulfill the `documentColor` request in LSP.
|
||||||
|
pub fn document_color<'a>(&'a self, code: &str) -> Result<Vec<ColorInformation>> {
|
||||||
|
let colors = Rc::new(RefCell::new(vec![]));
|
||||||
|
|
||||||
|
let add_color = |literal: &Node<Literal>| {
|
||||||
|
// Check if the string is a color.
|
||||||
|
if let Some(c) = literal.value.is_color() {
|
||||||
|
let color = ColorInformation {
|
||||||
|
range: literal.as_source_range().to_lsp_range(code),
|
||||||
|
color: tower_lsp::lsp_types::Color {
|
||||||
|
red: c.r,
|
||||||
|
green: c.g,
|
||||||
|
blue: c.b,
|
||||||
|
alpha: c.a,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if colors.borrow().iter().any(|c| *c == color) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
colors.borrow_mut().push(color);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The position must be within the variable declaration.
|
||||||
|
crate::walk::walk(self, |node: crate::walk::Node<'a>| {
|
||||||
|
match node {
|
||||||
|
crate::walk::Node::CallExpressionKw(call) => {
|
||||||
|
if call.inner.callee.inner.name.inner.name == "appearance" {
|
||||||
|
for arg in &call.arguments {
|
||||||
|
if arg.label.inner.name == "color" {
|
||||||
|
// Get the value of the argument.
|
||||||
|
if let Expr::Literal(literal) = &arg.arg {
|
||||||
|
add_color(literal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crate::walk::Node::Literal(literal) => {
|
||||||
|
// Check if the literal is a color.
|
||||||
|
add_color(literal);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok::<bool, anyhow::Error>(true)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let colors = colors.take();
|
||||||
|
Ok(colors)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is to fulfill the `colorPresentation` request in LSP.
|
||||||
|
pub fn color_presentation<'a>(
|
||||||
|
&'a self,
|
||||||
|
color: &Color,
|
||||||
|
pos_start: usize,
|
||||||
|
pos_end: usize,
|
||||||
|
) -> Result<Option<ColorPresentation>> {
|
||||||
|
let found = Rc::new(RefCell::new(false));
|
||||||
|
// Find the literal with the same start and end.
|
||||||
|
crate::walk::walk(self, |node: crate::walk::Node<'a>| {
|
||||||
|
match node {
|
||||||
|
crate::walk::Node::Literal(literal) => {
|
||||||
|
if literal.start == pos_start && literal.end == pos_end && literal.value.is_color().is_some() {
|
||||||
|
found.replace(true);
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Do nothing.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok::<bool, anyhow::Error>(true)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let found = found.take();
|
||||||
|
if !found {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_color = csscolorparser::Color::new(color.red, color.green, color.blue, color.alpha);
|
||||||
|
Ok(Some(ColorPresentation {
|
||||||
|
// The label will be what they replace the color with.
|
||||||
|
label: new_color.to_hex_string(),
|
||||||
|
text_edit: None,
|
||||||
|
additional_text_edits: None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Program {
|
impl Program {
|
||||||
|
Reference in New Issue
Block a user