add more tests for various scenarios (#2812)

* add more tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* cleanups

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* clippy

Signed-off-by: Jess Frazelle <github@jessfraz.com>

* even more tests

Signed-off-by: Jess Frazelle <github@jessfraz.com>

---------

Signed-off-by: Jess Frazelle <github@jessfraz.com>
This commit is contained in:
Jess Frazelle
2024-06-26 01:07:18 -07:00
committed by GitHub
parent 47e8d3f4fc
commit 9ffc08b84a
3 changed files with 761 additions and 28 deletions

View File

@ -67,6 +67,12 @@ impl Discovered {
} }
impl IntoDiagnostic for Discovered { impl IntoDiagnostic for Discovered {
fn to_lsp_diagnostic(&self, code: &str) -> Diagnostic {
(&self).to_lsp_diagnostic(code)
}
}
impl IntoDiagnostic for &Discovered {
fn to_lsp_diagnostic(&self, code: &str) -> Diagnostic { fn to_lsp_diagnostic(&self, code: &str) -> Diagnostic {
let message = self.finding.title.to_owned(); let message = self.finding.title.to_owned();
let source_range = self.pos; let source_range = self.pos;

View File

@ -14,21 +14,21 @@ use tower_lsp::{
jsonrpc::Result as RpcResult, jsonrpc::Result as RpcResult,
lsp_types::{ lsp_types::{
CompletionItem, CompletionItemKind, CompletionOptions, CompletionParams, CompletionResponse, CreateFilesParams, CompletionItem, CompletionItemKind, CompletionOptions, CompletionParams, CompletionResponse, CreateFilesParams,
DeleteFilesParams, DiagnosticOptions, DiagnosticServerCapabilities, DidChangeConfigurationParams, DeleteFilesParams, DiagnosticOptions, DiagnosticServerCapabilities, DiagnosticSeverity,
DidChangeTextDocumentParams, DidChangeWatchedFilesParams, DidChangeWorkspaceFoldersParams, DidChangeConfigurationParams, DidChangeTextDocumentParams, DidChangeWatchedFilesParams,
DidCloseTextDocumentParams, DidOpenTextDocumentParams, DidSaveTextDocumentParams, DocumentDiagnosticParams, DidChangeWorkspaceFoldersParams, DidCloseTextDocumentParams, DidOpenTextDocumentParams,
DocumentDiagnosticReport, DocumentDiagnosticReportResult, DocumentFilter, DocumentFormattingParams, DidSaveTextDocumentParams, DocumentDiagnosticParams, DocumentDiagnosticReport, DocumentDiagnosticReportResult,
DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse, Documentation, FoldingRange, FoldingRangeParams, DocumentFilter, DocumentFormattingParams, DocumentSymbol, DocumentSymbolParams, DocumentSymbolResponse,
FoldingRangeProviderCapability, FullDocumentDiagnosticReport, Hover, HoverContents, HoverParams, Documentation, FoldingRange, FoldingRangeParams, FoldingRangeProviderCapability, FullDocumentDiagnosticReport,
HoverProviderCapability, InitializeParams, InitializeResult, InitializedParams, InlayHint, InlayHintParams, Hover, HoverContents, HoverParams, HoverProviderCapability, InitializeParams, InitializeResult,
InsertTextFormat, MarkupContent, MarkupKind, MessageType, OneOf, Position, RelatedFullDocumentDiagnosticReport, InitializedParams, InlayHint, InlayHintParams, InsertTextFormat, MarkupContent, MarkupKind, MessageType, OneOf,
RenameFilesParams, RenameParams, SemanticToken, SemanticTokenType, SemanticTokens, SemanticTokensFullOptions, Position, RelatedFullDocumentDiagnosticReport, RenameFilesParams, RenameParams, SemanticToken,
SemanticTokensLegend, SemanticTokensOptions, SemanticTokensParams, SemanticTokensRegistrationOptions, SemanticTokenType, SemanticTokens, SemanticTokensFullOptions, SemanticTokensLegend, SemanticTokensOptions,
SemanticTokensResult, SemanticTokensServerCapabilities, ServerCapabilities, SignatureHelp, SemanticTokensParams, SemanticTokensRegistrationOptions, SemanticTokensResult,
SignatureHelpOptions, SignatureHelpParams, StaticRegistrationOptions, TextDocumentItem, SemanticTokensServerCapabilities, ServerCapabilities, SignatureHelp, SignatureHelpOptions, SignatureHelpParams,
TextDocumentRegistrationOptions, TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, StaticRegistrationOptions, TextDocumentItem, TextDocumentRegistrationOptions, TextDocumentSyncCapability,
TextEdit, WorkDoneProgressOptions, WorkspaceEdit, WorkspaceFolder, WorkspaceFoldersServerCapabilities, TextDocumentSyncKind, TextDocumentSyncOptions, TextEdit, WorkDoneProgressOptions, WorkspaceEdit,
WorkspaceServerCapabilities, WorkspaceFolder, WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities,
}, },
Client, LanguageServer, Client, LanguageServer,
}; };
@ -168,14 +168,18 @@ impl crate::lsp::backend::Backend for Backend {
} }
async fn inner_on_change(&self, params: TextDocumentItem, force: bool) { async fn inner_on_change(&self, params: TextDocumentItem, force: bool) {
self.clear_diagnostics_map(&params.uri).await;
// We already updated the code map in the shared backend. // We already updated the code map in the shared backend.
// Lets update the tokens. // Lets update the tokens.
let tokens = match crate::token::lexer(&params.text) { let tokens = match crate::token::lexer(&params.text) {
Ok(tokens) => tokens, Ok(tokens) => tokens,
Err(err) => { Err(err) => {
self.add_to_diagnostics(&params, err).await; self.add_to_diagnostics(&params, err, true).await;
self.token_map.remove(&params.uri.to_string()).await;
self.ast_map.remove(&params.uri.to_string()).await;
self.symbols_map.remove(&params.uri.to_string()).await;
self.semantic_tokens_map.remove(&params.uri.to_string()).await;
self.memory_map.remove(&params.uri.to_string()).await;
return; return;
} }
}; };
@ -215,7 +219,10 @@ impl crate::lsp::backend::Backend for Backend {
let ast = match result { let ast = match result {
Ok(ast) => ast, Ok(ast) => ast,
Err(err) => { Err(err) => {
self.add_to_diagnostics(&params, err).await; self.add_to_diagnostics(&params, err, true).await;
self.ast_map.remove(&params.uri.to_string()).await;
self.symbols_map.remove(&params.uri.to_string()).await;
self.memory_map.remove(&params.uri.to_string()).await;
return; return;
} }
}; };
@ -243,6 +250,21 @@ impl crate::lsp::backend::Backend for Backend {
ast.get_lsp_symbols(&params.text).unwrap_or_default(), ast.get_lsp_symbols(&params.text).unwrap_or_default(),
) )
.await; .await;
#[cfg(not(target_arch = "wasm32"))]
{
let discovered_findings = ast
.lint(checks::lint_variables)
.into_iter()
.flatten()
.collect::<Vec<_>>();
// Clear the lints before we lint.
self.clear_diagnostics_map(&params.uri, Some(DiagnosticSeverity::INFORMATION))
.await;
for discovered_finding in &discovered_findings {
self.add_to_diagnostics(&params, discovered_finding, false).await;
}
}
} }
// Send the notification to the client that the ast was updated. // Send the notification to the client that the ast was updated.
@ -261,12 +283,9 @@ impl crate::lsp::backend::Backend for Backend {
return; return;
} }
#[cfg(not(target_arch = "wasm32"))] // If we made it here we can clear the diagnostics.
{ self.clear_diagnostics_map(&params.uri, Some(DiagnosticSeverity::ERROR))
for discovered_finding in ast.lint(checks::lint_variables).into_iter().flatten() { .await;
self.add_to_diagnostics(&params, discovered_finding).await;
}
}
} }
} }
@ -364,7 +383,19 @@ impl Backend {
.await; .await;
} }
async fn clear_diagnostics_map(&self, uri: &url::Url) { async fn clear_diagnostics_map(&self, uri: &url::Url, severity: Option<DiagnosticSeverity>) {
let mut items = match self.diagnostics_map.get(uri.as_str()).await {
Some(DocumentDiagnosticReport::Full(report)) => report.full_document_diagnostic_report.items.clone(),
_ => vec![],
};
// If we only want to clear a specific severity, do that.
if let Some(severity) = severity {
items.retain(|x| x.severity != Some(severity));
} else {
items.clear();
}
self.diagnostics_map self.diagnostics_map
.insert( .insert(
uri.to_string(), uri.to_string(),
@ -372,7 +403,7 @@ impl Backend {
related_documents: None, related_documents: None,
full_document_diagnostic_report: FullDocumentDiagnosticReport { full_document_diagnostic_report: FullDocumentDiagnosticReport {
result_id: None, result_id: None,
items: vec![], items: items.clone(),
}, },
}), }),
) )
@ -380,7 +411,7 @@ impl Backend {
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
{ {
self.client.publish_diagnostics(uri.clone(), vec![], None).await; self.client.publish_diagnostics(uri.clone(), items, None).await;
} }
} }
@ -388,6 +419,7 @@ impl Backend {
&self, &self,
params: &TextDocumentItem, params: &TextDocumentItem,
diagnostic: DiagT, diagnostic: DiagT,
clear_all_before_add: bool,
) { ) {
self.client self.client
.log_message(MessageType::INFO, format!("adding {:?} to diag", diagnostic)) .log_message(MessageType::INFO, format!("adding {:?} to diag", diagnostic))
@ -395,6 +427,16 @@ impl Backend {
let diagnostic = diagnostic.to_lsp_diagnostic(&params.text); let diagnostic = diagnostic.to_lsp_diagnostic(&params.text);
if clear_all_before_add {
self.clear_diagnostics_map(&params.uri, None).await;
} else if diagnostic.severity == Some(DiagnosticSeverity::ERROR) {
// If the diagnostic is an error, it will be the only error we get since that halts
// execution.
// Clear the diagnostics before we add a new one.
self.clear_diagnostics_map(&params.uri, Some(DiagnosticSeverity::ERROR))
.await;
}
let DocumentDiagnosticReport::Full(mut report) = self let DocumentDiagnosticReport::Full(mut report) = self
.diagnostics_map .diagnostics_map
.get(params.uri.clone().as_str()) .get(params.uri.clone().as_str())
@ -410,6 +452,19 @@ impl Backend {
unreachable!(); unreachable!();
}; };
// Ensure we don't already have this diagnostic.
if report
.full_document_diagnostic_report
.items
.iter()
.any(|x| x == &diagnostic)
{
self.client
.publish_diagnostics(params.uri.clone(), report.full_document_diagnostic_report.items, None)
.await;
return;
}
report.full_document_diagnostic_report.items.push(diagnostic); report.full_document_diagnostic_report.items.push(diagnostic);
self.diagnostics_map self.diagnostics_map
@ -443,7 +498,8 @@ impl Backend {
let memory = match executor_ctx.run(ast, None).await { let memory = match executor_ctx.run(ast, None).await {
Ok(memory) => memory, Ok(memory) => memory,
Err(err) => { Err(err) => {
self.add_to_diagnostics(params, err).await; self.memory_map.remove(&params.uri.to_string()).await;
self.add_to_diagnostics(params, err, false).await;
// Since we already published the diagnostics we don't really care about the error // Since we already published the diagnostics we don't really care about the error
// string. // string.

View File

@ -2998,3 +2998,674 @@ async fn test_kcl_lsp_folding() {
} }
); );
} }
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_kcl_lsp_code_with_parse_error_and_ast_unchanged_but_has_diagnostics_reparse() {
let server = kcl_lsp_server(false).await.unwrap();
let code = r#"const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)
|> ^^^things(3.14, %)"#;
// 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: code.to_string(),
},
})
.await;
server.wait_on_handle().await;
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").await;
assert!(ast.is_none());
// Assure we have one diagnostics.
let diagnostics = server.diagnostics_map.get("file:///test.kcl").await.unwrap().clone();
if let tower_lsp::lsp_types::DocumentDiagnosticReport::Full(diagnostics) = diagnostics {
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 1);
} else {
panic!("Expected full diagnostics");
}
// Send change file, but the code is the same.
server
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
version: 2,
},
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: code.to_string(),
}],
})
.await;
server.wait_on_handle().await;
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").await;
assert!(ast.is_none());
// Assure we have one diagnostics.
let diagnostics = server.diagnostics_map.get("file:///test.kcl").await.unwrap().clone();
if let tower_lsp::lsp_types::DocumentDiagnosticReport::Full(diagnostics) = diagnostics {
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 1);
} else {
panic!("Expected full diagnostics");
}
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_kcl_lsp_code_with_lint_and_ast_unchanged_but_has_diagnostics_reparse() {
let server = kcl_lsp_server(false).await.unwrap();
let code = r#"const LINT = 1
const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)
|> extrude(3.14, %)"#;
// 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: code.to_string(),
},
})
.await;
server.wait_on_handle().await;
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").await.unwrap().clone();
assert!(ast != crate::ast::types::Program::default());
// Assure we have one diagnostics.
let diagnostics = server.diagnostics_map.get("file:///test.kcl").await.unwrap().clone();
if let tower_lsp::lsp_types::DocumentDiagnosticReport::Full(diagnostics) = diagnostics {
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 1);
} else {
panic!("Expected full diagnostics");
}
// Send change file, but the code is the same.
server
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
version: 2,
},
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: code.to_string(),
}],
})
.await;
server.wait_on_handle().await;
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").await.unwrap().clone();
assert!(ast != crate::ast::types::Program::default());
// Assure we have one diagnostics.
let diagnostics = server.diagnostics_map.get("file:///test.kcl").await.unwrap().clone();
if let tower_lsp::lsp_types::DocumentDiagnosticReport::Full(diagnostics) = diagnostics {
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 1);
} else {
panic!("Expected full diagnostics");
}
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_kcl_lsp_code_with_lint_and_parse_error_and_ast_unchanged_but_has_diagnostics_reparse() {
let server = kcl_lsp_server(false).await.unwrap();
let code = r#"const LINT = 1
const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)
|> ^^^^thing(3.14, %)"#;
// 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: code.to_string(),
},
})
.await;
server.wait_on_handle().await;
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").await;
assert!(ast.is_none());
// Assure we have diagnostics.
let diagnostics = server.diagnostics_map.get("file:///test.kcl").await.unwrap().clone();
if let tower_lsp::lsp_types::DocumentDiagnosticReport::Full(diagnostics) = diagnostics {
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 1);
} else {
panic!("Expected full diagnostics");
}
// Send change file, but the code is the same.
server
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
version: 2,
},
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: code.to_string(),
}],
})
.await;
server.wait_on_handle().await;
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").await;
assert!(ast.is_none());
// Assure we have one diagnostics.
let diagnostics = server.diagnostics_map.get("file:///test.kcl").await.unwrap().clone();
if let tower_lsp::lsp_types::DocumentDiagnosticReport::Full(diagnostics) = diagnostics {
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 1);
} else {
panic!("Expected full diagnostics");
}
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_kcl_lsp_code_lint_and_ast_unchanged_but_has_diagnostics_reexecute() {
let server = kcl_lsp_server(true).await.unwrap();
let code = r#"const LINT = 1
const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %, $seg01)
|> line([-20, 0], %, $seg01)
|> close(%)
|> extrude(3.14, %)"#;
// 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: code.to_string(),
},
})
.await;
server.wait_on_handle().await;
// Assure we have diagnostics.
let diagnostics = server.diagnostics_map.get("file:///test.kcl").await.unwrap().clone();
// Check the diagnostics.
if let tower_lsp::lsp_types::DocumentDiagnosticReport::Full(ref diagnostics) = diagnostics {
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 2);
} else {
panic!("Expected full diagnostics");
}
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").await.unwrap().clone();
assert!(ast != crate::ast::types::Program::default());
// Get the memory.
let memory = server.memory_map.get("file:///test.kcl").await;
assert!(memory.is_none());
// Send change file, but the code is the same.
server
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
version: 2,
},
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: code.to_string(),
}],
})
.await;
server.wait_on_handle().await;
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").await.unwrap().clone();
assert!(ast != crate::ast::types::Program::default());
// Get the memory.
let memory = server.memory_map.get("file:///test.kcl").await;
assert!(memory.is_none());
// Assure we have diagnostics.
let diagnostics = server.diagnostics_map.get("file:///test.kcl").await.unwrap().clone();
// Check the diagnostics.
if let tower_lsp::lsp_types::DocumentDiagnosticReport::Full(diagnostics) = diagnostics {
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 2);
} else {
panic!("Expected full diagnostics");
}
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_kcl_lsp_code_lint_reexecute_new_lint() {
let server = kcl_lsp_server(true).await.unwrap();
let code = r#"const LINT = 1
const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %, $seg01)
|> line([-20, 0], %, $seg01)
|> close(%)
|> extrude(3.14, %)"#;
// 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: code.to_string(),
},
})
.await;
server.wait_on_handle().await;
// Assure we have diagnostics.
let diagnostics = server.diagnostics_map.get("file:///test.kcl").await.unwrap().clone();
// Check the diagnostics.
if let tower_lsp::lsp_types::DocumentDiagnosticReport::Full(ref diagnostics) = diagnostics {
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 2);
} else {
panic!("Expected full diagnostics");
}
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").await.unwrap().clone();
assert!(ast != crate::ast::types::Program::default());
// Get the memory.
let memory = server.memory_map.get("file:///test.kcl").await;
assert!(memory.is_none());
// Send change file, but the code is the same.
server
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
version: 2,
},
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: r#"const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %, $seg01)
|> line([-20, 0], %, $seg01)
|> close(%)
|> extrude(3.14, %)
const NEW_LINT = 1"#
.to_string(),
}],
})
.await;
server.wait_on_handle().await;
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").await.unwrap().clone();
assert!(ast != crate::ast::types::Program::default());
// Get the memory.
let memory = server.memory_map.get("file:///test.kcl").await;
assert!(memory.is_none());
// Assure we have diagnostics.
let diagnostics = server.diagnostics_map.get("file:///test.kcl").await.unwrap().clone();
// Check the diagnostics.
if let tower_lsp::lsp_types::DocumentDiagnosticReport::Full(diagnostics) = diagnostics {
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 2);
} else {
panic!("Expected full diagnostics");
}
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_kcl_lsp_code_lint_reexecute_new_ast_error() {
let server = kcl_lsp_server(true).await.unwrap();
let code = r#"const LINT = 1
const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %, $seg01)
|> line([-20, 0], %, $seg01)
|> close(%)
|> ^^^extrude(3.14, %)"#;
// 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: code.to_string(),
},
})
.await;
server.wait_on_handle().await;
// Assure we have diagnostics.
let diagnostics = server.diagnostics_map.get("file:///test.kcl").await.unwrap().clone();
// Check the diagnostics.
if let tower_lsp::lsp_types::DocumentDiagnosticReport::Full(ref diagnostics) = diagnostics {
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 1);
} else {
panic!("Expected full diagnostics");
}
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").await;
assert!(ast.is_none());
// Get the memory.
let memory = server.memory_map.get("file:///test.kcl").await;
assert!(memory.is_none());
// Send change file, but the code is the same.
server
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
version: 2,
},
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: r#"const part001 = startSketchOn('XY')
|> ^^^^startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %, $seg01)
|> line([-20, 0], %, $seg01)
|> close(%)
|> extrude(3.14, %)
const NEW_LINT = 1"#
.to_string(),
}],
})
.await;
server.wait_on_handle().await;
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").await;
assert!(ast.is_none());
// Get the memory.
let memory = server.memory_map.get("file:///test.kcl").await;
assert!(memory.is_none());
// Assure we have diagnostics.
let diagnostics = server.diagnostics_map.get("file:///test.kcl").await.unwrap().clone();
// Check the diagnostics.
if let tower_lsp::lsp_types::DocumentDiagnosticReport::Full(diagnostics) = diagnostics {
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 1);
} else {
panic!("Expected full diagnostics");
}
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_kcl_lsp_code_lint_reexecute_had_lint_new_parse_error() {
let server = kcl_lsp_server(true).await.unwrap();
let code = r#"const LINT = 1
const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)
"#;
// 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: code.to_string(),
},
})
.await;
server.wait_on_handle().await;
// Assure we have diagnostics.
let diagnostics = server.diagnostics_map.get("file:///test.kcl").await.unwrap().clone();
// Check the diagnostics.
if let tower_lsp::lsp_types::DocumentDiagnosticReport::Full(ref diagnostics) = diagnostics {
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 1);
} else {
panic!("Expected full diagnostics");
}
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").await.unwrap().clone();
assert!(ast != crate::ast::types::Program::default());
// Get the symbols map.
let symbols_map = server.symbols_map.get("file:///test.kcl").await.unwrap().clone();
assert!(symbols_map != vec![]);
// Get the semantic tokens map.
let semantic_tokens_map = server
.semantic_tokens_map
.get("file:///test.kcl")
.await
.unwrap()
.clone();
assert!(semantic_tokens_map != vec![]);
// Get the memory.
let memory = server.memory_map.get("file:///test.kcl").await.unwrap().clone();
assert!(memory != ProgramMemory::default());
// Send change file, but the code is the same.
server
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
version: 2,
},
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: r#"const part001 = startSketchOn('XY')
|> ^^^^startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)
|> extrude(3.14, %)
const NEW_LINT = 1"#
.to_string(),
}],
})
.await;
server.wait_on_handle().await;
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").await;
assert!(ast.is_none());
// Get the symbols map.
let symbols_map = server.symbols_map.get("file:///test.kcl").await;
assert!(symbols_map.is_none());
// Get the semantic tokens map.
let semantic_tokens_map = server
.semantic_tokens_map
.get("file:///test.kcl")
.await
.unwrap()
.clone();
assert!(semantic_tokens_map != vec![]);
// Get the memory.
let memory = server.memory_map.get("file:///test.kcl").await;
assert!(memory.is_none());
// Assure we have diagnostics.
let diagnostics = server.diagnostics_map.get("file:///test.kcl").await.unwrap().clone();
// Check the diagnostics.
if let tower_lsp::lsp_types::DocumentDiagnosticReport::Full(diagnostics) = diagnostics {
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 1);
} else {
panic!("Expected full diagnostics");
}
}
#[tokio::test(flavor = "multi_thread")]
async fn serial_test_kcl_lsp_code_lint_reexecute_had_lint_new_execution_error() {
let server = kcl_lsp_server(true).await.unwrap();
let code = r#"const LINT = 1
const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %)
|> line([0, 20], %)
|> line([-20, 0], %)
|> close(%)
"#;
// 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: code.to_string(),
},
})
.await;
server.wait_on_handle().await;
// Assure we have diagnostics.
let diagnostics = server.diagnostics_map.get("file:///test.kcl").await.unwrap().clone();
// Check the diagnostics.
if let tower_lsp::lsp_types::DocumentDiagnosticReport::Full(ref diagnostics) = diagnostics {
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 1);
} else {
panic!("Expected full diagnostics");
}
// Get the token map.
let token_map = server.token_map.get("file:///test.kcl").await.unwrap().clone();
assert!(token_map != vec![]);
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").await.unwrap().clone();
assert!(ast != crate::ast::types::Program::default());
// Get the symbols map.
let symbols_map = server.symbols_map.get("file:///test.kcl").await.unwrap().clone();
assert!(symbols_map != vec![]);
// Get the semantic tokens map.
let semantic_tokens_map = server
.semantic_tokens_map
.get("file:///test.kcl")
.await
.unwrap()
.clone();
assert!(semantic_tokens_map != vec![]);
// Get the memory.
let memory = server.memory_map.get("file:///test.kcl").await.unwrap().clone();
assert!(memory != ProgramMemory::default());
// Send change file, but the code is the same.
server
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
text_document: tower_lsp::lsp_types::VersionedTextDocumentIdentifier {
uri: "file:///test.kcl".try_into().unwrap(),
version: 2,
},
content_changes: vec![tower_lsp::lsp_types::TextDocumentContentChangeEvent {
range: None,
range_length: None,
text: r#"const LINT = 1
const part001 = startSketchOn('XY')
|> startProfileAt([-10, -10], %)
|> line([20, 0], %, $seg01)
|> line([0, 20], %, $seg01)
|> line([-20, 0], %)
|> close(%)
"#
.to_string(),
}],
})
.await;
server.wait_on_handle().await;
// Get the token map.
let token_map = server.token_map.get("file:///test.kcl").await.unwrap().clone();
assert!(token_map != vec![]);
// Get the ast.
let ast = server.ast_map.get("file:///test.kcl").await.unwrap().clone();
assert!(ast != crate::ast::types::Program::default());
// Get the symbols map.
let symbols_map = server.symbols_map.get("file:///test.kcl").await.unwrap().clone();
assert!(symbols_map != vec![]);
// Get the semantic tokens map.
let semantic_tokens_map = server
.semantic_tokens_map
.get("file:///test.kcl")
.await
.unwrap()
.clone();
assert!(semantic_tokens_map != vec![]);
// Get the memory.
let memory = server.memory_map.get("file:///test.kcl").await;
assert!(memory.is_none());
// Assure we have diagnostics.
let diagnostics = server.diagnostics_map.get("file:///test.kcl").await.unwrap().clone();
// Check the diagnostics.
if let tower_lsp::lsp_types::DocumentDiagnosticReport::Full(diagnostics) = diagnostics {
assert_eq!(diagnostics.full_document_diagnostic_report.items.len(), 2);
} else {
panic!("Expected full diagnostics");
}
}