diff --git a/kclvm/tools/src/LSP/src/notification.rs b/kclvm/tools/src/LSP/src/notification.rs index d553022fc..d7be1b5a5 100644 --- a/kclvm/tools/src/LSP/src/notification.rs +++ b/kclvm/tools/src/LSP/src/notification.rs @@ -97,7 +97,9 @@ impl LanguageServerState { let old_text = text.clone(); apply_document_changes(&mut text, content_changes); vfs.set_file_contents(path.into(), Some(text.clone().into_bytes())); - + self.opened_files + .write() + .insert(file_id, text_document.version); // Update word index let old_word_index = build_word_index_with_content(&old_text, &text_document.uri, true); let new_word_index = build_word_index_with_content(&text, &text_document.uri, true); diff --git a/kclvm/tools/src/LSP/src/request.rs b/kclvm/tools/src/LSP/src/request.rs index 7afde3ccd..f5b36ba31 100644 --- a/kclvm/tools/src/LSP/src/request.rs +++ b/kclvm/tools/src/LSP/src/request.rs @@ -151,7 +151,6 @@ impl LanguageServerSnapshot { let current_version = *self.opened_files.read().get(&file_id).ok_or_else(|| { anyhow::anyhow!(LSPError::DocumentVersionNotFound(path.clone().into())) })?; - Ok(db_version == current_version) } } @@ -306,16 +305,23 @@ pub(crate) fn handle_completion( return Ok(None); } - let kcl_pos = kcl_pos(&file, params.text_document_position.position); + let db = match snapshot.get_db(&path.clone().into()) { + Ok(db) => db, + Err(_) => return Ok(None), + }; + let completion_trigger_character = params .context .and_then(|ctx| ctx.trigger_character) .and_then(|s| s.chars().next()); - let db = match snapshot.get_db(&path.clone().into()) { - Ok(db) => db, - Err(_) => return Ok(None), - }; + if matches!(completion_trigger_character, Some('\n')) { + if !snapshot.verify_request_version(db.version, &path)? { + return Err(anyhow!(LSPError::Retry)); + } + } + + let kcl_pos = kcl_pos(&file, params.text_document_position.position); let metadata = snapshot .entry_cache diff --git a/kclvm/tools/src/LSP/src/test_data/completion_test/newline/newline_with_version/newline_with_version.k b/kclvm/tools/src/LSP/src/test_data/completion_test/newline/newline_with_version/newline_with_version.k new file mode 100644 index 000000000..cd37b7c40 --- /dev/null +++ b/kclvm/tools/src/LSP/src/test_data/completion_test/newline/newline_with_version/newline_with_version.k @@ -0,0 +1,6 @@ +schema Name: + name: str + +name1 = "" + +name: Name{} \ No newline at end of file diff --git a/kclvm/tools/src/LSP/src/tests.rs b/kclvm/tools/src/LSP/src/tests.rs index e7f4a0fd0..e4cb70f41 100644 --- a/kclvm/tools/src/LSP/src/tests.rs +++ b/kclvm/tools/src/LSP/src/tests.rs @@ -1172,6 +1172,93 @@ fn complete_test() { ) } +#[test] +fn complete_with_version_test() { + let root = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let mut path = root.clone(); + + path.push("src/test_data/completion_test/newline/newline_with_version/newline_with_version.k"); + + let path = path.to_str().unwrap(); + let src = std::fs::read_to_string(path).unwrap(); + let server = Project {}.server(InitializeParams::default()); + + // Mock open file + server.notification::( + lsp_types::DidOpenTextDocumentParams { + text_document: TextDocumentItem { + uri: Url::from_file_path(path).unwrap(), + language_id: "KCL".to_string(), + version: 0, + text: src, + }, + }, + ); + + let id = server.next_request_id.get(); + server.next_request_id.set(id.wrapping_add(1)); + + let r: Request = Request::new( + id.into(), + "textDocument/completion".to_string(), + CompletionParams { + text_document_position: TextDocumentPositionParams { + text_document: TextDocumentIdentifier { + uri: Url::from_file_path(path).unwrap(), + }, + position: Position::new(6, 4), + }, + work_done_progress_params: Default::default(), + partial_result_params: Default::default(), + context: Some(CompletionContext { + trigger_kind: CompletionTriggerKind::TRIGGER_CHARACTER, + trigger_character: Some("\n".to_string()), + }), + }, + ); + + let id = r.id.clone(); + server.client.sender.send(r.into()).unwrap(); + + server.notification::( + lsp_types::DidChangeTextDocumentParams { + text_document: lsp_types::VersionedTextDocumentIdentifier { + uri: Url::from_file_path(path).unwrap(), + version: 1, + }, + content_changes: vec![TextDocumentContentChangeEvent { + range: None, + range_length: None, + text: "schema Name:\n name: str\n\nname1 = \"\"\n\nname: Name{\n \n}" + .to_string(), + }], + }, + ); + + while let Some(msg) = server.recv() { + match msg { + Message::Request(req) => { + panic!("did not expect a request as a response to a request: {req:?}") + } + Message::Notification(_) => (), + Message::Response(res) => { + assert_eq!(res.id, id); + assert_eq!( + res.result.unwrap(), + to_json(CompletionResponse::Array(vec![CompletionItem { + label: "name".to_string(), + kind: Some(CompletionItemKind::FIELD), + detail: Some("name: str".to_string()), + ..Default::default() + },])) + .unwrap() + ); + break; + } + } + } +} + #[test] fn hover_test() { let root = PathBuf::from(env!("CARGO_MANIFEST_DIR"));