2024-06-27 15:43:49 -07:00
use std ::collections ::BTreeMap ;
2024-03-12 23:57:43 -07:00
use pretty_assertions ::assert_eq ;
2024-06-26 05:02:36 -07:00
use tower_lsp ::{
2024-11-18 16:25:25 -05:00
lsp_types ::{ Diagnostic , SemanticTokenModifier , SemanticTokenType } ,
2024-06-26 05:02:36 -07:00
LanguageServer ,
} ;
2024-03-12 23:57:43 -07:00
2024-06-27 15:43:49 -07:00
use crate ::{
lsp ::test_util ::{ copilot_lsp_server , kcl_lsp_server } ,
2024-12-05 17:56:49 +13:00
parsing ::ast ::types ::{ Node , Program } ,
2024-06-27 15:43:49 -07:00
} ;
2024-03-12 23:57:43 -07:00
2024-12-06 13:57:31 +13:00
#[ track_caller ]
fn assert_diagnostic_count ( diagnostics : Option < & Vec < Diagnostic > > , n : usize ) {
let Some ( diagnostics ) = diagnostics else {
assert_eq! ( n , 0 , " No diagnostics " ) ;
return ;
} ;
assert_eq! (
diagnostics
. iter ( )
. filter ( | d | d . severity . as_ref ( ) . unwrap ( ) ! = & tower_lsp ::lsp_types ::DiagnosticSeverity ::WARNING )
. count ( ) ,
n ,
" expected {n} errors, found {diagnostics:#?} "
) ;
}
2024-04-15 17:18:32 -07:00
#[ tokio::test(flavor = " multi_thread " , worker_threads = 12) ]
2024-03-12 23:57:43 -07:00
async fn test_updating_kcl_lsp_files ( ) {
2024-04-12 21:32:57 -07:00
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
2024-06-27 15:43:49 -07:00
assert_eq! ( server . code_map . len ( ) , 0 ) ;
2024-03-12 23:57:43 -07:00
// Get the path to the current file.
let path = std ::path ::Path ::new ( env! ( " CARGO_MANIFEST_DIR " ) ) . join ( " src " ) . join ( " lsp " ) ;
let string_path = format! ( " file:// {} " , path . display ( ) ) ;
// Run workspace folders change.
server
. did_change_workspace_folders ( tower_lsp ::lsp_types ::DidChangeWorkspaceFoldersParams {
event : tower_lsp ::lsp_types ::WorkspaceFoldersChangeEvent {
added : vec ! [ tower_lsp ::lsp_types ::WorkspaceFolder {
uri : string_path . as_str ( ) . try_into ( ) . unwrap ( ) ,
name : " my-project " . to_string ( ) ,
} ] ,
removed : vec ! [ ] ,
} ,
} )
. await ;
// Get the workspace folders.
2024-06-27 15:43:49 -07:00
assert_eq! ( server . workspace_folders . len ( ) , 1 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . workspace_folders . get ( " my-project " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
tower_lsp ::lsp_types ::WorkspaceFolder {
uri : string_path . as_str ( ) . try_into ( ) . unwrap ( ) ,
2024-06-27 15:43:49 -07:00
name : " my-project " . to_string ( )
2024-03-12 23:57:43 -07:00
}
) ;
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 11 ) ;
2024-03-12 23:57:43 -07:00
// Run 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 : " test " . to_string ( ) ,
} ,
} )
. await ;
// Check the code map.
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 12 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test " . as_bytes ( )
) ;
// Close the file.
server
. did_close ( tower_lsp ::lsp_types ::DidCloseTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
} )
. await ;
// Check the code map.
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 12 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test " . as_bytes ( )
) ;
// Open another file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test2.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
text : " test2 " . to_string ( ) ,
} ,
} )
. await ;
// Check the code map.
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 13 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test " . as_bytes ( )
) ;
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test2.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test2 " . as_bytes ( )
) ;
// Run on change.
server
. did_change ( tower_lsp ::lsp_types ::DidChangeTextDocumentParams {
text_document : tower_lsp ::lsp_types ::VersionedTextDocumentIdentifier {
uri : " file:///test2.kcl " . try_into ( ) . unwrap ( ) ,
version : 2 ,
} ,
content_changes : vec ! [ tower_lsp ::lsp_types ::TextDocumentContentChangeEvent {
range : None ,
range_length : None ,
text : " changed " . to_string ( ) ,
} ] ,
} )
. await ;
// Check the code map.
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 13 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test " . as_bytes ( )
) ;
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test2.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" changed " . as_bytes ( )
) ;
// Rename a file.
server
. did_rename_files ( tower_lsp ::lsp_types ::RenameFilesParams {
files : vec ! [ tower_lsp ::lsp_types ::FileRename {
old_uri : " file:///test2.kcl " . into ( ) ,
new_uri : " file:///test3.kcl " . into ( ) ,
} ] ,
} )
. await ;
// Check the code map.
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 13 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test " . as_bytes ( )
) ;
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test3.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" changed " . as_bytes ( )
) ;
// Create a file.
server
. did_create_files ( tower_lsp ::lsp_types ::CreateFilesParams {
files : vec ! [ tower_lsp ::lsp_types ::FileCreate {
uri : " file:///test4.kcl " . into ( ) ,
} ] ,
} )
. await ;
// Check the code map.
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 14 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test " . as_bytes ( )
) ;
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test3.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" changed " . as_bytes ( )
) ;
2024-06-27 15:43:49 -07:00
assert_eq! ( server . code_map . get ( " file:///test4.kcl " ) . unwrap ( ) . clone ( ) , " " . as_bytes ( ) ) ;
2024-03-12 23:57:43 -07:00
// Delete a file.
server
. did_delete_files ( tower_lsp ::lsp_types ::DeleteFilesParams {
files : vec ! [ tower_lsp ::lsp_types ::FileDelete {
uri : " file:///test4.kcl " . into ( ) ,
} ] ,
} )
. await ;
// Check the code map.
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 13 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test " . as_bytes ( )
) ;
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test3.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" changed " . as_bytes ( )
) ;
2024-04-15 17:18:32 -07:00
// If we are adding the same folder we already had we should not nuke the code_map.
2024-03-12 23:57:43 -07:00
server
. did_change_workspace_folders ( tower_lsp ::lsp_types ::DidChangeWorkspaceFoldersParams {
event : tower_lsp ::lsp_types ::WorkspaceFoldersChangeEvent {
added : vec ! [ tower_lsp ::lsp_types ::WorkspaceFolder {
uri : string_path . as_str ( ) . try_into ( ) . unwrap ( ) ,
name : " my-project " . to_string ( ) ,
} ] ,
removed : vec ! [ ] ,
} ,
} )
. await ;
// Get the workspace folders.
2024-06-27 15:43:49 -07:00
assert_eq! ( server . workspace_folders . len ( ) , 1 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . workspace_folders . get ( " my-project " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
tower_lsp ::lsp_types ::WorkspaceFolder {
uri : string_path . as_str ( ) . try_into ( ) . unwrap ( ) ,
2024-06-27 15:43:49 -07:00
name : " my-project " . to_string ( )
2024-03-12 23:57:43 -07:00
}
) ;
// Check the code map.
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 13 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test " . as_bytes ( )
) ;
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test3.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" changed " . as_bytes ( )
) ;
// Remove folders.
// Run workspace folders change.
server
. did_change_workspace_folders ( tower_lsp ::lsp_types ::DidChangeWorkspaceFoldersParams {
event : tower_lsp ::lsp_types ::WorkspaceFoldersChangeEvent {
added : vec ! [ tower_lsp ::lsp_types ::WorkspaceFolder {
uri : string_path . as_str ( ) . try_into ( ) . unwrap ( ) ,
name : " my-project2 " . to_string ( ) ,
} ] ,
removed : vec ! [ tower_lsp ::lsp_types ::WorkspaceFolder {
uri : string_path . as_str ( ) . try_into ( ) . unwrap ( ) ,
name : " my-project " . to_string ( ) ,
} ] ,
} ,
} )
. await ;
// Get the workspace folders.
2024-06-27 15:43:49 -07:00
assert_eq! ( server . workspace_folders . len ( ) , 1 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . workspace_folders . get ( " my-project2 " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
tower_lsp ::lsp_types ::WorkspaceFolder {
uri : string_path . as_str ( ) . try_into ( ) . unwrap ( ) ,
2024-06-27 15:43:49 -07:00
name : " my-project2 " . to_string ( )
2024-03-12 23:57:43 -07:00
}
) ;
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 11 ) ;
2024-03-12 23:57:43 -07:00
// Just make sure that one of the current files read from disk is accurate.
assert_eq! (
2024-06-27 15:43:49 -07:00
server
. code_map
. get ( & format! ( " {} /util.rs " , string_path ) )
. unwrap ( )
. clone ( ) ,
2024-03-12 23:57:43 -07:00
include_str! ( " util.rs " ) . as_bytes ( )
) ;
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-03-12 23:57:43 -07:00
async fn test_updating_copilot_lsp_files ( ) {
2024-04-15 17:18:32 -07:00
let server = copilot_lsp_server ( ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
2024-06-27 15:43:49 -07:00
assert_eq! ( server . code_map . len ( ) , 0 ) ;
2024-03-12 23:57:43 -07:00
// Get the path to the current file.
let path = std ::path ::Path ::new ( env! ( " CARGO_MANIFEST_DIR " ) ) . join ( " src " ) . join ( " lsp " ) ;
let string_path = format! ( " file:// {} " , path . display ( ) ) ;
// Run workspace folders change.
server
. did_change_workspace_folders ( tower_lsp ::lsp_types ::DidChangeWorkspaceFoldersParams {
event : tower_lsp ::lsp_types ::WorkspaceFoldersChangeEvent {
added : vec ! [ tower_lsp ::lsp_types ::WorkspaceFolder {
uri : string_path . as_str ( ) . try_into ( ) . unwrap ( ) ,
name : " my-project " . to_string ( ) ,
} ] ,
removed : vec ! [ ] ,
} ,
} )
. await ;
// Get the workspace folders.
2024-06-27 15:43:49 -07:00
assert_eq! ( server . workspace_folders . len ( ) , 1 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . workspace_folders . get ( " my-project " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
tower_lsp ::lsp_types ::WorkspaceFolder {
uri : string_path . as_str ( ) . try_into ( ) . unwrap ( ) ,
2024-06-27 15:43:49 -07:00
name : " my-project " . to_string ( )
2024-03-12 23:57:43 -07:00
}
) ;
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 11 ) ;
2024-03-12 23:57:43 -07:00
// Run 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 : " test " . to_string ( ) ,
} ,
} )
. await ;
// Check the code map.
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 12 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test " . as_bytes ( )
) ;
// Close the file.
server
. did_close ( tower_lsp ::lsp_types ::DidCloseTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
} )
. await ;
// Check the code map.
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 12 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test " . as_bytes ( )
) ;
// Open another file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test2.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
text : " test2 " . to_string ( ) ,
} ,
} )
. await ;
// Check the code map.
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 13 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test " . as_bytes ( )
) ;
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test2.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test2 " . as_bytes ( )
) ;
// Run on change.
server
. did_change ( tower_lsp ::lsp_types ::DidChangeTextDocumentParams {
text_document : tower_lsp ::lsp_types ::VersionedTextDocumentIdentifier {
uri : " file:///test2.kcl " . try_into ( ) . unwrap ( ) ,
version : 2 ,
} ,
content_changes : vec ! [ tower_lsp ::lsp_types ::TextDocumentContentChangeEvent {
range : None ,
range_length : None ,
text : " changed " . to_string ( ) ,
} ] ,
} )
. await ;
// Check the code map.
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 13 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test " . as_bytes ( )
) ;
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test2.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" changed " . as_bytes ( )
) ;
// Rename a file.
server
. did_rename_files ( tower_lsp ::lsp_types ::RenameFilesParams {
files : vec ! [ tower_lsp ::lsp_types ::FileRename {
old_uri : " file:///test2.kcl " . into ( ) ,
new_uri : " file:///test3.kcl " . into ( ) ,
} ] ,
} )
. await ;
// Check the code map.
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 13 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test " . as_bytes ( )
) ;
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test3.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" changed " . as_bytes ( )
) ;
// Create a file.
server
. did_create_files ( tower_lsp ::lsp_types ::CreateFilesParams {
files : vec ! [ tower_lsp ::lsp_types ::FileCreate {
uri : " file:///test4.kcl " . into ( ) ,
} ] ,
} )
. await ;
// Check the code map.
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 14 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test " . as_bytes ( )
) ;
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test3.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" changed " . as_bytes ( )
) ;
2024-06-27 15:43:49 -07:00
assert_eq! ( server . code_map . get ( " file:///test4.kcl " ) . unwrap ( ) . clone ( ) , " " . as_bytes ( ) ) ;
2024-03-12 23:57:43 -07:00
// Delete a file.
server
. did_delete_files ( tower_lsp ::lsp_types ::DeleteFilesParams {
files : vec ! [ tower_lsp ::lsp_types ::FileDelete {
uri : " file:///test4.kcl " . into ( ) ,
} ] ,
} )
. await ;
// Check the code map.
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 13 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test " . as_bytes ( )
) ;
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test3.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" changed " . as_bytes ( )
) ;
2024-04-15 17:18:32 -07:00
// If we are adding the same folder we already had we should not nuke the code_map.
2024-03-12 23:57:43 -07:00
server
. did_change_workspace_folders ( tower_lsp ::lsp_types ::DidChangeWorkspaceFoldersParams {
event : tower_lsp ::lsp_types ::WorkspaceFoldersChangeEvent {
added : vec ! [ tower_lsp ::lsp_types ::WorkspaceFolder {
uri : string_path . as_str ( ) . try_into ( ) . unwrap ( ) ,
name : " my-project " . to_string ( ) ,
} ] ,
removed : vec ! [ ] ,
} ,
} )
. await ;
// Get the workspace folders.
2024-06-27 15:43:49 -07:00
assert_eq! ( server . workspace_folders . len ( ) , 1 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . workspace_folders . get ( " my-project " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
tower_lsp ::lsp_types ::WorkspaceFolder {
uri : string_path . as_str ( ) . try_into ( ) . unwrap ( ) ,
2024-06-27 15:43:49 -07:00
name : " my-project " . to_string ( )
2024-03-12 23:57:43 -07:00
}
) ;
// Check the code map.
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 13 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test " . as_bytes ( )
) ;
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test3.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" changed " . as_bytes ( )
) ;
// If we change nothing it should not change the current code map.
server
. did_change_workspace_folders ( tower_lsp ::lsp_types ::DidChangeWorkspaceFoldersParams {
event : tower_lsp ::lsp_types ::WorkspaceFoldersChangeEvent {
added : vec ! [ ] ,
removed : vec ! [ ] ,
} ,
} )
. await ;
// Get the workspace folders.
2024-06-27 15:43:49 -07:00
assert_eq! ( server . workspace_folders . len ( ) , 1 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . workspace_folders . get ( " my-project " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
tower_lsp ::lsp_types ::WorkspaceFolder {
uri : string_path . as_str ( ) . try_into ( ) . unwrap ( ) ,
2024-06-27 15:43:49 -07:00
name : " my-project " . to_string ( )
2024-03-12 23:57:43 -07:00
}
) ;
// Check the code map.
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 13 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test " . as_bytes ( )
) ;
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test3.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" changed " . as_bytes ( )
) ;
// Remove folders.
// Run workspace folders change.
server
. did_change_workspace_folders ( tower_lsp ::lsp_types ::DidChangeWorkspaceFoldersParams {
event : tower_lsp ::lsp_types ::WorkspaceFoldersChangeEvent {
added : vec ! [ tower_lsp ::lsp_types ::WorkspaceFolder {
uri : string_path . as_str ( ) . try_into ( ) . unwrap ( ) ,
name : " my-project2 " . to_string ( ) ,
} ] ,
removed : vec ! [ tower_lsp ::lsp_types ::WorkspaceFolder {
uri : string_path . as_str ( ) . try_into ( ) . unwrap ( ) ,
name : " my-project " . to_string ( ) ,
} ] ,
} ,
} )
. await ;
// Get the workspace folders.
2024-06-27 15:43:49 -07:00
assert_eq! ( server . workspace_folders . len ( ) , 1 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . workspace_folders . get ( " my-project2 " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
tower_lsp ::lsp_types ::WorkspaceFolder {
uri : string_path . as_str ( ) . try_into ( ) . unwrap ( ) ,
2024-06-27 15:43:49 -07:00
name : " my-project2 " . to_string ( )
2024-03-12 23:57:43 -07:00
}
) ;
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 11 ) ;
2024-03-12 23:57:43 -07:00
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-03-12 23:57:43 -07:00
async fn test_kcl_lsp_create_zip ( ) {
2024-04-12 21:32:57 -07:00
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
2024-06-27 15:43:49 -07:00
assert_eq! ( server . code_map . len ( ) , 0 ) ;
2024-03-12 23:57:43 -07:00
// Get the path to the current file.
let path = std ::path ::Path ::new ( env! ( " CARGO_MANIFEST_DIR " ) ) . join ( " src " ) . join ( " lsp " ) ;
let string_path = format! ( " file:// {} " , path . display ( ) ) ;
// Run workspace folders change.
server
. did_change_workspace_folders ( tower_lsp ::lsp_types ::DidChangeWorkspaceFoldersParams {
event : tower_lsp ::lsp_types ::WorkspaceFoldersChangeEvent {
added : vec ! [ tower_lsp ::lsp_types ::WorkspaceFolder {
uri : string_path . as_str ( ) . try_into ( ) . unwrap ( ) ,
name : " my-project " . to_string ( ) ,
} ] ,
removed : vec ! [ ] ,
} ,
} )
. await ;
// Get the workspace folders.
2024-06-27 15:43:49 -07:00
assert_eq! ( server . workspace_folders . len ( ) , 1 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . workspace_folders . get ( " my-project " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
tower_lsp ::lsp_types ::WorkspaceFolder {
uri : string_path . as_str ( ) . try_into ( ) . unwrap ( ) ,
2024-06-27 15:43:49 -07:00
name : " my-project " . to_string ( )
2024-03-12 23:57:43 -07:00
}
) ;
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 11 ) ;
2024-03-12 23:57:43 -07:00
// Run 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 : " test " . to_string ( ) ,
} ,
} )
. await ;
// Check the code map.
2025-03-04 22:53:31 +13:00
assert_eq! ( server . code_map . len ( ) , 12 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" test " . as_bytes ( )
) ;
// Create a zip.
2024-04-15 17:18:32 -07:00
let bytes = server . create_zip ( ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
// Write the bytes to a tmp file.
let tmp_dir = std ::env ::temp_dir ( ) ;
let filename = format! ( " test- {} .zip " , chrono ::Utc ::now ( ) . timestamp ( ) ) ;
let tmp_file = tmp_dir . join ( filename ) ;
std ::fs ::write ( & tmp_file , bytes ) . unwrap ( ) ;
// Try to unzip the file.
let mut archive = zip ::ZipArchive ::new ( std ::fs ::File ::open ( & tmp_file ) . unwrap ( ) ) . unwrap ( ) ;
// Check the files in the zip.
let mut files = BTreeMap ::new ( ) ;
for i in 0 .. archive . len ( ) {
let file = archive . by_index ( i ) . unwrap ( ) ;
files . insert ( file . name ( ) . to_string ( ) , file . size ( ) ) ;
}
2025-03-04 22:53:31 +13:00
assert_eq! ( files . len ( ) , 12 ) ;
2024-03-12 23:57:43 -07:00
let util_path = format! ( " {} /util.rs " , string_path ) . replace ( " file:// " , " " ) ;
2024-05-02 15:13:00 -07:00
assert! ( files . contains_key ( & util_path ) ) ;
2024-03-12 23:57:43 -07:00
assert_eq! ( files . get ( " /test.kcl " ) , Some ( & 4 ) ) ;
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-03-12 23:57:43 -07:00
async fn test_kcl_lsp_completions ( ) {
2024-04-12 21:32:57 -07:00
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
// 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 ,
2025-03-05 16:23:46 +13:00
// Blank lines to check that we get completions even in an AST newline thing.
text : r #"
thing = 1
2024-03-12 23:57:43 -07:00
st " #
. to_string ( ) ,
} ,
} )
. await ;
// Send completion request.
let completions = server
. completion ( tower_lsp ::lsp_types ::CompletionParams {
text_document_position : tower_lsp ::lsp_types ::TextDocumentPositionParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
2025-03-05 16:23:46 +13:00
position : tower_lsp ::lsp_types ::Position { line : 0 , character : 0 } ,
2024-03-12 23:57:43 -07:00
} ,
context : None ,
partial_result_params : Default ::default ( ) ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( )
. unwrap ( ) ;
// Check the completions.
if let tower_lsp ::lsp_types ::CompletionResponse ::Array ( completions ) = completions {
assert! ( completions . len ( ) > 10 ) ;
2025-03-05 16:23:46 +13:00
assert! ( completions . iter ( ) . any ( | c | c . label = = " @settings " ) ) ;
2024-03-12 23:57:43 -07:00
} else {
panic! ( " Expected array of completions " ) ;
}
}
2024-06-29 18:10:07 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn test_kcl_lsp_completions_empty_in_comment ( ) {
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
2024-12-10 06:27:04 +13:00
text : r #" thing= 1 // st " #. to_string ( ) ,
2024-06-29 18:10:07 -07:00
} ,
} )
. await ;
// Send completion request.
let completions = server
. completion ( tower_lsp ::lsp_types ::CompletionParams {
text_document_position : tower_lsp ::lsp_types ::TextDocumentPositionParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
2024-12-10 06:27:04 +13:00
position : tower_lsp ::lsp_types ::Position { line : 0 , character : 13 } ,
2024-06-29 18:10:07 -07:00
} ,
context : None ,
partial_result_params : Default ::default ( ) ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( ) ;
assert! ( completions . is_none ( ) ) ;
}
2024-06-24 14:45:07 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn test_kcl_lsp_completions_tags ( ) {
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
2025-03-21 22:39:12 +13:00
text : r #" part001 = startSketchOn(XY)
2024-06-24 14:45:07 -07:00
| > startProfileAt ( [ 11.19 , 28.35 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 28.67 , - 13.25 ] , tag = $here )
| > line ( end = [ - 4.12 , - 22.81 ] )
| > line ( end = [ - 33.24 , 14.55 ] )
| > close ( )
| > extrude ( length = 5 ) " #
2024-06-24 14:45:07 -07:00
. to_string ( ) ,
} ,
} )
. await ;
// Send completion request.
let completions = server
. completion ( tower_lsp ::lsp_types ::CompletionParams {
text_document_position : tower_lsp ::lsp_types ::TextDocumentPositionParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
position : tower_lsp ::lsp_types ::Position {
line : 0 ,
character : 198 ,
} ,
} ,
context : None ,
partial_result_params : Default ::default ( ) ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( )
. unwrap ( ) ;
// Check the completions.
if let tower_lsp ::lsp_types ::CompletionResponse ::Array ( completions ) = completions {
assert! ( completions . len ( ) > 10 ) ;
// Make sure that `here` is in the completions.
let const_completion = completions
. iter ( )
. find ( | completion | completion . label = = " here " )
. unwrap ( ) ;
assert_eq! (
const_completion . kind ,
Some ( tower_lsp ::lsp_types ::CompletionItemKind ::REFERENCE )
) ;
} else {
panic! ( " Expected array of completions " ) ;
}
}
2024-04-22 14:53:49 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn test_kcl_lsp_completions_const_raw ( ) {
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
text : r #" con " #. to_string ( ) ,
} ,
} )
. await ;
// Send completion request.
let completions = server
. completion ( tower_lsp ::lsp_types ::CompletionParams {
text_document_position : tower_lsp ::lsp_types ::TextDocumentPositionParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
position : tower_lsp ::lsp_types ::Position { line : 0 , character : 2 } ,
} ,
context : None ,
partial_result_params : Default ::default ( ) ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( )
. unwrap ( ) ;
// Check the completions.
if let tower_lsp ::lsp_types ::CompletionResponse ::Array ( completions ) = completions {
assert! ( completions . len ( ) > 10 ) ;
2025-02-20 19:33:21 +13:00
// Find the one with label "fn".
let const_completion = completions . iter ( ) . find ( | completion | completion . label = = " fn " ) . unwrap ( ) ;
2024-04-22 14:53:49 -07:00
assert_eq! (
const_completion . kind ,
Some ( tower_lsp ::lsp_types ::CompletionItemKind ::KEYWORD )
) ;
} else {
panic! ( " Expected array of completions " ) ;
}
}
2024-12-17 10:48:38 +13:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn test_kcl_lsp_completions_import ( ) {
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
text : r #" import boo, baz as bux from 'bar.kcl'
//import 'bar.kcl'
x = b " #
. to_string ( ) ,
} ,
} )
. await ;
// Send completion request.
let completions = server
. completion ( tower_lsp ::lsp_types ::CompletionParams {
text_document_position : tower_lsp ::lsp_types ::TextDocumentPositionParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
position : tower_lsp ::lsp_types ::Position { line : 2 , character : 5 } ,
} ,
context : None ,
partial_result_params : Default ::default ( ) ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( )
. unwrap ( ) ;
// Check the completions.
if let tower_lsp ::lsp_types ::CompletionResponse ::Array ( completions ) = completions {
assert! ( completions . len ( ) > 10 ) ;
// Find the one with label "foo".
completions . iter ( ) . find ( | completion | completion . label = = " boo " ) . unwrap ( ) ;
// completions
// .iter()
// .find(|completion| completion.label == "bar")
// .unwrap();
completions . iter ( ) . find ( | completion | completion . label = = " bux " ) . unwrap ( ) ;
assert! ( ! completions . iter ( ) . any ( | completion | completion . label = = " baz " ) ) ;
// Find the one with label "bar".
} else {
panic! ( " Expected array of completions " ) ;
}
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-03-12 23:57:43 -07:00
async fn test_kcl_lsp_on_hover ( ) {
2025-03-04 22:53:31 +13:00
let server = kcl_lsp_server ( true ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
// 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 ,
2025-03-04 22:53:31 +13:00
text : r #" startSketchOn(XY)
foo = 42
foo
fn bar ( x : string ) : string {
return x
}
bar ( " an arg " )
startSketchOn ( XY )
| > startProfileAt ( [ 0 , 0 ] , % )
| > line ( end = [ 10 , 0 ] )
| > line ( end = [ 0 , 10 ] )
" #
. to_string ( ) ,
2024-03-12 23:57:43 -07:00
} ,
} )
. await ;
2025-03-04 22:53:31 +13:00
// Std lib call
2024-03-12 23:57:43 -07:00
let hover = server
. hover ( tower_lsp ::lsp_types ::HoverParams {
text_document_position_params : tower_lsp ::lsp_types ::TextDocumentPositionParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
position : tower_lsp ::lsp_types ::Position { line : 0 , character : 2 } ,
} ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( ) ;
2025-02-27 22:03:37 +13:00
match hover . unwrap ( ) . contents {
tower_lsp ::lsp_types ::HoverContents ::Markup ( tower_lsp ::lsp_types ::MarkupContent { value , .. } ) = > {
2025-03-04 22:53:31 +13:00
assert! ( value . contains ( " startSketchOn " ) ) ;
assert! ( value . contains ( " : SketchSurface " ) ) ;
assert! ( value . contains ( " Start a new 2-dimensional sketch on a specific " ) ) ;
}
_ = > unreachable! ( ) ,
}
// Variable use
let hover = server
. hover ( tower_lsp ::lsp_types ::HoverParams {
text_document_position_params : tower_lsp ::lsp_types ::TextDocumentPositionParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
position : tower_lsp ::lsp_types ::Position { line : 2 , character : 1 } ,
} ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( ) ;
match hover . unwrap ( ) . contents {
tower_lsp ::lsp_types ::HoverContents ::Markup ( tower_lsp ::lsp_types ::MarkupContent { value , .. } ) = > {
assert! ( value . contains ( " foo: number = 42 " ) ) ;
}
_ = > unreachable! ( ) ,
}
// User-defined function call.
let hover = server
. hover ( tower_lsp ::lsp_types ::HoverParams {
text_document_position_params : tower_lsp ::lsp_types ::TextDocumentPositionParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
position : tower_lsp ::lsp_types ::Position { line : 8 , character : 1 } ,
} ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( ) ;
match hover . unwrap ( ) . contents {
tower_lsp ::lsp_types ::HoverContents ::Markup ( tower_lsp ::lsp_types ::MarkupContent { value , .. } ) = > {
assert! ( value . contains ( " bar(x: string): string " ) ) ;
}
_ = > unreachable! ( ) ,
}
// Variable inside a function
let hover = server
. hover ( tower_lsp ::lsp_types ::HoverParams {
text_document_position_params : tower_lsp ::lsp_types ::TextDocumentPositionParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
position : tower_lsp ::lsp_types ::Position { line : 5 , character : 9 } ,
} ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( ) ;
match hover . unwrap ( ) . contents {
tower_lsp ::lsp_types ::HoverContents ::Markup ( tower_lsp ::lsp_types ::MarkupContent { value , .. } ) = > {
assert! ( value . contains ( " x: string " ) ) ;
}
_ = > unreachable! ( ) ,
}
// std function KwArg
let hover = server
. hover ( tower_lsp ::lsp_types ::HoverParams {
text_document_position_params : tower_lsp ::lsp_types ::TextDocumentPositionParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
position : tower_lsp ::lsp_types ::Position {
line : 12 ,
character : 11 ,
} ,
} ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( ) ;
match hover . unwrap ( ) . contents {
tower_lsp ::lsp_types ::HoverContents ::Markup ( tower_lsp ::lsp_types ::MarkupContent { value , .. } ) = > {
assert! ( value . contains ( " end?: [number] " ) ) ;
assert! ( value . contains ( " How far away (along the X and Y axes) should this line go? " ) ) ;
2025-02-27 22:03:37 +13:00
}
_ = > unreachable! ( ) ,
2024-03-12 23:57:43 -07:00
}
}
2024-05-02 16:31:33 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn test_kcl_lsp_on_hover_shebang ( ) {
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
text : r #" #!/usr/bin/env zoo kcl view
startSketchOn ( ) " #
. to_string ( ) ,
} ,
} )
. await ;
// Send hover request.
let hover = server
. hover ( tower_lsp ::lsp_types ::HoverParams {
text_document_position_params : tower_lsp ::lsp_types ::TextDocumentPositionParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
position : tower_lsp ::lsp_types ::Position { line : 0 , character : 2 } ,
} ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( ) ;
// Check the hover.
if let Some ( hover ) = hover {
assert_eq! (
hover . contents ,
tower_lsp ::lsp_types ::HoverContents ::Markup ( tower_lsp ::lsp_types ::MarkupContent {
kind : tower_lsp ::lsp_types ::MarkupKind ::Markdown ,
value : " The `#!` at the start of a script, known as a shebang, specifies the path to the interpreter that should execute the script. This line is not necessary for your `kcl` to run in the modeling-app. You can safely delete it. If you wish to learn more about what you _can_ do with a shebang, read this doc: [zoo.dev/docs/faq/shebang](https://zoo.dev/docs/faq/shebang). " . to_string ( )
} )
) ;
} else {
panic! ( " Expected hover " ) ;
}
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-03-12 23:57:43 -07:00
async fn test_kcl_lsp_signature_help ( ) {
2024-04-12 21:32:57 -07:00
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
// Send open file.
server
. did_change ( tower_lsp ::lsp_types ::DidChangeTextDocumentParams {
text_document : tower_lsp ::lsp_types ::VersionedTextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
version : 1 ,
} ,
content_changes : vec ! [ tower_lsp ::lsp_types ::TextDocumentContentChangeEvent {
range : None ,
range_length : None ,
2025-03-21 22:39:12 +13:00
text : " startSketchOn(XY) " . to_string ( ) ,
2024-03-12 23:57:43 -07:00
} ] ,
} )
. await ;
// Send signature help request.
let signature_help = server
. signature_help ( tower_lsp ::lsp_types ::SignatureHelpParams {
text_document_position_params : tower_lsp ::lsp_types ::TextDocumentPositionParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
position : tower_lsp ::lsp_types ::Position { line : 0 , character : 2 } ,
} ,
context : None ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( ) ;
// Check the signature help.
if let Some ( signature_help ) = signature_help {
assert_eq! (
signature_help . signatures . len ( ) ,
1 ,
" Expected one signature, got {:?} " ,
signature_help . signatures
) ;
assert_eq! ( signature_help . signatures [ 0 ] . label , " startSketchOn " ) ;
} else {
panic! ( " Expected signature help " ) ;
}
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-03-12 23:57:43 -07:00
async fn test_kcl_lsp_semantic_tokens ( ) {
2024-04-12 21:32:57 -07:00
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
// 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 ,
2025-03-21 22:39:12 +13:00
text : " startSketchOn(XY) " . to_string ( ) ,
2024-03-12 23:57:43 -07:00
} ,
} )
. await ;
// Send semantic tokens request.
let semantic_tokens = server
. semantic_tokens_full ( tower_lsp ::lsp_types ::SemanticTokensParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
partial_result_params : Default ::default ( ) ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( )
. unwrap ( ) ;
// Check the semantic tokens.
if let tower_lsp ::lsp_types ::SemanticTokensResult ::Tokens ( semantic_tokens ) = semantic_tokens {
assert_eq! ( semantic_tokens . data . len ( ) , 2 ) ;
assert_eq! ( semantic_tokens . data [ 0 ] . length , 13 ) ;
2024-05-10 15:30:40 -07:00
assert_eq! ( semantic_tokens . data [ 0 ] . delta_start , 0 ) ;
assert_eq! ( semantic_tokens . data [ 0 ] . delta_line , 0 ) ;
2024-08-14 02:38:37 -04:00
assert_eq! (
semantic_tokens . data [ 0 ] . token_type ,
server
. get_semantic_token_type_index ( & SemanticTokenType ::FUNCTION )
. unwrap ( )
) ;
2025-03-21 22:39:12 +13:00
assert_eq! ( semantic_tokens . data [ 1 ] . length , 2 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! ( semantic_tokens . data [ 1 ] . delta_start , 14 ) ;
2024-05-10 15:30:40 -07:00
assert_eq! ( semantic_tokens . data [ 1 ] . delta_line , 0 ) ;
2024-08-14 02:38:37 -04:00
assert_eq! (
semantic_tokens . data [ 1 ] . token_type ,
server
2025-03-21 22:39:12 +13:00
. get_semantic_token_type_index ( & SemanticTokenType ::VARIABLE )
2024-08-14 02:38:37 -04:00
. unwrap ( )
) ;
2024-03-12 23:57:43 -07:00
} else {
panic! ( " Expected semantic tokens " ) ;
}
}
2024-06-26 14:51:47 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn test_kcl_lsp_semantic_tokens_large_file ( ) {
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
2025-03-01 13:59:01 -08:00
let code = include_str! ( " ../../e2e/executor/inputs/global-tags.kcl " ) ;
2024-06-26 14:51:47 -07:00
// 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 ;
// Send semantic tokens request.
let semantic_tokens = server
. semantic_tokens_full ( tower_lsp ::lsp_types ::SemanticTokensParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
partial_result_params : Default ::default ( ) ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( )
. unwrap ( ) ;
// Check the semantic tokens.
if let tower_lsp ::lsp_types ::SemanticTokensResult ::Tokens ( semantic_tokens ) = semantic_tokens {
assert! ( ! semantic_tokens . data . is_empty ( ) ) ;
} else {
panic! ( " Expected semantic tokens " ) ;
}
}
2024-06-26 05:02:36 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn test_kcl_lsp_semantic_tokens_with_modifiers ( ) {
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
2025-03-21 22:39:12 +13:00
text : r #" part001 = startSketchOn(XY)
2024-06-26 05:02:36 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] , tag = $seg01 )
| > line ( end = [ - 20 , 0 ] )
| > close ( )
| > extrude ( length = 3.14 )
2024-06-26 05:02:36 -07:00
2024-12-10 06:27:04 +13:00
thing = { blah : " foo " }
bar = thing . blah
2024-06-26 05:02:36 -07:00
fn myFn = ( param1 ) = > {
return param1
} " #
. to_string ( ) ,
} ,
} )
. await ;
2024-12-06 13:57:31 +13:00
// Assure we have no errors.
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 0 ) ;
2024-06-26 05:02:36 -07:00
// Get the token map.
2024-06-27 15:43:49 -07:00
let token_map = server . token_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2024-12-10 14:26:53 +13:00
assert! ( ! token_map . is_empty ( ) ) ;
2024-06-26 05:02:36 -07:00
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-06-26 05:02:36 -07:00
// Send semantic tokens request.
let semantic_tokens = server
. semantic_tokens_full ( tower_lsp ::lsp_types ::SemanticTokensParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
partial_result_params : Default ::default ( ) ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( )
. unwrap ( ) ;
// Check the semantic tokens.
if let tower_lsp ::lsp_types ::SemanticTokensResult ::Tokens ( semantic_tokens ) = semantic_tokens {
let function_index = server
2024-06-26 14:51:47 -07:00
. get_semantic_token_type_index ( & SemanticTokenType ::FUNCTION )
2024-06-26 05:02:36 -07:00
. unwrap ( ) ;
let property_index = server
2024-06-26 14:51:47 -07:00
. get_semantic_token_type_index ( & SemanticTokenType ::PROPERTY )
2024-06-26 05:02:36 -07:00
. unwrap ( ) ;
let parameter_index = server
2024-06-26 14:51:47 -07:00
. get_semantic_token_type_index ( & SemanticTokenType ::PARAMETER )
2024-06-26 05:02:36 -07:00
. unwrap ( ) ;
let variable_index = server
2024-06-26 14:51:47 -07:00
. get_semantic_token_type_index ( & SemanticTokenType ::VARIABLE )
2024-06-26 05:02:36 -07:00
. unwrap ( ) ;
let declaration_index = server
2024-06-26 14:51:47 -07:00
. get_semantic_token_modifier_index ( vec! [ SemanticTokenModifier ::DECLARATION ] )
2024-06-26 05:02:36 -07:00
. unwrap ( ) ;
let definition_index = server
2024-06-26 14:51:47 -07:00
. get_semantic_token_modifier_index ( vec! [ SemanticTokenModifier ::DEFINITION ] )
. unwrap ( ) ;
let default_library_index = server
. get_semantic_token_modifier_index ( vec! [ SemanticTokenModifier ::DEFAULT_LIBRARY ] )
. unwrap ( ) ;
let variable_modifiers = server
. get_semantic_token_modifier_index ( vec! [
SemanticTokenModifier ::DECLARATION ,
SemanticTokenModifier ::READONLY ,
] )
. unwrap ( ) ;
let tag_modifiers = server
. get_semantic_token_modifier_index ( vec! [ SemanticTokenModifier ::DEFINITION , SemanticTokenModifier ::STATIC ] )
2024-06-26 05:02:36 -07:00
. unwrap ( ) ;
// Iterate over the tokens and check the token types.
let mut found_parameter = false ;
let mut found_property = false ;
let mut found_function_declaration = false ;
let mut found_variable_declaration = false ;
let mut found_property_declaration = false ;
2024-06-26 14:51:47 -07:00
let mut found_tag_declaration = false ;
let mut found_default_library = false ;
2024-06-26 05:02:36 -07:00
for token in semantic_tokens . data {
2024-06-26 14:51:47 -07:00
if token . token_type = = function_index & & token . token_modifiers_bitset = = default_library_index {
found_default_library = true ;
2024-06-26 05:02:36 -07:00
}
if token . token_type = = parameter_index {
found_parameter = true ;
} else if token . token_type = = property_index {
found_property = true ;
}
2024-06-26 14:51:47 -07:00
if token . token_type = = definition_index & & token . token_modifiers_bitset = = tag_modifiers {
found_tag_declaration = true ;
}
if token . token_type = = function_index & & token . token_modifiers_bitset = = variable_modifiers {
2024-06-26 05:02:36 -07:00
found_function_declaration = true ;
}
2024-06-26 14:51:47 -07:00
if token . token_type = = variable_index & & token . token_modifiers_bitset = = variable_modifiers {
2024-06-26 05:02:36 -07:00
found_variable_declaration = true ;
}
if token . token_type = = property_index & & token . token_modifiers_bitset = = declaration_index {
found_property_declaration = true ;
}
2024-06-26 14:51:47 -07:00
if found_parameter
2024-06-26 05:02:36 -07:00
& & found_property
& & found_function_declaration
& & found_variable_declaration
& & found_property_declaration
2024-06-26 14:51:47 -07:00
& & found_tag_declaration
& & found_default_library
2024-06-26 05:02:36 -07:00
{
break ;
}
}
if ! found_parameter {
panic! ( " Expected parameter token " ) ;
}
if ! found_property {
panic! ( " Expected property token " ) ;
}
if ! found_function_declaration {
panic! ( " Expected function declaration token " ) ;
}
if ! found_variable_declaration {
panic! ( " Expected variable declaration token " ) ;
}
if ! found_property_declaration {
panic! ( " Expected property declaration token " ) ;
}
2024-06-26 14:51:47 -07:00
if ! found_tag_declaration {
panic! ( " Expected tag declaration token " ) ;
}
if ! found_default_library {
panic! ( " Expected default library token " ) ;
}
2024-06-26 05:02:36 -07:00
} else {
panic! ( " Expected semantic tokens " ) ;
}
}
2024-05-10 15:30:40 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn test_kcl_lsp_semantic_tokens_multiple_comments ( ) {
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
text : r #" // Ball Bearing
// A ball bearing is a type of rolling-element bearing that uses balls to maintain the separation between the bearing races. The primary purpose of a ball bearing is to reduce rotational friction and support radial and axial loads.
// Define constants like ball diameter, inside diameter, overhange length, and thickness
2024-12-10 06:27:04 +13:00
sphereDia = 0.5 " #
2024-06-27 15:43:49 -07:00
. to_string ( ) ,
2024-05-10 15:30:40 -07:00
} ,
} )
. await ;
// Send semantic tokens request.
let semantic_tokens = server
. semantic_tokens_full ( tower_lsp ::lsp_types ::SemanticTokensParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
partial_result_params : Default ::default ( ) ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( )
. unwrap ( ) ;
// Check the semantic tokens.
if let tower_lsp ::lsp_types ::SemanticTokensResult ::Tokens ( semantic_tokens ) = semantic_tokens {
2024-12-10 06:27:04 +13:00
assert_eq! ( semantic_tokens . data . len ( ) , 6 ) ;
2024-05-10 15:30:40 -07:00
assert_eq! ( semantic_tokens . data [ 0 ] . length , 15 ) ;
assert_eq! ( semantic_tokens . data [ 0 ] . delta_start , 0 ) ;
assert_eq! ( semantic_tokens . data [ 0 ] . delta_line , 0 ) ;
2024-08-14 02:38:37 -04:00
assert_eq! (
semantic_tokens . data [ 0 ] . token_type ,
server
. get_semantic_token_type_index ( & SemanticTokenType ::COMMENT )
. unwrap ( )
) ;
2024-05-10 15:30:40 -07:00
assert_eq! ( semantic_tokens . data [ 1 ] . length , 232 ) ;
assert_eq! ( semantic_tokens . data [ 1 ] . delta_start , 0 ) ;
assert_eq! ( semantic_tokens . data [ 1 ] . delta_line , 1 ) ;
2024-08-14 02:38:37 -04:00
assert_eq! (
semantic_tokens . data [ 1 ] . token_type ,
server
. get_semantic_token_type_index ( & SemanticTokenType ::COMMENT )
. unwrap ( )
) ;
2024-05-10 15:30:40 -07:00
assert_eq! ( semantic_tokens . data [ 2 ] . length , 88 ) ;
assert_eq! ( semantic_tokens . data [ 2 ] . delta_start , 0 ) ;
assert_eq! ( semantic_tokens . data [ 2 ] . delta_line , 2 ) ;
2024-08-14 02:38:37 -04:00
assert_eq! (
semantic_tokens . data [ 2 ] . token_type ,
server
. get_semantic_token_type_index ( & SemanticTokenType ::COMMENT )
. unwrap ( )
) ;
2024-12-10 06:27:04 +13:00
assert_eq! ( semantic_tokens . data [ 3 ] . length , 9 ) ;
2024-05-10 15:30:40 -07:00
assert_eq! ( semantic_tokens . data [ 3 ] . delta_start , 0 ) ;
assert_eq! ( semantic_tokens . data [ 3 ] . delta_line , 1 ) ;
2024-08-14 02:38:37 -04:00
assert_eq! (
semantic_tokens . data [ 3 ] . token_type ,
server
. get_semantic_token_type_index ( & SemanticTokenType ::VARIABLE )
. unwrap ( )
) ;
2024-12-10 06:27:04 +13:00
assert_eq! ( semantic_tokens . data [ 4 ] . length , 1 ) ;
assert_eq! ( semantic_tokens . data [ 4 ] . delta_start , 10 ) ;
2024-08-14 02:38:37 -04:00
assert_eq! (
2024-12-10 06:27:04 +13:00
semantic_tokens . data [ 4 ] . token_type ,
2024-08-14 02:38:37 -04:00
server
. get_semantic_token_type_index ( & SemanticTokenType ::OPERATOR )
. unwrap ( )
) ;
2024-12-10 06:27:04 +13:00
assert_eq! ( semantic_tokens . data [ 5 ] . length , 3 ) ;
assert_eq! ( semantic_tokens . data [ 5 ] . delta_start , 2 ) ;
2024-08-14 02:38:37 -04:00
assert_eq! (
2024-12-10 06:27:04 +13:00
semantic_tokens . data [ 5 ] . token_type ,
2024-08-14 02:38:37 -04:00
server
. get_semantic_token_type_index ( & SemanticTokenType ::NUMBER )
. unwrap ( )
) ;
2024-05-10 15:30:40 -07:00
} else {
panic! ( " Expected semantic tokens " ) ;
}
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-03-12 23:57:43 -07:00
async fn test_kcl_lsp_document_symbol ( ) {
2024-04-12 21:32:57 -07:00
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
// 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 ,
2024-12-10 06:27:04 +13:00
text : r #" myVar = 1
2025-03-21 22:39:12 +13:00
startSketchOn ( XY ) " #
2024-03-12 23:57:43 -07:00
. to_string ( ) ,
} ,
} )
. await ;
// Send document symbol request.
let document_symbol = server
. document_symbol ( tower_lsp ::lsp_types ::DocumentSymbolParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
work_done_progress_params : Default ::default ( ) ,
partial_result_params : Default ::default ( ) ,
} )
. await
. unwrap ( )
. unwrap ( ) ;
// Check the document symbol.
if let tower_lsp ::lsp_types ::DocumentSymbolResponse ::Nested ( document_symbol ) = document_symbol {
assert_eq! ( document_symbol . len ( ) , 1 ) ;
assert_eq! ( document_symbol [ 0 ] . name , " myVar " ) ;
} else {
panic! ( " Expected document symbol " ) ;
}
}
2024-06-24 14:45:07 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn test_kcl_lsp_document_symbol_tag ( ) {
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
2025-03-21 22:39:12 +13:00
text : r #" part001 = startSketchOn(XY)
2024-06-24 14:45:07 -07:00
| > startProfileAt ( [ 11.19 , 28.35 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 28.67 , - 13.25 ] , tag = $here )
| > line ( end = [ - 4.12 , - 22.81 ] )
| > line ( end = [ - 33.24 , 14.55 ] )
| > close ( )
| > extrude ( length = 5 ) " #
2024-06-24 14:45:07 -07:00
. to_string ( ) ,
} ,
} )
. await ;
// Send document symbol request.
let document_symbol = server
. document_symbol ( tower_lsp ::lsp_types ::DocumentSymbolParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
work_done_progress_params : Default ::default ( ) ,
partial_result_params : Default ::default ( ) ,
} )
. await
. unwrap ( )
. unwrap ( ) ;
// Check the document symbol.
if let tower_lsp ::lsp_types ::DocumentSymbolResponse ::Nested ( document_symbol ) = document_symbol {
assert_eq! ( document_symbol . len ( ) , 2 ) ;
assert_eq! ( document_symbol [ 0 ] . name , " part001 " ) ;
assert_eq! ( document_symbol [ 1 ] . name , " here " ) ;
} else {
panic! ( " Expected document symbol " ) ;
}
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-03-12 23:57:43 -07:00
async fn test_kcl_lsp_formatting ( ) {
2024-04-12 21:32:57 -07:00
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
// 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 ,
2025-03-21 22:39:12 +13:00
text : r #" startSketchOn(XY)
2024-03-12 23:57:43 -07:00
| > startProfileAt ( [ 0 , 0 ] , % ) " #
. to_string ( ) ,
} ,
} )
. await ;
// Send formatting request.
let formatting = server
. formatting ( tower_lsp ::lsp_types ::DocumentFormattingParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
options : tower_lsp ::lsp_types ::FormattingOptions {
tab_size : 4 ,
insert_spaces : true ,
properties : Default ::default ( ) ,
trim_trailing_whitespace : None ,
insert_final_newline : None ,
trim_final_newlines : None ,
} ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( )
. unwrap ( ) ;
// Check the formatting.
assert_eq! ( formatting . len ( ) , 1 ) ;
assert_eq! (
formatting [ 0 ] . new_text ,
2025-03-21 22:39:12 +13:00
r #" startSketchOn(XY)
2024-03-12 23:57:43 -07:00
| > startProfileAt ( [ 0 , 0 ] , % ) " #
) ;
}
2024-05-10 15:30:40 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn test_kcl_lsp_formatting_extra_parens ( ) {
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
text : r #" // Ball Bearing
// A ball bearing is a type of rolling-element bearing that uses balls to maintain the separation between the bearing races. The primary purpose of a ball bearing is to reduce rotational friction and support radial and axial loads.
// Define constants like ball diameter, inside diameter, overhange length, and thickness
2024-12-10 06:27:04 +13:00
sphereDia = 0.5
insideDia = 1
thickness = 0.25
overHangLength = . 4
2024-05-10 15:30:40 -07:00
// Sketch and revolve the inside bearing piece
2025-03-21 22:39:12 +13:00
insideRevolve = startSketchOn ( XZ )
2024-05-10 15:30:40 -07:00
| > startProfileAt ( [ insideDia / 2 , 0 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 0 , thickness + sphereDia / 2 ] )
| > line ( end = [ overHangLength , 0 ] )
| > line ( end = [ 0 , - thickness ] )
| > line ( end = [ - overHangLength + thickness , 0 ] )
| > line ( end = [ 0 , - sphereDia ] )
| > line ( end = [ overHangLength - thickness , 0 ] )
| > line ( end = [ 0 , - thickness ] )
| > line ( end = [ - overHangLength , 0 ] )
| > close ( )
2024-05-10 15:30:40 -07:00
| > revolve ( { axis : ' y ' } , % )
// Sketch and revolve one of the balls and duplicate it using a circular pattern. (This is currently a workaround, we have a bug with rotating on a sketch that touches the rotation axis)
2025-03-21 22:39:12 +13:00
sphere = startSketchOn ( XZ )
2024-05-10 15:30:40 -07:00
| > startProfileAt ( [
0.05 + insideDia / 2 + thickness ,
0 - 0.05
] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ sphereDia - 0.1 , 0 ] )
2024-05-10 15:30:40 -07:00
| > arc ( {
angle_start : 0 ,
angle_end : - 180 ,
radius : sphereDia / 2 - 0.05
} , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2024-05-10 15:30:40 -07:00
| > revolve ( { axis : ' x ' } , % )
2025-02-11 16:06:47 -06:00
| > patternCircular3d (
axis = [ 0 , 0 , 1 ] ,
center = [ 0 , 0 , 0 ] ,
repetitions = 10 ,
arcDegrees = 360 ,
rotateDuplicates = true ,
)
2024-05-10 15:30:40 -07:00
// Sketch and revolve the outside bearing
2025-03-21 22:39:12 +13:00
outsideRevolve = startSketchOn ( XZ )
2024-05-10 15:30:40 -07:00
| > startProfileAt ( [
insideDia / 2 + thickness + sphereDia ,
0
] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 0 , sphereDia / 2 ] )
| > line ( end = [ - overHangLength + thickness , 0 ] )
| > line ( end = [ 0 , thickness ] )
| > line ( end = [ overHangLength , 0 ] )
| > line ( end = [ 0 , - 2 * thickness - sphereDia ] )
| > line ( end = [ - overHangLength , 0 ] )
| > line ( end = [ 0 , thickness ] )
| > line ( end = [ overHangLength - thickness , 0 ] )
| > close ( )
2024-05-10 15:30:40 -07:00
| > revolve ( { axis : ' y ' } , % ) " #
. to_string ( ) ,
} ,
} )
. await ;
// Send formatting request.
let formatting = server
. formatting ( tower_lsp ::lsp_types ::DocumentFormattingParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
options : tower_lsp ::lsp_types ::FormattingOptions {
tab_size : 2 ,
insert_spaces : true ,
properties : Default ::default ( ) ,
trim_trailing_whitespace : None ,
insert_final_newline : None ,
trim_final_newlines : None ,
} ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( )
. unwrap ( ) ;
// Check the formatting.
assert_eq! ( formatting . len ( ) , 1 ) ;
assert_eq! (
formatting [ 0 ] . range ,
tower_lsp ::lsp_types ::Range {
start : tower_lsp ::lsp_types ::Position { line : 0 , character : 0 } ,
end : tower_lsp ::lsp_types ::Position {
line : 60 ,
character : 30
}
}
) ;
assert_eq! (
formatting [ 0 ] . new_text ,
r #" // Ball Bearing
// A ball bearing is a type of rolling-element bearing that uses balls to maintain the separation between the bearing races. The primary purpose of a ball bearing is to reduce rotational friction and support radial and axial loads.
// Define constants like ball diameter, inside diameter, overhange length, and thickness
2024-10-02 14:19:40 -05:00
sphereDia = 0.5
insideDia = 1
thickness = 0.25
overHangLength = . 4
2024-05-10 15:30:40 -07:00
// Sketch and revolve the inside bearing piece
2025-03-21 22:39:12 +13:00
insideRevolve = startSketchOn ( XZ )
2024-05-10 15:30:40 -07:00
| > startProfileAt ( [ insideDia / 2 , 0 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 0 , thickness + sphereDia / 2 ] )
| > line ( end = [ overHangLength , 0 ] )
| > line ( end = [ 0 , - thickness ] )
| > line ( end = [ - overHangLength + thickness , 0 ] )
| > line ( end = [ 0 , - sphereDia ] )
| > line ( end = [ overHangLength - thickness , 0 ] )
| > line ( end = [ 0 , - thickness ] )
| > line ( end = [ - overHangLength , 0 ] )
| > close ( )
2024-11-25 09:21:55 +13:00
| > revolve ( { axis = 'y' } , % )
2024-05-10 15:30:40 -07:00
// Sketch and revolve one of the balls and duplicate it using a circular pattern. (This is currently a workaround, we have a bug with rotating on a sketch that touches the rotation axis)
2025-03-21 22:39:12 +13:00
sphere = startSketchOn ( XZ )
2024-05-10 15:30:40 -07:00
| > startProfileAt ( [
0.05 + insideDia / 2 + thickness ,
0 - 0.05
] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ sphereDia - 0.1 , 0 ] )
2024-05-10 15:30:40 -07:00
| > arc ( {
2024-11-25 09:21:55 +13:00
angle_start = 0 ,
angle_end = - 180 ,
radius = sphereDia / 2 - 0.05
2024-05-10 15:30:40 -07:00
} , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
2024-11-25 09:21:55 +13:00
| > revolve ( { axis = 'x' } , % )
2025-02-11 16:06:47 -06:00
| > patternCircular3d (
2024-11-25 09:21:55 +13:00
axis = [ 0 , 0 , 1 ] ,
center = [ 0 , 0 , 0 ] ,
repetitions = 10 ,
arcDegrees = 360 ,
2025-02-11 16:06:47 -06:00
rotateDuplicates = true ,
)
2024-05-10 15:30:40 -07:00
// Sketch and revolve the outside bearing
2025-03-21 22:39:12 +13:00
outsideRevolve = startSketchOn ( XZ )
2024-05-10 15:30:40 -07:00
| > startProfileAt ( [
insideDia / 2 + thickness + sphereDia ,
0
] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 0 , sphereDia / 2 ] )
| > line ( end = [ - overHangLength + thickness , 0 ] )
| > line ( end = [ 0 , thickness ] )
| > line ( end = [ overHangLength , 0 ] )
| > line ( end = [ 0 , - 2 * thickness - sphereDia ] )
| > line ( end = [ - overHangLength , 0 ] )
| > line ( end = [ 0 , thickness ] )
| > line ( end = [ overHangLength - thickness , 0 ] )
| > close ( )
2024-11-25 09:21:55 +13:00
| > revolve ( { axis = 'y' } , % ) " #
2024-05-10 15:30:40 -07:00
) ;
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-03-12 23:57:43 -07:00
async fn test_kcl_lsp_rename ( ) {
2024-04-12 21:32:57 -07:00
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
// 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 ,
2024-12-10 06:27:04 +13:00
text : r #" thing= 1 " #. to_string ( ) ,
2024-03-12 23:57:43 -07:00
} ,
} )
. await ;
// Send rename request.
let rename = server
. rename ( tower_lsp ::lsp_types ::RenameParams {
text_document_position : tower_lsp ::lsp_types ::TextDocumentPositionParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
2024-12-10 06:27:04 +13:00
position : tower_lsp ::lsp_types ::Position { line : 0 , character : 2 } ,
2024-03-12 23:57:43 -07:00
} ,
new_name : " newName " . to_string ( ) ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( )
. unwrap ( ) ;
// Check the rename.
let changes = rename . changes . unwrap ( ) ;
let u : tower_lsp ::lsp_types ::Url = " file:///test.kcl " . try_into ( ) . unwrap ( ) ;
assert_eq! (
changes . get ( & u ) . unwrap ( ) . clone ( ) ,
vec! [ tower_lsp ::lsp_types ::TextEdit {
range : tower_lsp ::lsp_types ::Range {
start : tower_lsp ::lsp_types ::Position { line : 0 , character : 0 } ,
2024-12-10 06:27:04 +13:00
end : tower_lsp ::lsp_types ::Position { line : 0 , character : 7 }
2024-03-12 23:57:43 -07:00
} ,
2024-10-02 14:19:40 -05:00
new_text : " newName = 1 \n " . to_string ( )
2024-03-12 23:57:43 -07:00
} ]
) ;
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-03-12 23:57:43 -07:00
async fn test_kcl_lsp_diagnostic_no_errors ( ) {
2024-04-12 21:32:57 -07:00
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
// 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 ,
2024-10-02 14:19:40 -05:00
text : r #" thing= 1 " #. to_string ( ) ,
2024-03-12 23:57:43 -07:00
} ,
} )
. await ;
// Send diagnostics request.
let diagnostics = server
. diagnostic ( tower_lsp ::lsp_types ::DocumentDiagnosticParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
partial_result_params : Default ::default ( ) ,
work_done_progress_params : Default ::default ( ) ,
identifier : None ,
previous_result_id : None ,
} )
. await
. unwrap ( ) ;
// Check the diagnostics.
if let tower_lsp ::lsp_types ::DocumentDiagnosticReportResult ::Report ( diagnostics ) = diagnostics {
if let tower_lsp ::lsp_types ::DocumentDiagnosticReport ::Full ( diagnostics ) = diagnostics {
assert_eq! ( diagnostics . full_document_diagnostic_report . items . len ( ) , 0 ) ;
} else {
panic! ( " Expected full diagnostics " ) ;
}
} else {
panic! ( " Expected diagnostics " ) ;
}
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-03-12 23:57:43 -07:00
async fn test_kcl_lsp_diagnostic_has_errors ( ) {
2024-04-12 21:32:57 -07:00
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
text : r #" k;ajsndasd thing= 1 " #. to_string ( ) ,
} ,
} )
. await ;
// Send diagnostics request.
let diagnostics = server
. diagnostic ( tower_lsp ::lsp_types ::DocumentDiagnosticParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
partial_result_params : Default ::default ( ) ,
work_done_progress_params : Default ::default ( ) ,
identifier : None ,
previous_result_id : None ,
} )
. await
. unwrap ( ) ;
// Check the diagnostics.
if let tower_lsp ::lsp_types ::DocumentDiagnosticReportResult ::Report ( diagnostics ) = diagnostics {
if let tower_lsp ::lsp_types ::DocumentDiagnosticReport ::Full ( diagnostics ) = diagnostics {
assert_eq! ( diagnostics . full_document_diagnostic_report . items . len ( ) , 1 ) ;
assert_eq! (
diagnostics . full_document_diagnostic_report . items [ 0 ] . message ,
2025-03-21 10:56:55 +13:00
" Unexpected token: ; "
2024-03-12 23:57:43 -07:00
) ;
} else {
panic! ( " Expected full diagnostics " ) ;
}
} else {
panic! ( " Expected diagnostics " ) ;
}
}
2024-06-11 19:23:35 -04:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn test_kcl_lsp_diagnostic_has_lints ( ) {
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///testlint.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
2024-12-10 06:27:04 +13:00
text : r #" THING = 10 " #. to_string ( ) ,
2024-06-11 19:23:35 -04:00
} ,
} )
. await ;
// Send diagnostics request.
let diagnostics = server
. diagnostic ( tower_lsp ::lsp_types ::DocumentDiagnosticParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///testlint.kcl " . try_into ( ) . unwrap ( ) ,
} ,
partial_result_params : Default ::default ( ) ,
work_done_progress_params : Default ::default ( ) ,
identifier : None ,
previous_result_id : None ,
} )
. await
. unwrap ( ) ;
// Check the diagnostics.
if let tower_lsp ::lsp_types ::DocumentDiagnosticReportResult ::Report ( diagnostics ) = diagnostics {
if let tower_lsp ::lsp_types ::DocumentDiagnosticReport ::Full ( diagnostics ) = diagnostics {
assert_eq! ( diagnostics . full_document_diagnostic_report . items . len ( ) , 1 ) ;
assert_eq! (
diagnostics . full_document_diagnostic_report . items [ 0 ] . message ,
" Identifiers must be lowerCamelCase "
) ;
} else {
panic! ( " Expected full diagnostics " ) ;
}
} else {
panic! ( " Expected diagnostics " ) ;
}
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-03-12 23:57:43 -07:00
async fn test_copilot_lsp_set_editor_info ( ) {
2024-04-15 17:18:32 -07:00
let server = copilot_lsp_server ( ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
// Send set editor info request.
server
. set_editor_info ( crate ::lsp ::copilot ::types ::CopilotEditorInfo {
editor_info : crate ::lsp ::copilot ::types ::EditorInfo {
name : " vscode " . to_string ( ) ,
version : " 1.0.0 " . to_string ( ) ,
} ,
editor_configuration : crate ::lsp ::copilot ::types ::EditorConfiguration {
disabled_languages : vec ! [ ] ,
enable_auto_completions : true ,
} ,
editor_plugin_info : crate ::lsp ::copilot ::types ::EditorInfo {
name : " copilot " . to_string ( ) ,
version : " 1.0.0 " . to_string ( ) ,
} ,
} )
. await
. unwrap ( ) ;
// Check the editor info.
// Acquire the lock.
let editor_info = server . editor_info . read ( ) . unwrap ( ) ;
assert_eq! ( editor_info . editor_info . name , " vscode " ) ;
assert_eq! ( editor_info . editor_info . version , " 1.0.0 " ) ;
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-03-20 17:40:46 -07:00
#[ ignore ] // Ignore til hosted model is faster (@jessfraz working on).
2024-03-12 23:57:43 -07:00
async fn test_copilot_lsp_completions_raw ( ) {
2024-04-15 17:18:32 -07:00
let server = copilot_lsp_server ( ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test.copilot " . try_into ( ) . unwrap ( ) ,
language_id : " copilot " . to_string ( ) ,
version : 1 ,
text : " st " . to_string ( ) ,
} ,
} )
. await ;
// Send completion request.
let completions = server
. get_completions (
" kcl " . to_string ( ) ,
2025-03-21 22:39:12 +13:00
r #" bracket = startSketchOn(XY)
2024-03-15 13:07:02 -07:00
| > startProfileAt ( [ 0 , 0 ] , % )
" #
2024-03-12 23:57:43 -07:00
. to_string ( ) ,
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
r #" |> close()
| > extrude ( length = 10 ) " #
2024-03-15 13:07:02 -07:00
. to_string ( ) ,
2024-03-12 23:57:43 -07:00
)
. await
. unwrap ( ) ;
// Check the completions.
assert_eq! ( completions . len ( ) , 1 ) ;
println! ( " got completion: \n ``` \n {} \n ``` " , completions [ 0 ] ) ;
// Test the cache.
let completions_hit_cache = server
. get_completions (
" kcl " . to_string ( ) ,
2025-03-21 22:39:12 +13:00
r #" bracket = startSketchOn(XY)
2024-03-15 13:07:02 -07:00
| > startProfileAt ( [ 0 , 0 ] , % )
" #
2024-03-12 23:57:43 -07:00
. to_string ( ) ,
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
r #" |> close()
| > extrude ( length = 10 ) " #
2024-03-15 13:07:02 -07:00
. to_string ( ) ,
2024-03-12 23:57:43 -07:00
)
. await
. unwrap ( ) ;
assert_eq! ( completions , completions_hit_cache ) ;
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-03-20 17:40:46 -07:00
#[ ignore ] // Ignore til hosted model is faster (@jessfraz working on).
2024-03-12 23:57:43 -07:00
async fn test_copilot_lsp_completions ( ) {
2024-04-15 17:18:32 -07:00
let server = copilot_lsp_server ( ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test.copilot " . try_into ( ) . unwrap ( ) ,
language_id : " copilot " . to_string ( ) ,
version : 1 ,
text : " st " . to_string ( ) ,
} ,
} )
. await ;
// Send completion request.
let params = crate ::lsp ::copilot ::types ::CopilotLspCompletionParams {
doc : crate ::lsp ::copilot ::types ::CopilotDocParams {
indent_size : 4 ,
insert_spaces : true ,
language_id : " kcl " . to_string ( ) ,
path : " file:///test.copilot " . to_string ( ) ,
2024-03-15 13:07:02 -07:00
position : crate ::lsp ::copilot ::types ::CopilotPosition { line : 3 , character : 3 } ,
2024-03-12 23:57:43 -07:00
relative_path : " test.copilot " . to_string ( ) ,
2025-03-21 22:39:12 +13:00
source : r #" bracket = startSketchOn(XY)
2024-03-15 13:07:02 -07:00
| > startProfileAt ( [ 0 , 0 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
| > extrude ( length = 10 )
2024-03-12 23:57:43 -07:00
" #
. to_string ( ) ,
tab_size : 4 ,
uri : " file:///test.copilot " . into ( ) ,
} ,
} ;
let completions = server . get_completions_cycling ( params . clone ( ) ) . await . unwrap ( ) ;
// Check the completions.
assert_eq! ( completions . completions . len ( ) , 1 ) ;
// Accept the completion.
let completion = completions . completions . first ( ) . unwrap ( ) ;
// Send completion accept request.
server
. accept_completion ( crate ::lsp ::copilot ::types ::CopilotAcceptCompletionParams { uuid : completion . uuid } )
. await ;
// Test the cache.
let completions_hit_cache = server . get_completions_cycling ( params ) . await . unwrap ( ) ;
assert_eq! ( completions . completions , completions_hit_cache . completions ) ;
// Reject the completion.
let completion = completions . completions . first ( ) . unwrap ( ) ;
// Send completion reject request.
server
. reject_completions ( crate ::lsp ::copilot ::types ::CopilotRejectCompletionParams {
uuids : vec ! [ completion . uuid ] ,
} )
. await ;
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-03-12 23:57:43 -07:00
async fn test_copilot_on_save ( ) {
2024-04-15 17:18:32 -07:00
let server = copilot_lsp_server ( ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
// Send save file.
server
. did_save ( tower_lsp ::lsp_types ::DidSaveTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.copilot " . try_into ( ) . unwrap ( ) ,
} ,
text : Some ( " my file " . to_string ( ) ) ,
} )
. await ;
// Check the code map.
2024-06-27 15:43:49 -07:00
assert_eq! ( server . code_map . len ( ) , 1 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.copilot " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" my file " . as_bytes ( )
) ;
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-03-12 23:57:43 -07:00
async fn test_kcl_on_save ( ) {
2024-04-12 21:32:57 -07:00
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
// Send save file.
server
. did_save ( tower_lsp ::lsp_types ::DidSaveTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
text : Some ( " my file " . to_string ( ) ) ,
} )
. await ;
// Check the code map.
2024-06-27 15:43:49 -07:00
assert_eq! ( server . code_map . len ( ) , 1 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" my file " . as_bytes ( )
) ;
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-03-12 23:57:43 -07:00
async fn test_copilot_rename_not_exists ( ) {
2024-04-15 17:18:32 -07:00
let server = copilot_lsp_server ( ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
// Send rename request.
server
. did_rename_files ( tower_lsp ::lsp_types ::RenameFilesParams {
files : vec ! [ tower_lsp ::lsp_types ::FileRename {
old_uri : " file:///test.copilot " . into ( ) ,
new_uri : " file:///test2.copilot " . into ( ) ,
} ] ,
} )
. await ;
// Check the code map.
2024-06-27 15:43:49 -07:00
assert_eq! ( server . code_map . len ( ) , 1 ) ;
2024-03-12 23:57:43 -07:00
assert_eq! (
2024-06-27 15:43:49 -07:00
server . code_map . get ( " file:///test2.copilot " ) . unwrap ( ) . clone ( ) ,
2024-03-12 23:57:43 -07:00
" " . as_bytes ( )
) ;
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-03-12 23:57:43 -07:00
async fn test_lsp_initialized ( ) {
2024-04-15 17:18:32 -07:00
let copilot_server = copilot_lsp_server ( ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
// Send initialize request.
copilot_server
. initialize ( tower_lsp ::lsp_types ::InitializeParams ::default ( ) )
. await
. unwrap ( ) ;
// Send initialized request.
copilot_server
. initialized ( tower_lsp ::lsp_types ::InitializedParams { } )
. await ;
// Check the code map.
2024-06-27 15:43:49 -07:00
assert_eq! ( copilot_server . code_map . len ( ) , 0 ) ;
2024-03-12 23:57:43 -07:00
// Now do the same for kcl.
2024-04-12 21:32:57 -07:00
let kcl_server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
2024-03-12 23:57:43 -07:00
// Send initialize request.
kcl_server
. initialize ( tower_lsp ::lsp_types ::InitializeParams ::default ( ) )
. await
. unwrap ( ) ;
// Send initialized request.
kcl_server . initialized ( tower_lsp ::lsp_types ::InitializedParams { } ) . await ;
// Check the code map.
2024-06-27 15:43:49 -07:00
assert_eq! ( kcl_server . code_map . len ( ) , 0 ) ;
2024-03-12 23:57:43 -07:00
// Now shut them down.
copilot_server . shutdown ( ) . await . unwrap ( ) ;
kcl_server . shutdown ( ) . await . unwrap ( ) ;
}
2024-04-12 21:32:57 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn test_kcl_lsp_on_change_update_ast ( ) {
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
2024-12-10 06:27:04 +13:00
let same_text = r # "thing = 1"# . to_string ( ) ;
2024-04-12 21:32:57 -07:00
// 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 : same_text . clone ( ) ,
} ,
} )
. await ;
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2024-04-12 21:32:57 -07:00
// Send change file.
server
. did_change ( tower_lsp ::lsp_types ::DidChangeTextDocumentParams {
text_document : tower_lsp ::lsp_types ::VersionedTextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
version : 1 ,
} ,
content_changes : vec ! [ tower_lsp ::lsp_types ::TextDocumentContentChangeEvent {
range : None ,
range_length : None ,
text : same_text . clone ( ) ,
} ] ,
} )
. await ;
// Make sure the ast is the same.
2024-06-27 15:43:49 -07:00
assert_eq! ( ast , server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ) ;
2024-04-12 21:32:57 -07:00
// Update the text.
2024-12-10 06:27:04 +13:00
let new_text = r # "thing = 2"# . to_string ( ) ;
2024-04-12 21:32:57 -07:00
// Send change file.
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 : new_text . clone ( ) ,
} ] ,
} )
. await ;
2024-06-27 15:43:49 -07:00
assert! ( ast ! = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ) ;
2024-04-12 21:32:57 -07:00
}
#[ tokio::test(flavor = " multi_thread " ) ]
2024-08-12 13:06:30 -07:00
async fn kcl_test_kcl_lsp_on_change_update_memory ( ) {
2024-04-12 21:32:57 -07:00
let server = kcl_lsp_server ( true ) . await . unwrap ( ) ;
2024-12-10 06:27:04 +13:00
let same_text = r # "thing = 1"# . to_string ( ) ;
2024-04-12 21:32:57 -07:00
// 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 : same_text . clone ( ) ,
} ,
} )
. await ;
// Send change file.
server
. did_change ( tower_lsp ::lsp_types ::DidChangeTextDocumentParams {
text_document : tower_lsp ::lsp_types ::VersionedTextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
version : 1 ,
} ,
content_changes : vec ! [ tower_lsp ::lsp_types ::TextDocumentContentChangeEvent {
range : None ,
range_length : None ,
text : same_text . clone ( ) ,
} ] ,
} )
. await ;
// Update the text.
2024-12-10 06:27:04 +13:00
let new_text = r # "thing = 2"# . to_string ( ) ;
2024-04-12 21:32:57 -07:00
// Send change file.
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 : new_text . clone ( ) ,
} ] ,
} )
. await ;
}
#[ tokio::test(flavor = " multi_thread " , worker_threads = 10) ]
2024-08-12 13:06:30 -07:00
async fn kcl_test_kcl_lsp_update_units ( ) {
2024-04-12 21:32:57 -07:00
let server = kcl_lsp_server ( true ) . await . unwrap ( ) ;
let same_text = r #" fn cube = (pos, scale) => {
2025-03-21 22:39:12 +13:00
sg = startSketchOn ( XY )
2024-04-12 21:32:57 -07:00
| > startProfileAt ( pos , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 0 , scale ] )
| > line ( end = [ scale , 0 ] )
| > line ( end = [ 0 , - scale ] )
2024-04-12 21:32:57 -07:00
return sg
}
2024-12-10 06:27:04 +13:00
part001 = cube ( [ 0 , 0 ] , 20 )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > close ( )
| > extrude ( length = 20 ) " #
2024-04-12 21:32:57 -07:00
. to_string ( ) ;
// 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 : same_text . clone ( ) ,
} ,
} )
. await ;
2024-04-15 17:18:32 -07:00
// Get the tokens.
2024-06-27 15:43:49 -07:00
let tokens = server . token_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
assert_eq! ( tokens . as_slice ( ) . len ( ) , 123 ) ;
2024-04-15 17:18:32 -07:00
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert_eq! ( ast . ast . body . len ( ) , 2 ) ;
2024-04-12 21:32:57 -07:00
// Send change file.
server
. did_change ( tower_lsp ::lsp_types ::DidChangeTextDocumentParams {
text_document : tower_lsp ::lsp_types ::VersionedTextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
version : 1 ,
} ,
content_changes : vec ! [ tower_lsp ::lsp_types ::TextDocumentContentChangeEvent {
range : None ,
range_length : None ,
text : same_text . clone ( ) ,
} ] ,
} )
. await ;
2024-04-25 00:13:09 -07:00
let units = server . executor_ctx . read ( ) . await . clone ( ) . unwrap ( ) . settings . units ;
assert_eq! ( units , crate ::settings ::types ::UnitLength ::Mm ) ;
2024-04-12 21:32:57 -07:00
// Update the units.
server
. update_units ( crate ::lsp ::kcl ::custom_notifications ::UpdateUnitsParams {
2024-04-15 17:18:32 -07:00
text_document : crate ::lsp ::kcl ::custom_notifications ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
2024-04-25 00:13:09 -07:00
units : crate ::settings ::types ::UnitLength ::M ,
2024-04-15 17:18:32 -07:00
text : same_text . clone ( ) ,
} )
. await
. unwrap ( ) ;
2024-06-27 15:43:49 -07:00
let units = server . executor_ctx ( ) . await . clone ( ) . unwrap ( ) . settings . units ;
2024-04-25 00:13:09 -07:00
assert_eq! ( units , crate ::settings ::types ::UnitLength ::M ) ;
2024-04-15 17:18:32 -07:00
}
#[ tokio::test(flavor = " multi_thread " ) ]
2024-08-12 13:06:30 -07:00
async fn kcl_test_kcl_lsp_empty_file_execute_ok ( ) {
2024-04-15 17:18:32 -07:00
let server = kcl_lsp_server ( true ) . await . unwrap ( ) ;
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
text : " " . to_string ( ) ,
} ,
} )
. await ;
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn test_kcl_lsp_diagnostics_on_parse_error ( ) {
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
text : " asdasd asdasd asda!d " . to_string ( ) ,
} ,
} )
. await ;
// Get the diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 1 ) ;
2024-04-15 17:18:32 -07:00
// Update the text.
2024-12-10 06:27:04 +13:00
let new_text = r # "thing = 2"# . to_string ( ) ;
2024-04-15 17:18:32 -07:00
// Send change file.
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 : new_text . clone ( ) ,
} ] ,
} )
. await ;
// Get the diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 0 ) ;
2024-04-15 17:18:32 -07:00
}
#[ tokio::test(flavor = " multi_thread " ) ]
2024-08-12 13:06:30 -07:00
async fn kcl_test_kcl_lsp_diagnostics_on_execution_error ( ) {
2024-04-15 17:18:32 -07:00
let server = kcl_lsp_server ( true ) . await . unwrap ( ) ;
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
2025-03-21 22:39:12 +13:00
text : r #" part001 = startSketchOn(XY)
2024-04-15 17:18:32 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] )
| > line ( end = [ - 20 , 0 ] )
| > close ( )
| > extrude ( length = 3.14 )
2025-02-21 14:41:25 -06:00
| > fillet (
radius = 3.14 ,
tags = [ " tag_or_edge_fn " ] ,
) " #
. to_string ( ) ,
2024-04-15 17:18:32 -07:00
} ,
} )
. await ;
// Get the diagnostics.
2024-12-17 09:38:32 +13:00
// TODO warnings being stomped by execution errors?
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 1 ) ;
2024-04-15 17:18:32 -07:00
// Update the text.
2025-03-21 22:39:12 +13:00
let new_text = r #" part001 = startSketchOn(XY)
2024-04-15 17:18:32 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] )
| > line ( end = [ - 20 , 0 ] )
| > close ( )
| > extrude ( length = 3.14 ) " #
2024-04-15 17:18:32 -07:00
. to_string ( ) ;
// Send change file.
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 : new_text . clone ( ) ,
} ] ,
} )
. await ;
// Get the diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 0 ) ;
2024-04-15 17:18:32 -07:00
}
#[ tokio::test(flavor = " multi_thread " ) ]
2024-08-12 13:06:30 -07:00
async fn kcl_test_kcl_lsp_full_to_empty_file_updates_ast_and_memory ( ) {
2024-04-15 17:18:32 -07:00
let server = kcl_lsp_server ( true ) . await . unwrap ( ) ;
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
2025-03-21 22:39:12 +13:00
text : r #" part001 = startSketchOn(XY)
2024-04-15 17:18:32 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] )
| > line ( end = [ - 20 , 0 ] )
| > close ( )
| > extrude ( length = 3.14 ) " #
2024-04-15 17:18:32 -07:00
. to_string ( ) ,
} ,
} )
. await ;
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-04-15 17:18:32 -07:00
// Send change file.
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 : " " . to_string ( ) ,
} ] ,
} )
. await ;
2024-10-30 16:52:17 -04:00
let mut default_hashed = Node ::< Program > ::default ( ) ;
2024-07-16 12:13:17 -04:00
default_hashed . compute_digest ( ) ;
2024-04-15 17:18:32 -07:00
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert_eq! ( ast . ast , default_hashed ) ;
2024-04-15 17:18:32 -07:00
}
#[ tokio::test(flavor = " multi_thread " ) ]
2024-08-12 13:06:30 -07:00
async fn kcl_test_kcl_lsp_code_unchanged_but_has_diagnostics_reexecute ( ) {
2024-04-15 17:18:32 -07:00
let server = kcl_lsp_server ( true ) . await . unwrap ( ) ;
2025-03-21 22:39:12 +13:00
let code = r #" part001 = startSketchOn(XY)
2024-04-15 17:18:32 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] )
| > line ( end = [ - 20 , 0 ] )
| > close ( )
| > extrude ( length = 3.14 ) " #;
2024-04-15 17:18:32 -07:00
// 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 ;
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-04-15 17:18:32 -07:00
// Assure we have no diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 0 ) ;
2024-04-15 17:18:32 -07:00
// Add some fake diagnostics.
2024-06-27 15:43:49 -07:00
server . diagnostics_map . insert (
" file:///test.kcl " . to_string ( ) ,
vec! [ tower_lsp ::lsp_types ::Diagnostic {
range : tower_lsp ::lsp_types ::Range {
start : tower_lsp ::lsp_types ::Position { line : 0 , character : 0 } ,
end : tower_lsp ::lsp_types ::Position { line : 0 , character : 0 } ,
} ,
message : " fake diagnostic " . to_string ( ) ,
severity : Some ( tower_lsp ::lsp_types ::DiagnosticSeverity ::ERROR ) ,
code : None ,
source : None ,
related_information : None ,
tags : None ,
data : None ,
code_description : None ,
} ] ,
) ;
2024-04-15 17:18:32 -07:00
// Assure we have one diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 1 ) ;
2024-04-15 17:18:32 -07:00
// Clear the ast and memory.
2025-02-27 14:34:01 -08:00
server . ast_map . insert (
" file:///test.kcl " . to_string ( ) ,
crate ::Program {
ast : Default ::default ( ) ,
original_file_contents : Default ::default ( ) ,
} ,
) ;
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert_eq! ( ast . ast , Node ::< Program > ::default ( ) ) ;
2024-04-15 17:18:32 -07:00
// 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 ;
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-04-15 17:18:32 -07:00
// Assure we have no diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 0 ) ;
2024-04-15 17:18:32 -07:00
}
#[ tokio::test(flavor = " multi_thread " ) ]
2024-08-12 13:06:30 -07:00
async fn kcl_test_kcl_lsp_code_and_ast_unchanged_but_has_diagnostics_reexecute ( ) {
2024-04-15 17:18:32 -07:00
let server = kcl_lsp_server ( true ) . await . unwrap ( ) ;
2025-03-21 22:39:12 +13:00
let code = r #" part001 = startSketchOn(XY)
2024-04-15 17:18:32 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] )
| > line ( end = [ - 20 , 0 ] )
| > close ( )
| > extrude ( length = 3.14 ) " #;
2024-04-15 17:18:32 -07:00
// 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 ;
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-04-15 17:18:32 -07:00
// Assure we have no diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 0 ) ;
2024-04-15 17:18:32 -07:00
// Add some fake diagnostics.
2024-06-27 15:43:49 -07:00
server . diagnostics_map . insert (
" file:///test.kcl " . to_string ( ) ,
vec! [ tower_lsp ::lsp_types ::Diagnostic {
range : tower_lsp ::lsp_types ::Range {
start : tower_lsp ::lsp_types ::Position { line : 0 , character : 0 } ,
end : tower_lsp ::lsp_types ::Position { line : 0 , character : 0 } ,
} ,
message : " fake diagnostic " . to_string ( ) ,
severity : Some ( tower_lsp ::lsp_types ::DiagnosticSeverity ::ERROR ) ,
code : None ,
source : None ,
related_information : None ,
tags : None ,
data : None ,
code_description : None ,
} ] ,
) ;
2024-04-15 17:18:32 -07:00
// Assure we have one diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 1 ) ;
2024-04-15 17:18:32 -07:00
// 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 ;
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-04-15 17:18:32 -07:00
// Assure we have no diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 0 ) ;
2024-04-15 17:18:32 -07:00
}
#[ tokio::test(flavor = " multi_thread " ) ]
2024-08-12 13:06:30 -07:00
async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_diagnostics_reexecute_on_unit_change ( ) {
2024-04-15 17:18:32 -07:00
let server = kcl_lsp_server ( true ) . await . unwrap ( ) ;
2025-03-21 22:39:12 +13:00
let code = r #" part001 = startSketchOn(XY)
2024-04-15 17:18:32 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] )
| > line ( end = [ - 20 , 0 ] )
| > close ( )
| > extrude ( length = 3.14 ) " #;
2024-04-15 17:18:32 -07:00
// 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 ;
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-04-15 17:18:32 -07:00
// Assure we have no diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 0 ) ;
2024-04-15 17:18:32 -07:00
// Add some fake diagnostics.
2024-06-27 15:43:49 -07:00
server . diagnostics_map . insert (
" file:///test.kcl " . to_string ( ) ,
vec! [ tower_lsp ::lsp_types ::Diagnostic {
range : tower_lsp ::lsp_types ::Range {
start : tower_lsp ::lsp_types ::Position { line : 0 , character : 0 } ,
end : tower_lsp ::lsp_types ::Position { line : 0 , character : 0 } ,
} ,
message : " fake diagnostic " . to_string ( ) ,
severity : Some ( tower_lsp ::lsp_types ::DiagnosticSeverity ::ERROR ) ,
code : None ,
source : None ,
related_information : None ,
tags : None ,
data : None ,
code_description : None ,
} ] ,
) ;
2024-04-15 17:18:32 -07:00
// Assure we have one diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 1 ) ;
2024-04-15 17:18:32 -07:00
2024-06-27 15:43:49 -07:00
let units = server . executor_ctx ( ) . await . clone ( ) . unwrap ( ) . settings . units ;
2024-04-25 00:13:09 -07:00
assert_eq! ( units , crate ::settings ::types ::UnitLength ::Mm ) ;
2024-04-15 17:18:32 -07:00
// Update the units to the _same_ units.
server
. update_units ( crate ::lsp ::kcl ::custom_notifications ::UpdateUnitsParams {
text_document : crate ::lsp ::kcl ::custom_notifications ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
2024-04-25 00:13:09 -07:00
units : crate ::settings ::types ::UnitLength ::Mm ,
2024-04-15 17:18:32 -07:00
text : code . to_string ( ) ,
} )
. await
. unwrap ( ) ;
2024-06-27 15:43:49 -07:00
let units = server . executor_ctx ( ) . await . clone ( ) . unwrap ( ) . settings . units ;
2024-04-25 00:13:09 -07:00
assert_eq! ( units , crate ::settings ::types ::UnitLength ::Mm ) ;
2024-04-15 17:18:32 -07:00
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-04-15 17:18:32 -07:00
// Assure we have no diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 0 ) ;
2024-04-15 17:18:32 -07:00
}
#[ tokio::test(flavor = " multi_thread " ) ]
2024-08-12 13:06:30 -07:00
async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_memory_reexecute_on_unit_change ( ) {
2024-04-15 17:18:32 -07:00
let server = kcl_lsp_server ( true ) . await . unwrap ( ) ;
2025-03-21 22:39:12 +13:00
let code = r #" part001 = startSketchOn(XY)
2024-04-15 17:18:32 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] )
| > line ( end = [ - 20 , 0 ] )
| > close ( )
| > extrude ( length = 3.14 ) " #;
2024-04-15 17:18:32 -07:00
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
2024-04-12 21:32:57 -07:00
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
2024-04-15 17:18:32 -07:00
language_id : " kcl " . to_string ( ) ,
version : 1 ,
text : code . to_string ( ) ,
2024-04-12 21:32:57 -07:00
} ,
} )
. await ;
2024-04-15 17:18:32 -07:00
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-04-15 17:18:32 -07:00
// Assure we have no diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 0 ) ;
2024-04-15 17:18:32 -07:00
2024-06-27 15:43:49 -07:00
let units = server . executor_ctx ( ) . await . clone ( ) . unwrap ( ) . settings . units ;
2024-04-25 00:13:09 -07:00
assert_eq! ( units , crate ::settings ::types ::UnitLength ::Mm ) ;
2024-04-12 21:32:57 -07:00
2024-04-15 17:18:32 -07:00
// Update the units to the _same_ units.
server
. update_units ( crate ::lsp ::kcl ::custom_notifications ::UpdateUnitsParams {
text_document : crate ::lsp ::kcl ::custom_notifications ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
2024-04-25 00:13:09 -07:00
units : crate ::settings ::types ::UnitLength ::Mm ,
2024-04-15 17:18:32 -07:00
text : code . to_string ( ) ,
} )
. await
. unwrap ( ) ;
2024-06-27 15:43:49 -07:00
let units = server . executor_ctx ( ) . await . clone ( ) . unwrap ( ) . settings . units ;
2024-04-25 00:13:09 -07:00
assert_eq! ( units , crate ::settings ::types ::UnitLength ::Mm ) ;
2024-04-15 17:18:32 -07:00
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-04-15 17:18:32 -07:00
// Assure we have no diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 0 ) ;
2024-04-15 17:18:32 -07:00
}
#[ tokio::test(flavor = " multi_thread " ) ]
2024-08-12 13:06:30 -07:00
async fn kcl_test_kcl_lsp_cant_execute_set ( ) {
2024-04-15 17:18:32 -07:00
let server = kcl_lsp_server ( true ) . await . unwrap ( ) ;
2025-03-21 22:39:12 +13:00
let code = r #" part001 = startSketchOn(XY)
2024-04-15 17:18:32 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] )
| > line ( end = [ - 20 , 0 ] )
| > close ( )
| > extrude ( length = 3.14 ) " #;
2024-04-15 17:18:32 -07:00
// 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 ;
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-04-15 17:18:32 -07:00
// Assure we have no diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 0 ) ;
2024-04-15 17:18:32 -07:00
// Update the units to the _same_ units.
2024-06-27 15:43:49 -07:00
let units = server . executor_ctx ( ) . await . clone ( ) . unwrap ( ) . settings . units ;
2024-04-25 00:13:09 -07:00
assert_eq! ( units , crate ::settings ::types ::UnitLength ::Mm ) ;
2024-04-15 17:18:32 -07:00
server
. update_units ( crate ::lsp ::kcl ::custom_notifications ::UpdateUnitsParams {
text_document : crate ::lsp ::kcl ::custom_notifications ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
2024-04-25 00:13:09 -07:00
units : crate ::settings ::types ::UnitLength ::Mm ,
2024-04-15 17:18:32 -07:00
text : code . to_string ( ) ,
} )
. await
. unwrap ( ) ;
2024-06-27 15:43:49 -07:00
let units = server . executor_ctx ( ) . await . clone ( ) . unwrap ( ) . settings . units ;
2024-04-25 00:13:09 -07:00
assert_eq! ( units , crate ::settings ::types ::UnitLength ::Mm ) ;
2024-04-15 17:18:32 -07:00
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-04-15 17:18:32 -07:00
// Assure we have no diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 0 ) ;
2024-04-15 17:18:32 -07:00
assert_eq! ( server . can_execute ( ) . await , true ) ;
// Set that we cannot execute.
server
. update_can_execute ( crate ::lsp ::kcl ::custom_notifications ::UpdateCanExecuteParams { can_execute : false } )
. await
. unwrap ( ) ;
assert_eq! ( server . can_execute ( ) . await , false ) ;
// Update the units to the _same_ units.
2024-06-27 15:43:49 -07:00
let units = server . executor_ctx ( ) . await . clone ( ) . unwrap ( ) . settings . units ;
2024-04-25 00:13:09 -07:00
assert_eq! ( units , crate ::settings ::types ::UnitLength ::Mm ) ;
2024-04-15 17:18:32 -07:00
server
. update_units ( crate ::lsp ::kcl ::custom_notifications ::UpdateUnitsParams {
text_document : crate ::lsp ::kcl ::custom_notifications ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
2024-04-25 00:13:09 -07:00
units : crate ::settings ::types ::UnitLength ::Mm ,
2024-04-15 17:18:32 -07:00
text : code . to_string ( ) ,
} )
. await
. unwrap ( ) ;
2024-06-27 15:43:49 -07:00
let units = server . executor_ctx ( ) . await . clone ( ) . unwrap ( ) . settings . units ;
2024-04-25 00:13:09 -07:00
assert_eq! ( units , crate ::settings ::types ::UnitLength ::Mm ) ;
2024-04-15 17:18:32 -07:00
2024-10-30 16:52:17 -04:00
let mut default_hashed = Node ::< Program > ::default ( ) ;
2024-07-16 12:13:17 -04:00
default_hashed . compute_digest ( ) ;
2024-04-15 17:18:32 -07:00
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = default_hashed ) ;
2024-04-15 17:18:32 -07:00
// Assure we have no diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 0 ) ;
2024-04-15 17:18:32 -07:00
// Set that we CAN execute.
server
. update_can_execute ( crate ::lsp ::kcl ::custom_notifications ::UpdateCanExecuteParams { can_execute : true } )
. await
. unwrap ( ) ;
assert_eq! ( server . can_execute ( ) . await , true ) ;
// Update the units to the _same_ units.
2024-04-25 00:13:09 -07:00
let units = server . executor_ctx . read ( ) . await . clone ( ) . unwrap ( ) . settings . units ;
assert_eq! ( units , crate ::settings ::types ::UnitLength ::Mm ) ;
2024-04-15 17:18:32 -07:00
server
. update_units ( crate ::lsp ::kcl ::custom_notifications ::UpdateUnitsParams {
text_document : crate ::lsp ::kcl ::custom_notifications ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
2024-04-25 00:13:09 -07:00
units : crate ::settings ::types ::UnitLength ::Mm ,
2024-04-15 17:18:32 -07:00
text : code . to_string ( ) ,
} )
. await
. unwrap ( ) ;
2024-06-27 15:43:49 -07:00
2024-04-25 00:13:09 -07:00
let units = server . executor_ctx . read ( ) . await . clone ( ) . unwrap ( ) . settings . units ;
assert_eq! ( units , crate ::settings ::types ::UnitLength ::Mm ) ;
2024-04-12 21:32:57 -07:00
2024-04-15 17:18:32 -07:00
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-04-12 21:32:57 -07:00
2024-04-15 17:18:32 -07:00
// Assure we have no diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 0 ) ;
2024-04-15 17:18:32 -07:00
}
#[ tokio::test(flavor = " multi_thread " ) ]
async fn test_kcl_lsp_folding ( ) {
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
2025-03-21 22:39:12 +13:00
text : r #" startSketchOn(XY)
2024-04-15 17:18:32 -07:00
| > startProfileAt ( [ 0 , 0 ] , % ) " #
. to_string ( ) ,
} ,
} )
. await ;
// Send folding request.
let folding = server
. folding_range ( tower_lsp ::lsp_types ::FoldingRangeParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
work_done_progress_params : Default ::default ( ) ,
partial_result_params : Default ::default ( ) ,
} )
. await
. unwrap ( )
. unwrap ( ) ;
// Check the folding.
assert_eq! ( folding . len ( ) , 1 ) ;
assert_eq! (
folding . first ( ) . unwrap ( ) . clone ( ) ,
tower_lsp ::lsp_types ::FoldingRange {
2025-03-21 22:39:12 +13:00
start_line : 17 ,
2024-04-15 17:18:32 -07:00
start_character : None ,
2025-03-21 22:39:12 +13:00
end_line : 65 ,
2024-04-15 17:18:32 -07:00
end_character : None ,
kind : Some ( tower_lsp ::lsp_types ::FoldingRangeKind ::Region ) ,
2025-03-21 22:39:12 +13:00
collapsed_text : Some ( " startSketchOn(XY) " . to_string ( ) )
2024-04-15 17:18:32 -07:00
}
) ;
2024-04-12 21:32:57 -07:00
}
2024-06-26 01:07:18 -07:00
#[ tokio::test(flavor = " multi_thread " ) ]
2024-08-12 13:06:30 -07:00
async fn kcl_test_kcl_lsp_code_with_parse_error_and_ast_unchanged_but_has_diagnostics_reparse ( ) {
2024-06-26 01:07:18 -07:00
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
2025-03-21 22:39:12 +13:00
let code = r #" part001 = startSketchOn(XY)
2024-06-26 01:07:18 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] )
| > line ( end = [ - 20 , 0 ] )
| > close ( )
2024-06-26 01:07:18 -07:00
| > ^ ^ ^ 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 ;
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) ;
2024-06-26 01:07:18 -07:00
assert! ( ast . is_none ( ) ) ;
// Assure we have one diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 1 ) ;
2024-06-26 01:07:18 -07:00
// 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 ;
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) ;
2024-06-26 01:07:18 -07:00
assert! ( ast . is_none ( ) ) ;
// Assure we have one diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 1 ) ;
2024-06-26 01:07:18 -07:00
}
#[ tokio::test(flavor = " multi_thread " ) ]
2024-08-12 13:06:30 -07:00
async fn kcl_test_kcl_lsp_code_with_lint_and_ast_unchanged_but_has_diagnostics_reparse ( ) {
2024-06-26 01:07:18 -07:00
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
2024-12-10 06:27:04 +13:00
let code = r #" LINT = 1
2025-03-21 22:39:12 +13:00
part001 = startSketchOn ( XY )
2024-06-26 01:07:18 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] )
| > line ( end = [ - 20 , 0 ] )
| > close ( )
| > extrude ( length = 3.14 ) " #;
2024-06-26 01:07:18 -07:00
// 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 ;
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-06-26 01:07:18 -07:00
// Assure we have one diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 1 ) ;
2024-06-26 01:07:18 -07:00
// 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 ;
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-06-26 01:07:18 -07:00
// Assure we have one diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 1 ) ;
2024-06-26 01:07:18 -07:00
}
#[ tokio::test(flavor = " multi_thread " ) ]
2024-08-12 13:06:30 -07:00
async fn kcl_test_kcl_lsp_code_with_lint_and_parse_error_and_ast_unchanged_but_has_diagnostics_reparse ( ) {
2024-06-26 01:07:18 -07:00
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
2024-12-10 06:27:04 +13:00
let code = r #" LINT = 1
2025-03-21 22:39:12 +13:00
part001 = startSketchOn ( XY )
2024-06-26 01:07:18 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] )
| > line ( end = [ - 20 , 0 ] )
| > close ( )
2024-06-26 01:07:18 -07:00
| > ^ ^ ^ ^ 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 ;
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) ;
2024-06-26 01:07:18 -07:00
assert! ( ast . is_none ( ) ) ;
// Assure we have diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 1 ) ;
2024-06-26 01:07:18 -07:00
// 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 ;
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) ;
2024-06-26 01:07:18 -07:00
assert! ( ast . is_none ( ) ) ;
// Assure we have one diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 1 ) ;
2024-06-26 01:07:18 -07:00
}
#[ tokio::test(flavor = " multi_thread " ) ]
2024-08-12 13:06:30 -07:00
async fn kcl_test_kcl_lsp_code_lint_and_ast_unchanged_but_has_diagnostics_reexecute ( ) {
2024-06-26 01:07:18 -07:00
let server = kcl_lsp_server ( true ) . await . unwrap ( ) ;
2024-12-10 06:27:04 +13:00
let code = r #" LINT = 1
2025-03-21 22:39:12 +13:00
part001 = startSketchOn ( XY )
2024-06-26 01:07:18 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] , tag = $seg01 )
| > line ( end = [ - 20 , 0 ] , tag = $seg01 )
| > close ( )
| > extrude ( length = 3.14 ) " #;
2024-06-26 01:07:18 -07:00
// 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 ;
// Assure we have diagnostics.
2024-12-06 13:57:31 +13:00
2024-06-26 01:07:18 -07:00
// Check the diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 2 ) ;
2024-06-26 01:07:18 -07:00
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-06-26 01:07:18 -07:00
// 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 ;
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-06-26 01:07:18 -07:00
// Assure we have diagnostics.
2024-12-06 13:57:31 +13:00
2024-06-26 01:07:18 -07:00
// Check the diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 2 ) ;
2024-06-26 01:07:18 -07:00
}
#[ tokio::test(flavor = " multi_thread " ) ]
2024-08-12 13:06:30 -07:00
async fn kcl_test_kcl_lsp_code_lint_reexecute_new_lint ( ) {
2024-06-26 01:07:18 -07:00
let server = kcl_lsp_server ( true ) . await . unwrap ( ) ;
2024-12-10 06:27:04 +13:00
let code = r #" LINT = 1
2025-03-21 22:39:12 +13:00
part001 = startSketchOn ( XY )
2024-06-26 01:07:18 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] , tag = $seg01 )
| > line ( end = [ - 20 , 0 ] , tag = $seg01 )
| > close ( )
| > extrude ( length = 3.14 ) " #;
2024-06-26 01:07:18 -07:00
// 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 ;
// Assure we have diagnostics.
2024-12-06 13:57:31 +13:00
2024-06-26 01:07:18 -07:00
// Check the diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 2 ) ;
2024-06-26 01:07:18 -07:00
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-06-26 01:07:18 -07:00
// 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 ,
2025-03-21 22:39:12 +13:00
text : r #" part001 = startSketchOn(XY)
2024-06-26 01:07:18 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] , tag = $seg01 )
| > line ( end = [ - 20 , 0 ] , tag = $seg01 )
| > close ( )
| > extrude ( length = 3.14 )
2024-12-10 06:27:04 +13:00
NEW_LINT = 1 " #
2024-06-26 01:07:18 -07:00
. to_string ( ) ,
} ] ,
} )
. await ;
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-06-26 01:07:18 -07:00
// Assure we have diagnostics.
2024-12-06 13:57:31 +13:00
2024-06-26 01:07:18 -07:00
// Check the diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 2 ) ;
2024-06-26 01:07:18 -07:00
}
#[ tokio::test(flavor = " multi_thread " ) ]
2024-08-12 13:06:30 -07:00
async fn kcl_test_kcl_lsp_code_lint_reexecute_new_ast_error ( ) {
2024-06-26 01:07:18 -07:00
let server = kcl_lsp_server ( true ) . await . unwrap ( ) ;
2024-12-10 06:27:04 +13:00
let code = r #" LINT = 1
2025-03-21 22:39:12 +13:00
part001 = startSketchOn ( XY )
2024-06-26 01:07:18 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] , tag = $seg01 )
| > line ( end = [ - 20 , 0 ] , tag = $seg01 )
| > close ( )
| > ^ ^ ^ extrude ( length = 3.14 ) " #;
2024-06-26 01:07:18 -07:00
// 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 ;
// Assure we have diagnostics.
2024-12-06 13:57:31 +13:00
2024-06-26 01:07:18 -07:00
// Check the diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 1 ) ;
2024-06-26 01:07:18 -07:00
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) ;
2024-06-26 01:07:18 -07:00
assert! ( ast . 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 ,
2025-03-21 22:39:12 +13:00
text : r #" part001 = startSketchOn(XY)
2024-06-26 01:07:18 -07:00
| > ^ ^ ^ ^ startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] , tag = $seg01 )
| > line ( end = [ - 20 , 0 ] , tag = $seg01 )
| > close ( )
| > extrude ( length = 3.14 )
2024-12-10 06:27:04 +13:00
NEW_LINT = 1 " #
2024-06-26 01:07:18 -07:00
. to_string ( ) ,
} ] ,
} )
. await ;
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) ;
2024-06-26 01:07:18 -07:00
assert! ( ast . is_none ( ) ) ;
// Assure we have diagnostics.
2024-12-06 13:57:31 +13:00
2024-06-26 01:07:18 -07:00
// Check the diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 1 ) ;
2024-06-26 01:07:18 -07:00
}
#[ tokio::test(flavor = " multi_thread " ) ]
2024-08-12 13:06:30 -07:00
async fn kcl_test_kcl_lsp_code_lint_reexecute_had_lint_new_parse_error ( ) {
2024-06-26 01:07:18 -07:00
let server = kcl_lsp_server ( true ) . await . unwrap ( ) ;
2024-12-10 06:27:04 +13:00
let code = r #" LINT = 1
2025-03-21 22:39:12 +13:00
part001 = startSketchOn ( XY )
2024-06-26 01:07:18 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] )
| > line ( end = [ - 20 , 0 ] )
| > close ( )
2024-06-26 01:07:18 -07:00
" #;
// 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 ;
// Assure we have diagnostics.
2024-12-06 13:57:31 +13:00
2024-06-26 01:07:18 -07:00
// Check the diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 1 ) ;
2024-06-26 01:07:18 -07:00
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-06-26 01:07:18 -07:00
// Get the symbols map.
2024-06-27 15:43:49 -07:00
let symbols_map = server . symbols_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2024-12-10 14:26:53 +13:00
assert! ( ! symbols_map . is_empty ( ) ) ;
2024-06-26 01:07:18 -07:00
// Get the semantic tokens map.
2024-06-27 15:43:49 -07:00
let semantic_tokens_map = server . semantic_tokens_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2024-12-10 14:26:53 +13:00
assert! ( ! semantic_tokens_map . is_empty ( ) ) ;
2024-06-26 01:07:18 -07:00
// 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 ,
2025-03-21 22:39:12 +13:00
text : r #" part001 = startSketchOn(XY)
2024-06-26 01:07:18 -07:00
| > ^ ^ ^ ^ startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] )
| > line ( end = [ - 20 , 0 ] )
| > close ( )
| > extrude ( length = 3.14 )
2024-12-10 06:27:04 +13:00
NEW_LINT = 1 " #
2024-06-26 01:07:18 -07:00
. to_string ( ) ,
} ] ,
} )
. await ;
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) ;
2024-06-26 01:07:18 -07:00
assert! ( ast . is_none ( ) ) ;
// Get the symbols map.
2024-06-27 15:43:49 -07:00
let symbols_map = server . symbols_map . get ( " file:///test.kcl " ) ;
2024-06-26 01:07:18 -07:00
assert! ( symbols_map . is_none ( ) ) ;
// Get the semantic tokens map.
2024-06-27 15:43:49 -07:00
let semantic_tokens_map = server . semantic_tokens_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2024-12-10 14:26:53 +13:00
assert! ( ! semantic_tokens_map . is_empty ( ) ) ;
2024-06-26 01:07:18 -07:00
// Assure we have diagnostics.
2024-12-06 13:57:31 +13:00
2024-06-26 01:07:18 -07:00
// Check the diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 1 ) ;
2024-06-26 01:07:18 -07:00
}
#[ tokio::test(flavor = " multi_thread " ) ]
2024-08-12 13:06:30 -07:00
async fn kcl_test_kcl_lsp_code_lint_reexecute_had_lint_new_execution_error ( ) {
2024-06-26 01:07:18 -07:00
let server = kcl_lsp_server ( true ) . await . unwrap ( ) ;
2024-12-10 06:27:04 +13:00
let code = r #" LINT = 1
2025-03-21 22:39:12 +13:00
part001 = startSketchOn ( XY )
2024-06-26 01:07:18 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] )
| > line ( end = [ 0 , 20 ] )
| > line ( end = [ - 20 , 0 ] )
| > close ( )
2024-06-26 01:07:18 -07:00
" #;
// 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 ;
// Assure we have diagnostics.
2024-12-06 13:57:31 +13:00
2024-06-26 01:07:18 -07:00
// Check the diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 1 ) ;
2024-06-26 01:07:18 -07:00
// Get the token map.
2024-06-27 15:43:49 -07:00
let token_map = server . token_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2024-12-10 14:26:53 +13:00
assert! ( ! token_map . is_empty ( ) ) ;
2024-06-26 01:07:18 -07:00
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-06-26 01:07:18 -07:00
// Get the symbols map.
2024-06-27 15:43:49 -07:00
let symbols_map = server . symbols_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2024-12-10 14:26:53 +13:00
assert! ( ! symbols_map . is_empty ( ) ) ;
2024-06-26 01:07:18 -07:00
// Get the semantic tokens map.
2024-06-27 15:43:49 -07:00
let semantic_tokens_map = server . semantic_tokens_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2024-12-10 14:26:53 +13:00
assert! ( ! semantic_tokens_map . is_empty ( ) ) ;
2024-06-26 01:07:18 -07:00
// 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 ,
2024-12-10 06:27:04 +13:00
text : r #" LINT = 1
2025-03-21 22:39:12 +13:00
part001 = startSketchOn ( XY )
2024-06-26 01:07:18 -07:00
| > startProfileAt ( [ - 10 , - 10 ] , % )
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
| > line ( end = [ 20 , 0 ] , tag = $seg01 )
| > line ( end = [ 0 , 20 ] , tag = $seg01 )
| > line ( end = [ - 20 , 0 ] )
| > close ( )
2024-06-26 01:07:18 -07:00
" #
. to_string ( ) ,
} ] ,
} )
. await ;
// Get the token map.
2024-06-27 15:43:49 -07:00
let token_map = server . token_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2024-12-10 14:26:53 +13:00
assert! ( ! token_map . is_empty ( ) ) ;
2024-06-26 01:07:18 -07:00
// Get the ast.
2024-06-27 15:43:49 -07:00
let ast = server . ast_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2025-02-27 14:34:01 -08:00
assert! ( ast . ast ! = Node ::< Program > ::default ( ) ) ;
2024-06-26 01:07:18 -07:00
// Get the symbols map.
2024-06-27 15:43:49 -07:00
let symbols_map = server . symbols_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2024-12-10 14:26:53 +13:00
assert! ( ! symbols_map . is_empty ( ) ) ;
2024-06-26 01:07:18 -07:00
// Get the semantic tokens map.
2024-06-27 15:43:49 -07:00
let semantic_tokens_map = server . semantic_tokens_map . get ( " file:///test.kcl " ) . unwrap ( ) . clone ( ) ;
2024-12-10 14:26:53 +13:00
assert! ( ! semantic_tokens_map . is_empty ( ) ) ;
2024-06-26 01:07:18 -07:00
// Assure we have diagnostics.
2024-12-06 13:57:31 +13:00
2024-06-26 01:07:18 -07:00
// Check the diagnostics.
2024-12-06 13:57:31 +13:00
assert_diagnostic_count ( server . diagnostics_map . get ( " file:///test.kcl " ) . as_deref ( ) , 2 ) ;
2024-06-26 01:07:18 -07:00
}
2024-10-28 20:52:51 -04:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn kcl_test_kcl_lsp_completions_number_literal ( ) {
let server = kcl_lsp_server ( false ) . await . unwrap ( ) ;
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 ,
2024-12-10 06:27:04 +13:00
text : " thing = 10 " . to_string ( ) ,
2024-10-28 20:52:51 -04:00
} ,
} )
. await ;
let completions = server
. completion ( tower_lsp ::lsp_types ::CompletionParams {
text_document_position : tower_lsp ::lsp_types ::TextDocumentPositionParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///test.kcl " . try_into ( ) . unwrap ( ) ,
} ,
2024-12-10 06:27:04 +13:00
position : tower_lsp ::lsp_types ::Position { line : 0 , character : 10 } ,
2024-10-28 20:52:51 -04:00
} ,
context : None ,
partial_result_params : Default ::default ( ) ,
work_done_progress_params : Default ::default ( ) ,
} )
. await
. unwrap ( ) ;
assert_eq! ( completions . is_none ( ) , true ) ;
}
2025-02-26 19:29:59 -08:00
#[ tokio::test(flavor = " multi_thread " ) ]
async fn kcl_test_kcl_lsp_multi_file_error ( ) {
let server = kcl_lsp_server ( true ) . await . unwrap ( ) ;
let cwd = std ::env ::current_dir ( ) . unwrap ( ) ;
let joined = cwd . join ( " tests/import_file_parse_error/ " ) ;
// Change the current directory.
std ::env ::set_current_dir ( joined ) . unwrap ( ) ;
let code = std ::fs ::read_to_string ( " input.kcl " ) . unwrap ( ) ;
// Send open file.
server
. did_open ( tower_lsp ::lsp_types ::DidOpenTextDocumentParams {
text_document : tower_lsp ::lsp_types ::TextDocumentItem {
uri : " file:///input.kcl " . try_into ( ) . unwrap ( ) ,
language_id : " kcl " . to_string ( ) ,
version : 1 ,
text : code . clone ( ) ,
} ,
} )
. await ;
// Send diagnostics request.
let diagnostics = server
. diagnostic ( tower_lsp ::lsp_types ::DocumentDiagnosticParams {
text_document : tower_lsp ::lsp_types ::TextDocumentIdentifier {
uri : " file:///input.kcl " . try_into ( ) . unwrap ( ) ,
} ,
partial_result_params : Default ::default ( ) ,
work_done_progress_params : Default ::default ( ) ,
identifier : None ,
previous_result_id : None ,
} )
. await
. unwrap ( ) ;
// Check the diagnostics.
if let tower_lsp ::lsp_types ::DocumentDiagnosticReportResult ::Report ( diagnostics ) = diagnostics {
if let tower_lsp ::lsp_types ::DocumentDiagnosticReport ::Full ( diagnostics ) = diagnostics {
assert_eq! ( diagnostics . full_document_diagnostic_report . items . len ( ) , 1 ) ;
let item = diagnostics . full_document_diagnostic_report . items . first ( ) . unwrap ( ) ;
assert_eq! ( item . message , " syntax: Unexpected token: } " ) ;
assert_eq! (
Some ( vec! [ tower_lsp ::lsp_types ::DiagnosticRelatedInformation {
location : tower_lsp ::lsp_types ::Location {
uri : " file:///parse-failure.kcl " . try_into ( ) . unwrap ( ) ,
range : tower_lsp ::lsp_types ::Range {
start : tower_lsp ::lsp_types ::Position { line : 1 , character : 9 } ,
end : tower_lsp ::lsp_types ::Position { line : 2 , character : 1 } ,
} ,
} ,
message : " syntax: Unexpected token: } " . to_string ( ) ,
} ] ) ,
item . related_information
) ;
} else {
panic! ( " Expected full diagnostics " ) ;
}
} else {
panic! ( " Expected diagnostics " ) ;
}
}