kcl_lib/lsp/kcl/
mod.rs

1//! Functions for the `kcl` lsp server.
2#![allow(dead_code)]
3
4use std::{
5    collections::HashMap,
6    io::Write,
7    str::FromStr,
8    sync::{Arc, Mutex},
9};
10
11use anyhow::Result;
12#[cfg(feature = "cli")]
13use clap::Parser;
14use dashmap::DashMap;
15use sha2::Digest;
16use tokio::sync::RwLock;
17use tower_lsp::{
18    Client, LanguageServer,
19    jsonrpc::Result as RpcResult,
20    lsp_types::{
21        CodeAction, CodeActionKind, CodeActionOptions, CodeActionOrCommand, CodeActionParams,
22        CodeActionProviderCapability, CodeActionResponse, ColorInformation, ColorPresentation, ColorPresentationParams,
23        ColorProviderCapability, CompletionItem, CompletionItemKind, CompletionOptions, CompletionParams,
24        CompletionResponse, CreateFilesParams, DeleteFilesParams, Diagnostic, DiagnosticOptions,
25        DiagnosticServerCapabilities, DiagnosticSeverity, DidChangeConfigurationParams, DidChangeTextDocumentParams,
26        DidChangeWatchedFilesParams, DidChangeWorkspaceFoldersParams, DidCloseTextDocumentParams,
27        DidOpenTextDocumentParams, DidSaveTextDocumentParams, DocumentColorParams, DocumentDiagnosticParams,
28        DocumentDiagnosticReport, DocumentDiagnosticReportResult, DocumentFilter, DocumentFormattingParams,
29        DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse, Documentation, FoldingRange, FoldingRangeParams,
30        FoldingRangeProviderCapability, FullDocumentDiagnosticReport, Hover as LspHover, HoverContents, HoverParams,
31        HoverProviderCapability, InitializeParams, InitializeResult, InitializedParams, InlayHint, InlayHintParams,
32        InsertTextFormat, MarkupContent, MarkupKind, MessageType, OneOf, Position, PrepareRenameResponse,
33        RelatedFullDocumentDiagnosticReport, RenameFilesParams, RenameParams, SemanticToken, SemanticTokenModifier,
34        SemanticTokenType, SemanticTokens, SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions,
35        SemanticTokensParams, SemanticTokensRegistrationOptions, SemanticTokensResult,
36        SemanticTokensServerCapabilities, ServerCapabilities, SignatureHelp, SignatureHelpOptions, SignatureHelpParams,
37        StaticRegistrationOptions, TextDocumentItem, TextDocumentPositionParams, TextDocumentRegistrationOptions,
38        TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, TextEdit, WorkDoneProgressOptions,
39        WorkspaceEdit, WorkspaceFolder, WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities,
40    },
41};
42
43use crate::{
44    ModuleId, Program, SourceRange,
45    docs::kcl_doc::ModData,
46    errors::LspSuggestion,
47    exec::KclValue,
48    execution::{cache, kcl_value::FunctionSource},
49    lsp::{
50        backend::Backend as _,
51        kcl::hover::{Hover, HoverOpts},
52        util::IntoDiagnostic,
53    },
54    parsing::{
55        PIPE_OPERATOR,
56        ast::types::{Expr, VariableKind},
57        token::TokenStream,
58    },
59};
60
61pub mod custom_notifications;
62mod hover;
63
64const SEMANTIC_TOKEN_TYPES: [SemanticTokenType; 10] = [
65    SemanticTokenType::NUMBER,
66    SemanticTokenType::VARIABLE,
67    SemanticTokenType::KEYWORD,
68    SemanticTokenType::TYPE,
69    SemanticTokenType::STRING,
70    SemanticTokenType::OPERATOR,
71    SemanticTokenType::COMMENT,
72    SemanticTokenType::FUNCTION,
73    SemanticTokenType::PARAMETER,
74    SemanticTokenType::PROPERTY,
75];
76
77const SEMANTIC_TOKEN_MODIFIERS: [SemanticTokenModifier; 5] = [
78    SemanticTokenModifier::DECLARATION,
79    SemanticTokenModifier::DEFINITION,
80    SemanticTokenModifier::DEFAULT_LIBRARY,
81    SemanticTokenModifier::READONLY,
82    SemanticTokenModifier::STATIC,
83];
84
85/// A subcommand for running the server.
86#[derive(Clone, Debug)]
87#[cfg_attr(feature = "cli", derive(Parser))]
88pub struct Server {
89    /// Port that the server should listen
90    #[cfg_attr(feature = "cli", clap(long, default_value = "8080"))]
91    pub socket: i32,
92
93    /// Listen over stdin and stdout instead of a tcp socket.
94    #[cfg_attr(feature = "cli", clap(short, long, default_value = "false"))]
95    pub stdio: bool,
96}
97
98/// The lsp server backend.
99#[derive(Clone)]
100pub struct Backend {
101    /// The client for the backend.
102    pub client: Client,
103    /// The file system client to use.
104    pub fs: Arc<crate::fs::FileManager>,
105    /// The workspace folders.
106    pub workspace_folders: DashMap<String, WorkspaceFolder>,
107    /// The stdlib completions for the language.
108    pub stdlib_completions: HashMap<String, CompletionItem>,
109    /// The stdlib signatures for the language.
110    pub stdlib_signatures: HashMap<String, SignatureHelp>,
111    /// For all KwArg functions in std, a map from their arg names to arg help snippets (markdown format).
112    pub stdlib_args: HashMap<String, HashMap<String, String>>,
113    /// Token maps.
114    pub(super) token_map: DashMap<String, TokenStream>,
115    /// AST maps.
116    pub ast_map: DashMap<String, crate::Program>,
117    /// Current code.
118    pub code_map: DashMap<String, Vec<u8>>,
119    /// Diagnostics.
120    pub diagnostics_map: DashMap<String, Vec<Diagnostic>>,
121    /// Symbols map.
122    pub symbols_map: DashMap<String, Vec<DocumentSymbol>>,
123    /// Semantic tokens map.
124    pub semantic_tokens_map: DashMap<String, Vec<SemanticToken>>,
125    /// The Zoo API client.
126    pub zoo_client: kittycad::Client,
127    /// If we can send telemetry for this user.
128    pub can_send_telemetry: bool,
129    /// Optional executor context to use if we want to execute the code.
130    pub executor_ctx: Arc<RwLock<Option<crate::execution::ExecutorContext>>>,
131    /// If we are currently allowed to execute the ast.
132    pub can_execute: Arc<RwLock<bool>>,
133
134    pub is_initialized: Arc<RwLock<bool>>,
135}
136
137impl Backend {
138    #[cfg(target_arch = "wasm32")]
139    pub fn new_wasm(
140        client: Client,
141        executor_ctx: Option<crate::execution::ExecutorContext>,
142        fs: crate::fs::wasm::FileSystemManager,
143        zoo_client: kittycad::Client,
144        can_send_telemetry: bool,
145    ) -> Result<Self, String> {
146        Self::with_file_manager(
147            client,
148            executor_ctx,
149            crate::fs::FileManager::new(fs),
150            zoo_client,
151            can_send_telemetry,
152        )
153    }
154
155    #[cfg(not(target_arch = "wasm32"))]
156    pub fn new(
157        client: Client,
158        executor_ctx: Option<crate::execution::ExecutorContext>,
159        zoo_client: kittycad::Client,
160        can_send_telemetry: bool,
161    ) -> Result<Self, String> {
162        Self::with_file_manager(
163            client,
164            executor_ctx,
165            crate::fs::FileManager::new(),
166            zoo_client,
167            can_send_telemetry,
168        )
169    }
170
171    fn with_file_manager(
172        client: Client,
173        executor_ctx: Option<crate::execution::ExecutorContext>,
174        fs: crate::fs::FileManager,
175        zoo_client: kittycad::Client,
176        can_send_telemetry: bool,
177    ) -> Result<Self, String> {
178        let kcl_std = crate::docs::kcl_doc::walk_prelude();
179        let stdlib_completions = get_completions_from_stdlib(&kcl_std).map_err(|e| e.to_string())?;
180        let stdlib_signatures = get_signatures_from_stdlib(&kcl_std);
181        let stdlib_args = get_arg_maps_from_stdlib(&kcl_std);
182
183        Ok(Self {
184            client,
185            fs: Arc::new(fs),
186            stdlib_completions,
187            stdlib_signatures,
188            stdlib_args,
189            zoo_client,
190            can_send_telemetry,
191            can_execute: Arc::new(RwLock::new(executor_ctx.is_some())),
192            executor_ctx: Arc::new(RwLock::new(executor_ctx)),
193            workspace_folders: Default::default(),
194            token_map: Default::default(),
195            ast_map: Default::default(),
196            code_map: Default::default(),
197            diagnostics_map: Default::default(),
198            symbols_map: Default::default(),
199            semantic_tokens_map: Default::default(),
200            is_initialized: Default::default(),
201        })
202    }
203
204    fn remove_from_ast_maps(&self, filename: &str) {
205        self.ast_map.remove(filename);
206        self.symbols_map.remove(filename);
207    }
208}
209
210// Implement the shared backend trait for the language server.
211#[async_trait::async_trait]
212impl crate::lsp::backend::Backend for Backend {
213    fn client(&self) -> &Client {
214        &self.client
215    }
216
217    fn fs(&self) -> &Arc<crate::fs::FileManager> {
218        &self.fs
219    }
220
221    async fn is_initialized(&self) -> bool {
222        *self.is_initialized.read().await
223    }
224
225    async fn set_is_initialized(&self, is_initialized: bool) {
226        *self.is_initialized.write().await = is_initialized;
227    }
228
229    async fn workspace_folders(&self) -> Vec<WorkspaceFolder> {
230        // TODO: fix clone
231        self.workspace_folders.iter().map(|i| i.clone()).collect()
232    }
233
234    async fn add_workspace_folders(&self, folders: Vec<WorkspaceFolder>) {
235        for folder in folders {
236            self.workspace_folders.insert(folder.name.to_string(), folder);
237        }
238    }
239
240    async fn remove_workspace_folders(&self, folders: Vec<WorkspaceFolder>) {
241        for folder in folders {
242            self.workspace_folders.remove(&folder.name);
243        }
244    }
245
246    fn code_map(&self) -> &DashMap<String, Vec<u8>> {
247        &self.code_map
248    }
249
250    async fn insert_code_map(&self, uri: String, text: Vec<u8>) {
251        self.code_map.insert(uri, text);
252    }
253
254    async fn remove_from_code_map(&self, uri: String) -> Option<Vec<u8>> {
255        self.code_map.remove(&uri).map(|x| x.1)
256    }
257
258    async fn clear_code_state(&self) {
259        self.code_map.clear();
260        self.token_map.clear();
261        self.ast_map.clear();
262        self.diagnostics_map.clear();
263        self.symbols_map.clear();
264        self.semantic_tokens_map.clear();
265    }
266
267    fn current_diagnostics_map(&self) -> &DashMap<String, Vec<Diagnostic>> {
268        &self.diagnostics_map
269    }
270
271    async fn inner_on_change(&self, params: TextDocumentItem, force: bool) {
272        if force {
273            crate::bust_cache().await;
274        }
275
276        let filename = params.uri.to_string();
277        // We already updated the code map in the shared backend.
278
279        // Lets update the tokens.
280        let module_id = ModuleId::default();
281        let tokens = match crate::parsing::token::lex(&params.text, module_id) {
282            Ok(tokens) => tokens,
283            Err(err) => {
284                self.add_to_diagnostics(&params, &[err], true).await;
285                self.token_map.remove(&filename);
286                self.remove_from_ast_maps(&filename);
287                self.semantic_tokens_map.remove(&filename);
288                return;
289            }
290        };
291
292        // Get the previous tokens.
293        let tokens_changed = match self.token_map.get(&filename) {
294            Some(previous_tokens) => *previous_tokens != tokens,
295            _ => true,
296        };
297
298        let had_diagnostics = self.has_diagnostics(params.uri.as_ref()).await;
299
300        // Check if the tokens are the same.
301        if !tokens_changed && !force && !had_diagnostics {
302            // We return early here because the tokens are the same.
303            return;
304        }
305
306        if tokens_changed {
307            // Update our token map.
308            self.token_map.insert(params.uri.to_string(), tokens.clone());
309            // Update our semantic tokens.
310            self.update_semantic_tokens(&tokens, &params).await;
311        }
312
313        // Lets update the ast.
314
315        let (ast, errs) = match crate::parsing::parse_tokens(tokens.clone()).0 {
316            Ok(result) => result,
317            Err(err) => {
318                self.add_to_diagnostics(&params, &[err], true).await;
319                self.remove_from_ast_maps(&filename);
320                return;
321            }
322        };
323
324        self.add_to_diagnostics(&params, &errs, true).await;
325
326        if errs.iter().any(|e| e.severity == crate::errors::Severity::Fatal) {
327            self.remove_from_ast_maps(&filename);
328            return;
329        }
330
331        let Some(mut ast) = ast else {
332            self.remove_from_ast_maps(&filename);
333            return;
334        };
335
336        // Here we will want to store the digest and compare, but for now
337        // we're doing this in a non-load-bearing capacity so we can remove
338        // this if it backfires and only hork the LSP.
339        ast.compute_digest();
340
341        // Save it as a program.
342        let ast = crate::Program {
343            ast,
344            original_file_contents: params.text.clone(),
345        };
346
347        // Check if the ast changed.
348        let ast_changed = match self.ast_map.get(&filename) {
349            Some(old_ast) => {
350                // Check if the ast changed.
351                *old_ast.ast != *ast.ast
352            }
353            None => true,
354        };
355
356        if !ast_changed && !force && !had_diagnostics {
357            // Return early if the ast did not change and we don't need to force.
358            return;
359        }
360
361        if ast_changed {
362            self.ast_map.insert(params.uri.to_string(), ast.clone());
363            // Update the symbols map.
364            self.symbols_map.insert(
365                params.uri.to_string(),
366                ast.ast.get_lsp_symbols(&params.text).unwrap_or_default(),
367            );
368
369            // Update our semantic tokens.
370            self.update_semantic_tokens(&tokens, &params).await;
371
372            let discovered_findings = ast.lint_all().into_iter().flatten().collect::<Vec<_>>();
373            self.add_to_diagnostics(&params, &discovered_findings, false).await;
374        }
375
376        // Send the notification to the client that the ast was updated.
377        if self.can_execute().await || self.executor_ctx().await.is_none() {
378            // Only send the notification if we can execute.
379            // Otherwise it confuses the client.
380            self.client
381                .send_notification::<custom_notifications::AstUpdated>(ast.ast.clone())
382                .await;
383        }
384
385        // Execute the code if we have an executor context.
386        // This function automatically executes if we should & updates the diagnostics if we got
387        // errors.
388        if self.execute(&params, &ast).await.is_err() {
389            return;
390        }
391
392        // If we made it here we can clear the diagnostics.
393        self.clear_diagnostics_map(&params.uri, Some(DiagnosticSeverity::ERROR))
394            .await;
395    }
396}
397
398impl Backend {
399    pub async fn can_execute(&self) -> bool {
400        *self.can_execute.read().await
401    }
402
403    pub async fn executor_ctx(&self) -> tokio::sync::RwLockReadGuard<'_, Option<crate::execution::ExecutorContext>> {
404        self.executor_ctx.read().await
405    }
406
407    async fn update_semantic_tokens(&self, tokens: &TokenStream, params: &TextDocumentItem) {
408        // Update the semantic tokens map.
409        let mut semantic_tokens = vec![];
410        let mut last_position = Position::new(0, 0);
411        for token in tokens.as_slice() {
412            let Ok(token_type) = SemanticTokenType::try_from(token.token_type) else {
413                // We continue here because not all tokens can be converted this way, we will get
414                // the rest from the ast.
415                continue;
416            };
417
418            let mut token_type_index = match self.get_semantic_token_type_index(&token_type) {
419                Some(index) => index,
420                // This is actually bad this should not fail.
421                // The test for listing all semantic token types should make this never happen.
422                None => {
423                    self.client
424                        .log_message(
425                            MessageType::ERROR,
426                            format!("token type `{token_type:?}` not accounted for"),
427                        )
428                        .await;
429                    continue;
430                }
431            };
432
433            let source_range: SourceRange = token.into();
434            let position = source_range.start_to_lsp_position(&params.text);
435
436            // Calculate the token modifiers.
437            // Get the value at the current position.
438            let token_modifiers_bitset = match self.ast_map.get(params.uri.as_str()) {
439                Some(ast) => {
440                    let token_index = Arc::new(Mutex::new(token_type_index));
441                    let modifier_index: Arc<Mutex<u32>> = Arc::new(Mutex::new(0));
442                    crate::walk::walk(&ast.ast, |node: crate::walk::Node| {
443                        let Ok(node_range): Result<SourceRange, _> = (&node).try_into() else {
444                            return Ok(true);
445                        };
446
447                        if !node_range.contains(source_range.start()) {
448                            return Ok(true);
449                        }
450
451                        let get_modifier = |modifier: Vec<SemanticTokenModifier>| -> Result<bool> {
452                            let mut mods = modifier_index.lock().map_err(|_| anyhow::anyhow!("mutex"))?;
453                            let Some(token_modifier_index) = self.get_semantic_token_modifier_index(modifier) else {
454                                return Ok(true);
455                            };
456                            if *mods == 0 {
457                                *mods = token_modifier_index;
458                            } else {
459                                *mods |= token_modifier_index;
460                            }
461                            Ok(false)
462                        };
463
464                        match node {
465                            crate::walk::Node::TagDeclarator(_) => {
466                                return get_modifier(vec![
467                                    SemanticTokenModifier::DEFINITION,
468                                    SemanticTokenModifier::STATIC,
469                                ]);
470                            }
471                            crate::walk::Node::VariableDeclarator(variable) => {
472                                let sr: SourceRange = (&variable.id).into();
473                                if sr.contains(source_range.start()) {
474                                    if let Expr::FunctionExpression(_) = &variable.init {
475                                        let mut ti = token_index.lock().map_err(|_| anyhow::anyhow!("mutex"))?;
476                                        *ti = match self.get_semantic_token_type_index(&SemanticTokenType::FUNCTION) {
477                                            Some(index) => index,
478                                            None => token_type_index,
479                                        };
480                                    }
481
482                                    return get_modifier(vec![
483                                        SemanticTokenModifier::DECLARATION,
484                                        SemanticTokenModifier::READONLY,
485                                    ]);
486                                }
487                            }
488                            crate::walk::Node::Parameter(_) => {
489                                let mut ti = token_index.lock().map_err(|_| anyhow::anyhow!("mutex"))?;
490                                *ti = match self.get_semantic_token_type_index(&SemanticTokenType::PARAMETER) {
491                                    Some(index) => index,
492                                    None => token_type_index,
493                                };
494                                return Ok(false);
495                            }
496                            crate::walk::Node::MemberExpression(member_expression) => {
497                                let sr: SourceRange = (&member_expression.property).into();
498                                if sr.contains(source_range.start()) {
499                                    let mut ti = token_index.lock().map_err(|_| anyhow::anyhow!("mutex"))?;
500                                    *ti = match self.get_semantic_token_type_index(&SemanticTokenType::PROPERTY) {
501                                        Some(index) => index,
502                                        None => token_type_index,
503                                    };
504                                    return Ok(false);
505                                }
506                            }
507                            crate::walk::Node::ObjectProperty(object_property) => {
508                                let sr: SourceRange = (&object_property.key).into();
509                                if sr.contains(source_range.start()) {
510                                    let mut ti = token_index.lock().map_err(|_| anyhow::anyhow!("mutex"))?;
511                                    *ti = match self.get_semantic_token_type_index(&SemanticTokenType::PROPERTY) {
512                                        Some(index) => index,
513                                        None => token_type_index,
514                                    };
515                                }
516                                return get_modifier(vec![SemanticTokenModifier::DECLARATION]);
517                            }
518                            crate::walk::Node::CallExpressionKw(call_expr) => {
519                                let sr: SourceRange = (&call_expr.callee).into();
520                                if sr.contains(source_range.start()) {
521                                    let mut ti = token_index.lock().map_err(|_| anyhow::anyhow!("mutex"))?;
522                                    *ti = match self.get_semantic_token_type_index(&SemanticTokenType::FUNCTION) {
523                                        Some(index) => index,
524                                        None => token_type_index,
525                                    };
526
527                                    if self.stdlib_completions.contains_key(&call_expr.callee.name.name) {
528                                        // This is a stdlib function.
529                                        return get_modifier(vec![SemanticTokenModifier::DEFAULT_LIBRARY]);
530                                    }
531
532                                    return Ok(false);
533                                }
534                            }
535                            _ => {}
536                        }
537                        Ok(true)
538                    })
539                    .unwrap_or_default();
540
541                    let t = match token_index.lock() {
542                        Ok(guard) => *guard,
543                        _ => 0,
544                    };
545                    token_type_index = t;
546
547                    match modifier_index.lock() {
548                        Ok(guard) => *guard,
549                        _ => 0,
550                    }
551                }
552                _ => 0,
553            };
554
555            // We need to check if we are on the last token of the line.
556            // If we are starting from the end of the last line just add 1 to the line.
557            // Check if we are on the last token of the line.
558            if let Some(line) = params.text.lines().nth(position.line as usize) {
559                if line.len() == position.character as usize {
560                    // We are on the last token of the line.
561                    // We need to add a new line.
562                    let semantic_token = SemanticToken {
563                        delta_line: position.line - last_position.line + 1,
564                        delta_start: 0,
565                        length: (token.end - token.start) as u32,
566                        token_type: token_type_index,
567                        token_modifiers_bitset,
568                    };
569
570                    semantic_tokens.push(semantic_token);
571
572                    last_position = Position::new(position.line + 1, 0);
573                    continue;
574                }
575            }
576
577            let semantic_token = SemanticToken {
578                delta_line: position.line - last_position.line,
579                delta_start: if position.line != last_position.line {
580                    position.character
581                } else {
582                    position.character - last_position.character
583                },
584                length: (token.end - token.start) as u32,
585                token_type: token_type_index,
586                token_modifiers_bitset,
587            };
588
589            semantic_tokens.push(semantic_token);
590
591            last_position = position;
592        }
593        self.semantic_tokens_map.insert(params.uri.to_string(), semantic_tokens);
594    }
595
596    async fn clear_diagnostics_map(&self, uri: &url::Url, severity: Option<DiagnosticSeverity>) {
597        let Some(mut items) = self.diagnostics_map.get_mut(uri.as_str()) else {
598            return;
599        };
600
601        // If we only want to clear a specific severity, do that.
602        if let Some(severity) = severity {
603            items.retain(|x| x.severity != Some(severity));
604        } else {
605            items.clear();
606        }
607
608        if items.is_empty() {
609            #[cfg(not(target_arch = "wasm32"))]
610            {
611                self.client.publish_diagnostics(uri.clone(), items.clone(), None).await;
612            }
613
614            // We need to drop the items here.
615            drop(items);
616
617            self.diagnostics_map.remove(uri.as_str());
618        } else {
619            // We don't need to update the map since we used get_mut.
620
621            #[cfg(not(target_arch = "wasm32"))]
622            {
623                self.client.publish_diagnostics(uri.clone(), items.clone(), None).await;
624            }
625        }
626    }
627
628    async fn add_to_diagnostics<DiagT: IntoDiagnostic + std::fmt::Debug>(
629        &self,
630        params: &TextDocumentItem,
631        diagnostics: &[DiagT],
632        clear_all_before_add: bool,
633    ) {
634        if diagnostics.is_empty() {
635            return;
636        }
637
638        if clear_all_before_add {
639            self.clear_diagnostics_map(&params.uri, None).await;
640        } else if diagnostics.iter().all(|x| x.severity() == DiagnosticSeverity::ERROR) {
641            // If the diagnostic is an error, it will be the only error we get since that halts
642            // execution.
643            // Clear the diagnostics before we add a new one.
644            self.clear_diagnostics_map(&params.uri, Some(DiagnosticSeverity::ERROR))
645                .await;
646        } else if diagnostics
647            .iter()
648            .all(|x| x.severity() == DiagnosticSeverity::INFORMATION)
649        {
650            // If the diagnostic is a lint, we will pass them all to add at once so we need to
651            // clear the old ones.
652            self.clear_diagnostics_map(&params.uri, Some(DiagnosticSeverity::INFORMATION))
653                .await;
654        }
655
656        let mut items = match self.diagnostics_map.get(params.uri.as_str()) {
657            Some(items) => {
658                // TODO: Would be awesome to fix the clone here.
659                items.clone()
660            }
661            _ => {
662                vec![]
663            }
664        };
665
666        for diagnostic in diagnostics {
667            let lsp_d = diagnostic.to_lsp_diagnostics(&params.text);
668            // Make sure we don't duplicate diagnostics.
669            for d in lsp_d {
670                if !items.iter().any(|x| x == &d) {
671                    items.push(d);
672                }
673            }
674        }
675
676        self.diagnostics_map.insert(params.uri.to_string(), items.clone());
677
678        self.client.publish_diagnostics(params.uri.clone(), items, None).await;
679    }
680
681    async fn execute(&self, params: &TextDocumentItem, ast: &Program) -> Result<()> {
682        // Check if we can execute.
683        if !self.can_execute().await {
684            return Ok(());
685        }
686
687        // Execute the code if we have an executor context.
688        let ctx = self.executor_ctx().await;
689        let Some(ref executor_ctx) = *ctx else {
690            return Ok(());
691        };
692
693        if !self.is_initialized().await {
694            // We are not initialized yet.
695            return Ok(());
696        }
697
698        match executor_ctx.run_with_caching(ast.clone()).await {
699            Err(err) => {
700                self.add_to_diagnostics(params, &[err], false).await;
701
702                // Since we already published the diagnostics we don't really care about the error
703                // string.
704                Err(anyhow::anyhow!("failed to execute code"))
705            }
706            Ok(_) => Ok(()),
707        }
708    }
709
710    pub fn get_semantic_token_type_index(&self, token_type: &SemanticTokenType) -> Option<u32> {
711        SEMANTIC_TOKEN_TYPES
712            .iter()
713            .position(|x| *x == *token_type)
714            .map(|y| y as u32)
715    }
716
717    pub fn get_semantic_token_modifier_index(&self, token_types: Vec<SemanticTokenModifier>) -> Option<u32> {
718        if token_types.is_empty() {
719            return None;
720        }
721
722        let mut modifier = None;
723        for token_type in token_types {
724            if let Some(index) = SEMANTIC_TOKEN_MODIFIERS
725                .iter()
726                .position(|x| *x == token_type)
727                .map(|y| y as u32)
728            {
729                modifier = match modifier {
730                    Some(modifier) => Some(modifier | index),
731                    None => Some(index),
732                };
733            }
734        }
735        modifier
736    }
737
738    pub async fn create_zip(&self) -> Result<Vec<u8>> {
739        // Collect all the file data we know.
740        let mut buf = vec![];
741        let mut zip = zip::ZipWriter::new(std::io::Cursor::new(&mut buf));
742        for code in self.code_map.iter() {
743            let entry = code.key();
744            let value = code.value();
745            let file_name = entry.replace("file://", "").to_string();
746
747            let options = zip::write::SimpleFileOptions::default().compression_method(zip::CompressionMethod::Stored);
748            zip.start_file(file_name, options)?;
749            zip.write_all(value)?;
750        }
751        // Apply the changes you've made.
752        // Dropping the `ZipWriter` will have the same effect, but may silently fail
753        zip.finish()?;
754
755        Ok(buf)
756    }
757
758    pub async fn send_telemetry(&self) -> Result<()> {
759        // Get information about the user.
760        let user = self
761            .zoo_client
762            .users()
763            .get_self()
764            .await
765            .map_err(|e| anyhow::anyhow!(e.to_string()))?;
766
767        // Hash the user's id.
768        // Create a SHA-256 object
769        let mut hasher = sha2::Sha256::new();
770        // Write input message
771        hasher.update(user.id);
772        // Read hash digest and consume hasher
773        let result = hasher.finalize();
774        // Get the hash as a string.
775        let user_id_hash = format!("{result:x}");
776
777        // Get the workspace folders.
778        // The key of the workspace folder is the project name.
779        let workspace_folders = self.workspace_folders().await;
780        let project_names: Vec<&str> = workspace_folders.iter().map(|v| v.name.as_str()).collect::<Vec<_>>();
781        // Get the first name.
782        let project_name = project_names
783            .first()
784            .ok_or_else(|| anyhow::anyhow!("no project names"))?
785            .to_string();
786
787        // Send the telemetry data.
788        self.zoo_client
789            .meta()
790            .create_event(
791                vec![kittycad::types::multipart::Attachment {
792                    // Clean the URI part.
793                    name: "attachment".to_string(),
794                    filepath: Some("attachment.zip".into()),
795                    content_type: Some("application/x-zip".to_string()),
796                    data: self.create_zip().await?,
797                }],
798                &kittycad::types::Event {
799                    // This gets generated server side so leave empty for now.
800                    attachment_uri: None,
801                    created_at: chrono::Utc::now(),
802                    event_type: kittycad::types::ModelingAppEventType::SuccessfulCompileBeforeClose,
803                    last_compiled_at: Some(chrono::Utc::now()),
804                    // We do not have project descriptions yet.
805                    project_description: None,
806                    project_name,
807                    // The UUID for the Design Studio.
808                    // We can unwrap here because we know it will not panic.
809                    source_id: uuid::Uuid::from_str("70178592-dfca-47b3-bd2d-6fce2bcaee04").unwrap(),
810                    type_: kittycad::types::Type::ModelingAppEvent,
811                    user_id: user_id_hash,
812                },
813            )
814            .await
815            .map_err(|e| anyhow::anyhow!(e.to_string()))?;
816
817        Ok(())
818    }
819
820    pub async fn update_can_execute(
821        &self,
822        params: custom_notifications::UpdateCanExecuteParams,
823    ) -> RpcResult<custom_notifications::UpdateCanExecuteResponse> {
824        let mut can_execute = self.can_execute.write().await;
825
826        if *can_execute == params.can_execute {
827            return Ok(custom_notifications::UpdateCanExecuteResponse {});
828        }
829
830        *can_execute = params.can_execute;
831
832        Ok(custom_notifications::UpdateCanExecuteResponse {})
833    }
834
835    /// Returns the new string for the code after rename.
836    pub fn inner_prepare_rename(
837        &self,
838        params: &TextDocumentPositionParams,
839        new_name: &str,
840    ) -> RpcResult<Option<(String, String)>> {
841        let filename = params.text_document.uri.to_string();
842
843        let Some(current_code) = self.code_map.get(&filename) else {
844            return Ok(None);
845        };
846        let Ok(current_code) = std::str::from_utf8(&current_code) else {
847            return Ok(None);
848        };
849
850        // Parse the ast.
851        // I don't know if we need to do this again since it should be updated in the context.
852        // But I figure better safe than sorry since this will write back out to the file.
853        let module_id = ModuleId::default();
854        let Ok(mut ast) = crate::parsing::parse_str(current_code, module_id).parse_errs_as_err() else {
855            return Ok(None);
856        };
857
858        // Let's convert the position to a character index.
859        let pos = position_to_char_index(params.position, current_code);
860        // Now let's perform the rename on the ast.
861        ast.rename_symbol(new_name, pos);
862        // Now recast it.
863        let recast = ast.recast_top(&Default::default(), 0);
864
865        Ok(Some((current_code.to_string(), recast)))
866    }
867}
868
869#[tower_lsp::async_trait]
870impl LanguageServer for Backend {
871    async fn initialize(&self, params: InitializeParams) -> RpcResult<InitializeResult> {
872        self.client
873            .log_message(MessageType::INFO, format!("initialize: {params:?}"))
874            .await;
875
876        Ok(InitializeResult {
877            capabilities: ServerCapabilities {
878                color_provider: Some(ColorProviderCapability::Simple(true)),
879                code_action_provider: Some(CodeActionProviderCapability::Options(CodeActionOptions {
880                    code_action_kinds: Some(vec![CodeActionKind::QUICKFIX]),
881                    resolve_provider: Some(false),
882                    work_done_progress_options: WorkDoneProgressOptions::default(),
883                })),
884                completion_provider: Some(CompletionOptions {
885                    resolve_provider: Some(false),
886                    trigger_characters: Some(vec![".".to_string()]),
887                    work_done_progress_options: Default::default(),
888                    all_commit_characters: None,
889                    ..Default::default()
890                }),
891                diagnostic_provider: Some(DiagnosticServerCapabilities::Options(DiagnosticOptions {
892                    ..Default::default()
893                })),
894                document_formatting_provider: Some(OneOf::Left(true)),
895                folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
896                hover_provider: Some(HoverProviderCapability::Simple(true)),
897                inlay_hint_provider: Some(OneOf::Left(true)),
898                rename_provider: Some(OneOf::Left(true)),
899                semantic_tokens_provider: Some(SemanticTokensServerCapabilities::SemanticTokensRegistrationOptions(
900                    SemanticTokensRegistrationOptions {
901                        text_document_registration_options: {
902                            TextDocumentRegistrationOptions {
903                                document_selector: Some(vec![DocumentFilter {
904                                    language: Some("kcl".to_string()),
905                                    scheme: Some("file".to_string()),
906                                    pattern: None,
907                                }]),
908                            }
909                        },
910                        semantic_tokens_options: SemanticTokensOptions {
911                            work_done_progress_options: WorkDoneProgressOptions::default(),
912                            legend: SemanticTokensLegend {
913                                token_types: SEMANTIC_TOKEN_TYPES.to_vec(),
914                                token_modifiers: SEMANTIC_TOKEN_MODIFIERS.to_vec(),
915                            },
916                            range: Some(false),
917                            full: Some(SemanticTokensFullOptions::Bool(true)),
918                        },
919                        static_registration_options: StaticRegistrationOptions::default(),
920                    },
921                )),
922                signature_help_provider: Some(SignatureHelpOptions {
923                    trigger_characters: None,
924                    retrigger_characters: None,
925                    ..Default::default()
926                }),
927                text_document_sync: Some(TextDocumentSyncCapability::Options(TextDocumentSyncOptions {
928                    open_close: Some(true),
929                    change: Some(TextDocumentSyncKind::FULL),
930                    ..Default::default()
931                })),
932                workspace: Some(WorkspaceServerCapabilities {
933                    workspace_folders: Some(WorkspaceFoldersServerCapabilities {
934                        supported: Some(true),
935                        change_notifications: Some(OneOf::Left(true)),
936                    }),
937                    file_operations: None,
938                }),
939                ..Default::default()
940            },
941            ..Default::default()
942        })
943    }
944
945    async fn initialized(&self, params: InitializedParams) {
946        self.do_initialized(params).await
947    }
948
949    async fn shutdown(&self) -> RpcResult<()> {
950        self.do_shutdown().await
951    }
952
953    async fn did_change_workspace_folders(&self, params: DidChangeWorkspaceFoldersParams) {
954        self.do_did_change_workspace_folders(params).await
955    }
956
957    async fn did_change_configuration(&self, params: DidChangeConfigurationParams) {
958        self.do_did_change_configuration(params).await
959    }
960
961    async fn did_change_watched_files(&self, params: DidChangeWatchedFilesParams) {
962        self.do_did_change_watched_files(params).await
963    }
964
965    async fn did_create_files(&self, params: CreateFilesParams) {
966        self.do_did_create_files(params).await
967    }
968
969    async fn did_rename_files(&self, params: RenameFilesParams) {
970        self.do_did_rename_files(params).await
971    }
972
973    async fn did_delete_files(&self, params: DeleteFilesParams) {
974        self.do_did_delete_files(params).await
975    }
976
977    async fn did_open(&self, params: DidOpenTextDocumentParams) {
978        self.do_did_open(params).await
979    }
980
981    async fn did_change(&self, params: DidChangeTextDocumentParams) {
982        self.do_did_change(params).await;
983    }
984
985    async fn did_save(&self, params: DidSaveTextDocumentParams) {
986        self.do_did_save(params).await
987    }
988
989    async fn did_close(&self, params: DidCloseTextDocumentParams) {
990        self.do_did_close(params).await;
991
992        // Inject telemetry if we can train on the user's code.
993        // Return early if we cannot.
994        if !self.can_send_telemetry {
995            return;
996        }
997
998        // In wasm this needs to be spawn_local since fucking reqwests doesn't implement Send for wasm.
999        #[cfg(target_arch = "wasm32")]
1000        {
1001            let be = self.clone();
1002            wasm_bindgen_futures::spawn_local(async move {
1003                if let Err(err) = be.send_telemetry().await {
1004                    be.client
1005                        .log_message(MessageType::WARNING, format!("failed to send telemetry: {}", err))
1006                        .await;
1007                }
1008            });
1009        }
1010        #[cfg(not(target_arch = "wasm32"))]
1011        if let Err(err) = self.send_telemetry().await {
1012            self.client
1013                .log_message(MessageType::WARNING, format!("failed to send telemetry: {err}"))
1014                .await;
1015        }
1016    }
1017
1018    async fn hover(&self, params: HoverParams) -> RpcResult<Option<LspHover>> {
1019        let filename = params.text_document_position_params.text_document.uri.to_string();
1020
1021        let Some(current_code) = self.code_map.get(&filename) else {
1022            return Ok(None);
1023        };
1024        let Ok(current_code) = std::str::from_utf8(&current_code) else {
1025            return Ok(None);
1026        };
1027
1028        let pos = position_to_char_index(params.text_document_position_params.position, current_code);
1029
1030        // Let's iterate over the AST and find the node that contains the cursor.
1031        let Some(ast) = self.ast_map.get(&filename) else {
1032            return Ok(None);
1033        };
1034
1035        let Some(hover) = ast
1036            .ast
1037            .get_hover_value_for_position(pos, current_code, &HoverOpts::default_for_hover())
1038        else {
1039            return Ok(None);
1040        };
1041
1042        match hover {
1043            Hover::Function { name, range } => {
1044                let (sig, docs) = if let Some(Some(result)) = with_cached_var(&name, |value| {
1045                    match value {
1046                        // User-defined or KCL std function
1047                        KclValue::Function {
1048                            value: FunctionSource::User { ast, .. },
1049                            ..
1050                        } => {
1051                            // TODO get docs from comments
1052                            Some((ast.signature(), ""))
1053                        }
1054                        _ => None,
1055                    }
1056                })
1057                .await
1058                {
1059                    result
1060                } else {
1061                    // Get the docs for this function.
1062                    let Some(completion) = self.stdlib_completions.get(&name) else {
1063                        return Ok(None);
1064                    };
1065                    let Some(docs) = &completion.documentation else {
1066                        return Ok(None);
1067                    };
1068
1069                    let docs = match docs {
1070                        Documentation::String(docs) => docs,
1071                        Documentation::MarkupContent(MarkupContent { value, .. }) => value,
1072                    };
1073
1074                    let docs = if docs.len() > 320 {
1075                        let end = docs.find("\n\n").or_else(|| docs.find("\n\r\n")).unwrap_or(320);
1076                        &docs[..end]
1077                    } else {
1078                        &**docs
1079                    };
1080
1081                    let Some(label_details) = &completion.label_details else {
1082                        return Ok(None);
1083                    };
1084
1085                    let sig = if let Some(detail) = &label_details.detail {
1086                        detail.clone()
1087                    } else {
1088                        String::new()
1089                    };
1090
1091                    (sig, docs)
1092                };
1093
1094                Ok(Some(LspHover {
1095                    contents: HoverContents::Markup(MarkupContent {
1096                        kind: MarkupKind::Markdown,
1097                        value: format!("```\n{name}{sig}\n```\n\n{docs}"),
1098                    }),
1099                    range: Some(range),
1100                }))
1101            }
1102            Hover::Type { name, range } => {
1103                let Some(completion) = self.stdlib_completions.get(&name) else {
1104                    return Ok(None);
1105                };
1106                let Some(docs) = &completion.documentation else {
1107                    return Ok(None);
1108                };
1109
1110                let docs = match docs {
1111                    Documentation::String(docs) => docs,
1112                    Documentation::MarkupContent(MarkupContent { value, .. }) => value,
1113                };
1114
1115                let docs = if docs.len() > 320 {
1116                    let end = docs.find("\n\n").or_else(|| docs.find("\n\r\n")).unwrap_or(320);
1117                    &docs[..end]
1118                } else {
1119                    &**docs
1120                };
1121
1122                Ok(Some(LspHover {
1123                    contents: HoverContents::Markup(MarkupContent {
1124                        kind: MarkupKind::Markdown,
1125                        value: format!("```\n{name}\n```\n\n{docs}"),
1126                    }),
1127                    range: Some(range),
1128                }))
1129            }
1130            Hover::KwArg {
1131                name,
1132                callee_name,
1133                range,
1134            } => {
1135                // TODO handle user-defined functions too
1136
1137                let Some(arg_map) = self.stdlib_args.get(&callee_name) else {
1138                    return Ok(None);
1139                };
1140
1141                let Some(tip) = arg_map.get(&name) else {
1142                    return Ok(None);
1143                };
1144
1145                Ok(Some(LspHover {
1146                    contents: HoverContents::Markup(MarkupContent {
1147                        kind: MarkupKind::Markdown,
1148                        value: tip.clone(),
1149                    }),
1150                    range: Some(range),
1151                }))
1152            }
1153            Hover::Variable {
1154                name,
1155                ty: Some(ty),
1156                range,
1157            } => Ok(Some(LspHover {
1158                contents: HoverContents::Markup(MarkupContent {
1159                    kind: MarkupKind::Markdown,
1160                    value: format!("```\n{name}: {ty}\n```"),
1161                }),
1162                range: Some(range),
1163            })),
1164            Hover::Variable { name, ty: None, range } => Ok(with_cached_var(&name, |value| {
1165                let mut text: String = format!("```\n{name}");
1166                if let Some(ty) = value.principal_type() {
1167                    text.push_str(&format!(": {}", ty.human_friendly_type()));
1168                }
1169                if let Some(v) = value.value_str() {
1170                    text.push_str(&format!(" = {v}"));
1171                }
1172                text.push_str("\n```");
1173
1174                LspHover {
1175                    contents: HoverContents::Markup(MarkupContent {
1176                        kind: MarkupKind::Markdown,
1177                        value: text,
1178                    }),
1179                    range: Some(range),
1180                }
1181            })
1182            .await),
1183            Hover::Signature { .. } => Ok(None),
1184            Hover::Comment { value, range } => Ok(Some(LspHover {
1185                contents: HoverContents::Markup(MarkupContent {
1186                    kind: MarkupKind::Markdown,
1187                    value,
1188                }),
1189                range: Some(range),
1190            })),
1191        }
1192    }
1193
1194    async fn completion(&self, params: CompletionParams) -> RpcResult<Option<CompletionResponse>> {
1195        let mut completions = vec![CompletionItem {
1196            label: PIPE_OPERATOR.to_string(),
1197            label_details: None,
1198            kind: Some(CompletionItemKind::OPERATOR),
1199            detail: Some("A pipe operator.".to_string()),
1200            documentation: Some(Documentation::MarkupContent(MarkupContent {
1201                kind: MarkupKind::Markdown,
1202                value: "A pipe operator.".to_string(),
1203            })),
1204            deprecated: Some(false),
1205            preselect: None,
1206            sort_text: None,
1207            filter_text: None,
1208            insert_text: Some("|> ".to_string()),
1209            insert_text_format: Some(InsertTextFormat::PLAIN_TEXT),
1210            insert_text_mode: None,
1211            text_edit: None,
1212            additional_text_edits: None,
1213            command: None,
1214            commit_characters: None,
1215            data: None,
1216            tags: None,
1217        }];
1218
1219        // Get the current line up to cursor
1220        let Some(current_code) = self
1221            .code_map
1222            .get(params.text_document_position.text_document.uri.as_ref())
1223        else {
1224            return Ok(Some(CompletionResponse::Array(completions)));
1225        };
1226        let Ok(current_code) = std::str::from_utf8(&current_code) else {
1227            return Ok(Some(CompletionResponse::Array(completions)));
1228        };
1229
1230        // Get the current line up to cursor, with bounds checking
1231        if let Some(line) = current_code
1232            .lines()
1233            .nth(params.text_document_position.position.line as usize)
1234        {
1235            let char_pos = params.text_document_position.position.character as usize;
1236            if char_pos <= line.len() {
1237                let line_prefix = &line[..char_pos];
1238                // Get last word
1239                let last_word = line_prefix
1240                    .split(|c: char| c.is_whitespace() || c.is_ascii_punctuation())
1241                    .next_back()
1242                    .unwrap_or("");
1243
1244                // If the last word starts with a digit, return no completions
1245                if !last_word.is_empty() && last_word.chars().next().unwrap().is_ascii_digit() {
1246                    return Ok(None);
1247                }
1248            }
1249        }
1250
1251        completions.extend(self.stdlib_completions.values().cloned());
1252
1253        // Add more to the completions if we have more.
1254        let Some(ast) = self
1255            .ast_map
1256            .get(params.text_document_position.text_document.uri.as_ref())
1257        else {
1258            return Ok(Some(CompletionResponse::Array(completions)));
1259        };
1260
1261        let Some(current_code) = self
1262            .code_map
1263            .get(params.text_document_position.text_document.uri.as_ref())
1264        else {
1265            return Ok(Some(CompletionResponse::Array(completions)));
1266        };
1267        let Ok(current_code) = std::str::from_utf8(&current_code) else {
1268            return Ok(Some(CompletionResponse::Array(completions)));
1269        };
1270
1271        let position = position_to_char_index(params.text_document_position.position, current_code);
1272        if ast.ast.in_comment(position) {
1273            // If we are in a code comment we don't want to show completions.
1274            return Ok(None);
1275        }
1276
1277        // Get the completion items for the ast.
1278        let Ok(variables) = ast.ast.completion_items(position) else {
1279            return Ok(Some(CompletionResponse::Array(completions)));
1280        };
1281
1282        // Get our variables from our AST to include in our completions.
1283        completions.extend(variables);
1284
1285        Ok(Some(CompletionResponse::Array(completions)))
1286    }
1287
1288    async fn diagnostic(&self, params: DocumentDiagnosticParams) -> RpcResult<DocumentDiagnosticReportResult> {
1289        let filename = params.text_document.uri.to_string();
1290
1291        // Get the current diagnostics for this file.
1292        let Some(items) = self.diagnostics_map.get(&filename) else {
1293            // Send an empty report.
1294            return Ok(DocumentDiagnosticReportResult::Report(DocumentDiagnosticReport::Full(
1295                RelatedFullDocumentDiagnosticReport {
1296                    related_documents: None,
1297                    full_document_diagnostic_report: FullDocumentDiagnosticReport {
1298                        result_id: None,
1299                        items: vec![],
1300                    },
1301                },
1302            )));
1303        };
1304
1305        Ok(DocumentDiagnosticReportResult::Report(DocumentDiagnosticReport::Full(
1306            RelatedFullDocumentDiagnosticReport {
1307                related_documents: None,
1308                full_document_diagnostic_report: FullDocumentDiagnosticReport {
1309                    result_id: None,
1310                    items: items.clone(),
1311                },
1312            },
1313        )))
1314    }
1315
1316    async fn signature_help(&self, params: SignatureHelpParams) -> RpcResult<Option<SignatureHelp>> {
1317        let filename = params.text_document_position_params.text_document.uri.to_string();
1318
1319        let Some(current_code) = self.code_map.get(&filename) else {
1320            return Ok(None);
1321        };
1322        let Ok(current_code) = std::str::from_utf8(&current_code) else {
1323            return Ok(None);
1324        };
1325
1326        let pos = position_to_char_index(params.text_document_position_params.position, current_code);
1327
1328        // Get the character at the position.
1329        let Some(ch) = current_code.chars().nth(pos) else {
1330            return Ok(None);
1331        };
1332
1333        let check_char = |ch: char| {
1334            // If  we are on a (, then get the string in front of the (
1335            // and try to get the signature.
1336            // We do these before the ast check because we might not have a valid ast.
1337            if ch == '(' {
1338                // If the current character is not a " " then get the next space after
1339                // our position so we can split on that.
1340                // Find the next space after the current position.
1341                let next_space = if ch != ' ' {
1342                    if let Some(next_space) = current_code[pos..].find(' ') {
1343                        pos + next_space
1344                    } else if let Some(next_space) = current_code[pos..].find('(') {
1345                        pos + next_space
1346                    } else {
1347                        pos
1348                    }
1349                } else {
1350                    pos
1351                };
1352                let p2 = std::cmp::max(pos, next_space);
1353
1354                let last_word = current_code[..p2].split_whitespace().last()?;
1355
1356                // Get the function name.
1357                return self.stdlib_signatures.get(last_word);
1358            } else if ch == ',' {
1359                // If we have a comma, then get the string in front of
1360                // the closest ( and try to get the signature.
1361
1362                // Find the last ( before the comma.
1363                let last_paren = current_code[..pos].rfind('(')?;
1364                // Get the string in front of the (.
1365                let last_word = current_code[..last_paren].split_whitespace().last()?;
1366                // Get the function name.
1367                return self.stdlib_signatures.get(last_word);
1368            }
1369
1370            None
1371        };
1372
1373        if let Some(signature) = check_char(ch) {
1374            return Ok(Some(signature.clone()));
1375        }
1376
1377        // Check if we have context.
1378        if let Some(context) = params.context {
1379            if let Some(character) = context.trigger_character {
1380                for character in character.chars() {
1381                    // Check if we are on a ( or a ,.
1382                    if character == '(' || character == ',' {
1383                        if let Some(signature) = check_char(character) {
1384                            return Ok(Some(signature.clone()));
1385                        }
1386                    }
1387                }
1388            }
1389        }
1390
1391        // Let's iterate over the AST and find the node that contains the cursor.
1392        let Some(ast) = self.ast_map.get(&filename) else {
1393            return Ok(None);
1394        };
1395
1396        let Some(value) = ast.ast.get_expr_for_position(pos) else {
1397            return Ok(None);
1398        };
1399
1400        let Some(hover) =
1401            value.get_hover_value_for_position(pos, current_code, &HoverOpts::default_for_signature_help())
1402        else {
1403            return Ok(None);
1404        };
1405
1406        match hover {
1407            Hover::Function { name, range: _ } => {
1408                // Get the docs for this function.
1409                let Some(signature) = self.stdlib_signatures.get(&name) else {
1410                    return Ok(None);
1411                };
1412
1413                Ok(Some(signature.clone()))
1414            }
1415            Hover::Signature {
1416                name,
1417                parameter_index,
1418                range: _,
1419            } => {
1420                let Some(signature) = self.stdlib_signatures.get(&name) else {
1421                    return Ok(None);
1422                };
1423
1424                let mut signature = signature.clone();
1425
1426                signature.active_parameter = Some(parameter_index);
1427
1428                Ok(Some(signature))
1429            }
1430            _ => {
1431                return Ok(None);
1432            }
1433        }
1434    }
1435
1436    async fn inlay_hint(&self, _params: InlayHintParams) -> RpcResult<Option<Vec<InlayHint>>> {
1437        // TODO: do this
1438
1439        Ok(None)
1440    }
1441
1442    async fn semantic_tokens_full(&self, params: SemanticTokensParams) -> RpcResult<Option<SemanticTokensResult>> {
1443        let filename = params.text_document.uri.to_string();
1444
1445        let Some(semantic_tokens) = self.semantic_tokens_map.get(&filename) else {
1446            return Ok(None);
1447        };
1448
1449        Ok(Some(SemanticTokensResult::Tokens(SemanticTokens {
1450            result_id: None,
1451            data: semantic_tokens.clone(),
1452        })))
1453    }
1454
1455    async fn document_symbol(&self, params: DocumentSymbolParams) -> RpcResult<Option<DocumentSymbolResponse>> {
1456        let filename = params.text_document.uri.to_string();
1457
1458        let Some(symbols) = self.symbols_map.get(&filename) else {
1459            return Ok(None);
1460        };
1461
1462        Ok(Some(DocumentSymbolResponse::Nested(symbols.clone())))
1463    }
1464
1465    async fn formatting(&self, params: DocumentFormattingParams) -> RpcResult<Option<Vec<TextEdit>>> {
1466        let filename = params.text_document.uri.to_string();
1467
1468        let Some(current_code) = self.code_map.get(&filename) else {
1469            return Ok(None);
1470        };
1471        let Ok(current_code) = std::str::from_utf8(&current_code) else {
1472            return Ok(None);
1473        };
1474
1475        // Parse the ast.
1476        // I don't know if we need to do this again since it should be updated in the context.
1477        // But I figure better safe than sorry since this will write back out to the file.
1478        let module_id = ModuleId::default();
1479        let Ok(ast) = crate::parsing::parse_str(current_code, module_id).parse_errs_as_err() else {
1480            return Ok(None);
1481        };
1482        // Now recast it.
1483        let recast = ast.recast_top(
1484            &crate::parsing::ast::types::FormatOptions {
1485                tab_size: params.options.tab_size as usize,
1486                insert_final_newline: params.options.insert_final_newline.unwrap_or(false),
1487                use_tabs: !params.options.insert_spaces,
1488            },
1489            0,
1490        );
1491        let source_range = SourceRange::new(0, current_code.len(), module_id);
1492        let range = source_range.to_lsp_range(current_code);
1493        Ok(Some(vec![TextEdit {
1494            new_text: recast,
1495            range,
1496        }]))
1497    }
1498
1499    async fn rename(&self, params: RenameParams) -> RpcResult<Option<WorkspaceEdit>> {
1500        let Some((current_code, new_code)) =
1501            self.inner_prepare_rename(&params.text_document_position, &params.new_name)?
1502        else {
1503            return Ok(None);
1504        };
1505
1506        let source_range = SourceRange::new(0, current_code.len(), ModuleId::default());
1507        let range = source_range.to_lsp_range(&current_code);
1508        Ok(Some(WorkspaceEdit {
1509            changes: Some(HashMap::from([(
1510                params.text_document_position.text_document.uri,
1511                vec![TextEdit {
1512                    new_text: new_code,
1513                    range,
1514                }],
1515            )])),
1516            document_changes: None,
1517            change_annotations: None,
1518        }))
1519    }
1520
1521    async fn prepare_rename(&self, params: TextDocumentPositionParams) -> RpcResult<Option<PrepareRenameResponse>> {
1522        if self
1523            .inner_prepare_rename(&params, "someNameNoOneInTheirRightMindWouldEverUseForTesting")?
1524            .is_none()
1525        {
1526            return Ok(None);
1527        }
1528
1529        // Return back to the client, that it is safe to use the rename behavior.
1530        Ok(Some(PrepareRenameResponse::DefaultBehavior { default_behavior: true }))
1531    }
1532
1533    async fn folding_range(&self, params: FoldingRangeParams) -> RpcResult<Option<Vec<FoldingRange>>> {
1534        let filename = params.text_document.uri.to_string();
1535
1536        // Get the ast.
1537        let Some(ast) = self.ast_map.get(&filename) else {
1538            return Ok(None);
1539        };
1540
1541        // Get the folding ranges.
1542        let folding_ranges = ast.ast.get_lsp_folding_ranges();
1543
1544        if folding_ranges.is_empty() {
1545            return Ok(None);
1546        }
1547
1548        Ok(Some(folding_ranges))
1549    }
1550
1551    async fn code_action(&self, params: CodeActionParams) -> RpcResult<Option<CodeActionResponse>> {
1552        let actions = params
1553            .context
1554            .diagnostics
1555            .into_iter()
1556            .filter_map(|diagnostic| {
1557                let (suggestion, range) = diagnostic
1558                    .data
1559                    .as_ref()
1560                    .and_then(|data| serde_json::from_value::<LspSuggestion>(data.clone()).ok())?;
1561                let edit = TextEdit {
1562                    range,
1563                    new_text: suggestion.insert,
1564                };
1565                let changes = HashMap::from([(params.text_document.uri.clone(), vec![edit])]);
1566
1567                // If you add more code action kinds, make sure you add it to the server
1568                // capabilities on initialization!
1569                Some(CodeActionOrCommand::CodeAction(CodeAction {
1570                    title: suggestion.title,
1571                    kind: Some(CodeActionKind::QUICKFIX),
1572                    diagnostics: Some(vec![diagnostic]),
1573                    edit: Some(WorkspaceEdit {
1574                        changes: Some(changes),
1575                        document_changes: None,
1576                        change_annotations: None,
1577                    }),
1578                    command: None,
1579                    is_preferred: Some(true),
1580                    disabled: None,
1581                    data: None,
1582                }))
1583            })
1584            .collect();
1585
1586        Ok(Some(actions))
1587    }
1588
1589    async fn document_color(&self, params: DocumentColorParams) -> RpcResult<Vec<ColorInformation>> {
1590        let filename = params.text_document.uri.to_string();
1591
1592        let Some(current_code) = self.code_map.get(&filename) else {
1593            return Ok(vec![]);
1594        };
1595        let Ok(current_code) = std::str::from_utf8(&current_code) else {
1596            return Ok(vec![]);
1597        };
1598
1599        // Get the ast from our map.
1600        let Some(ast) = self.ast_map.get(&filename) else {
1601            return Ok(vec![]);
1602        };
1603
1604        // Get the colors from the ast.
1605        let Ok(colors) = ast.ast.document_color(current_code) else {
1606            return Ok(vec![]);
1607        };
1608
1609        Ok(colors)
1610    }
1611
1612    async fn color_presentation(&self, params: ColorPresentationParams) -> RpcResult<Vec<ColorPresentation>> {
1613        let filename = params.text_document.uri.to_string();
1614
1615        let Some(current_code) = self.code_map.get(&filename) else {
1616            return Ok(vec![]);
1617        };
1618        let Ok(current_code) = std::str::from_utf8(&current_code) else {
1619            return Ok(vec![]);
1620        };
1621
1622        // Get the ast from our map.
1623        let Some(ast) = self.ast_map.get(&filename) else {
1624            return Ok(vec![]);
1625        };
1626
1627        let pos_start = position_to_char_index(params.range.start, current_code);
1628        let pos_end = position_to_char_index(params.range.end, current_code);
1629
1630        // Get the colors from the ast.
1631        let Ok(Some(presentation)) = ast.ast.color_presentation(&params.color, pos_start, pos_end) else {
1632            return Ok(vec![]);
1633        };
1634
1635        Ok(vec![presentation])
1636    }
1637}
1638
1639/// Get completions from our stdlib.
1640pub fn get_completions_from_stdlib(kcl_std: &ModData) -> Result<HashMap<String, CompletionItem>> {
1641    let mut completions = HashMap::new();
1642
1643    for d in kcl_std.all_docs() {
1644        if let Some(ci) = d.to_completion_item() {
1645            completions.insert(d.name().to_owned(), ci);
1646        }
1647    }
1648
1649    let variable_kinds = VariableKind::to_completion_items();
1650    for variable_kind in variable_kinds {
1651        completions.insert(variable_kind.label.clone(), variable_kind);
1652    }
1653
1654    Ok(completions)
1655}
1656
1657/// Get signatures from our stdlib.
1658pub fn get_signatures_from_stdlib(kcl_std: &ModData) -> HashMap<String, SignatureHelp> {
1659    let mut signatures = HashMap::new();
1660
1661    for d in kcl_std.all_docs() {
1662        if let Some(sig) = d.to_signature_help() {
1663            signatures.insert(d.name().to_owned(), sig);
1664        }
1665    }
1666
1667    signatures
1668}
1669
1670/// Get signatures from our stdlib.
1671pub fn get_arg_maps_from_stdlib(kcl_std: &ModData) -> HashMap<String, HashMap<String, String>> {
1672    let mut result = HashMap::new();
1673
1674    for d in kcl_std.all_docs() {
1675        let crate::docs::kcl_doc::DocData::Fn(f) = d else {
1676            continue;
1677        };
1678        let arg_map: HashMap<String, String> = f
1679            .args
1680            .iter()
1681            .map(|data| {
1682                let mut tip = "```\n".to_owned();
1683                tip.push_str(&data.to_string());
1684                tip.push_str("\n```");
1685                if let Some(docs) = &data.docs {
1686                    tip.push_str("\n\n");
1687                    tip.push_str(docs);
1688                }
1689                (data.name.clone(), tip)
1690            })
1691            .collect();
1692        if !arg_map.is_empty() {
1693            result.insert(f.name.clone(), arg_map);
1694        }
1695    }
1696
1697    result
1698}
1699
1700/// Convert a position to a character index from the start of the file.
1701fn position_to_char_index(position: Position, code: &str) -> usize {
1702    // Get the character position from the start of the file.
1703    let mut char_position = 0;
1704    for (index, line) in code.lines().enumerate() {
1705        if index == position.line as usize {
1706            char_position += position.character as usize;
1707            break;
1708        } else {
1709            char_position += line.len() + 1;
1710        }
1711    }
1712
1713    std::cmp::min(char_position, code.len() - 1)
1714}
1715
1716async fn with_cached_var<T>(name: &str, f: impl Fn(&KclValue) -> T) -> Option<T> {
1717    let mem = cache::read_old_memory().await?;
1718    let value = mem.0.get(name, SourceRange::default()).ok()?;
1719
1720    Some(f(value))
1721}
1722
1723#[cfg(test)]
1724mod tests {
1725    use pretty_assertions::assert_eq;
1726
1727    use super::*;
1728
1729    #[test]
1730    fn test_position_to_char_index_first_line() {
1731        let code = r#"def foo():
1732return 42"#;
1733        let position = Position::new(0, 3);
1734        let index = position_to_char_index(position, code);
1735        assert_eq!(index, 3);
1736    }
1737
1738    #[test]
1739    fn test_position_to_char_index() {
1740        let code = r#"def foo():
1741return 42"#;
1742        let position = Position::new(1, 4);
1743        let index = position_to_char_index(position, code);
1744        assert_eq!(index, 15);
1745    }
1746
1747    #[test]
1748    fn test_position_to_char_index_with_newline() {
1749        let code = r#"def foo():
1750
1751return 42"#;
1752        let position = Position::new(2, 0);
1753        let index = position_to_char_index(position, code);
1754        assert_eq!(index, 12);
1755    }
1756
1757    #[test]
1758    fn test_position_to_char_at_end() {
1759        let code = r#"def foo():
1760return 42"#;
1761
1762        let position = Position::new(1, 8);
1763        let index = position_to_char_index(position, code);
1764        assert_eq!(index, 19);
1765    }
1766}