diff --git a/iii-lsp/src/analyzer.rs b/iii-lsp/src/analyzer.rs index 7a83143..821463d 100644 --- a/iii-lsp/src/analyzer.rs +++ b/iii-lsp/src/analyzer.rs @@ -72,7 +72,7 @@ fn is_method_access(kind: &str) -> bool { /// Method names that map to `trigger()` fn is_trigger_method(name: &str) -> bool { - name == "trigger" + name == "trigger" || name == "trigger_async" } /// Method names that map to `registerTrigger()` / `register_trigger()` @@ -902,6 +902,14 @@ mod tests { assert_eq!(result.current_text, "todos::create"); } + #[test] + fn py_trigger_async_function_id_dict() { + let source = "await iii.trigger_async({'function_id': 'todos::create'})"; + let result = analyze(source, pos(0, 41), Language::Python); + assert_eq!(result.context, CompletionContext::FunctionId); + assert_eq!(result.current_text, "todos::create"); + } + #[test] fn py_register_trigger_type_dict() { let source = "iii.register_trigger({'type': 'http', 'function_id': 'x'})"; @@ -928,6 +936,18 @@ mod tests { ); } + #[test] + fn py_payload_async_dict() { + let source = "await iii.trigger_async({'function_id': 'x', 'payload': {}})"; + let result = analyze(source, pos(0, 57), Language::Python); + assert_eq!( + result.context, + CompletionContext::PayloadProperty { + function_id: "x".to_string() + } + ); + } + #[test] fn py_trigger_config_dict() { let source = "iii.register_trigger({'type': 'http', 'function_id': 'x', 'config': {}})"; @@ -950,6 +970,14 @@ mod tests { assert_eq!(result.current_text, "todos::create"); } + #[test] + fn py_trigger_async_function_id_kwarg() { + let source = "await iii.trigger_async(function_id='todos::create')"; + let result = analyze(source, pos(0, 37), Language::Python); + assert_eq!(result.context, CompletionContext::FunctionId); + assert_eq!(result.current_text, "todos::create"); + } + #[test] fn py_register_trigger_type_kwarg() { let source = "iii.register_trigger(type='http', function_id='x')"; @@ -1076,4 +1104,22 @@ mod tests { let result = analyze(source, pos(2, 34), Language::Python); assert_eq!(result.context, CompletionContext::FunctionId); } + + #[test] + fn py_real_file_unclosed_string_async_dict() { + // Line 2: "result = await iii.trigger_async({'function_id': '" = 50 chars + let source = + "from iii import iii\n\nresult = await iii.trigger_async({'function_id': '\n\nx = 1\n"; + let result = analyze(source, pos(2, 50), Language::Python); + assert_eq!(result.context, CompletionContext::FunctionId); + } + + #[test] + fn py_real_file_unclosed_string_async_kwarg() { + // Line 2: "result = await iii.trigger_async(function_id='" = 46 chars + let source = + "from iii import iii\n\nresult = await iii.trigger_async(function_id='\n\nx = 1\n"; + let result = analyze(source, pos(2, 46), Language::Python); + assert_eq!(result.context, CompletionContext::FunctionId); + } } diff --git a/iii-lsp/src/diagnostics.rs b/iii-lsp/src/diagnostics.rs index 7a721ae..b051ebc 100644 --- a/iii-lsp/src/diagnostics.rs +++ b/iii-lsp/src/diagnostics.rs @@ -127,7 +127,7 @@ fn extract_method_name(call: Node, source: &str) -> Option { } fn is_trigger_method(name: &str) -> bool { - name == "trigger" + name == "trigger" || name == "trigger_async" } fn is_register_trigger_method(name: &str) -> bool { @@ -732,6 +732,18 @@ mod tests { assert_eq!(calls[0].payload_keys, vec!["title"]); } + #[test] + fn py_finds_trigger_async_calls_dict() { + let source = + "await iii.trigger_async({'function_id': 'todos::create', 'payload': {'title': 'test'}})"; + let tree = parse_py(source); + let (calls, _) = find_all_calls(tree.root_node(), source); + assert_eq!(calls.len(), 1); + assert_eq!(calls[0].function_id, "todos::create"); + assert!(calls[0].has_payload); + assert_eq!(calls[0].payload_keys, vec!["title"]); + } + #[test] fn py_finds_register_trigger_dict() { let source = "iii.register_trigger({'type': 'http', 'function_id': 'x', 'config': {'api_path': '/test'}})"; @@ -755,6 +767,18 @@ mod tests { assert_eq!(calls[0].payload_keys, vec!["title"]); } + #[test] + fn py_finds_trigger_async_calls_kwarg() { + let source = + "await iii.trigger_async(function_id='todos::create', payload={'title': 'test'})"; + let tree = parse_py(source); + let (calls, _) = find_all_calls(tree.root_node(), source); + assert_eq!(calls.len(), 1); + assert_eq!(calls[0].function_id, "todos::create"); + assert!(calls[0].has_payload); + assert_eq!(calls[0].payload_keys, vec!["title"]); + } + #[test] fn py_finds_register_trigger_kwarg() { let source = diff --git a/registry/index.json b/registry/index.json index 37e2a7d..910ec31 100644 --- a/registry/index.json +++ b/registry/index.json @@ -22,6 +22,16 @@ }, "version": "0.1.2" }, + "iii-lsp": { + "type": "binary", + "description": "III Language Server — autocompletion and hover for III engine functions and triggers", + "repo": "iii-hq/workers", + "tag_prefix": "iii-lsp", + "supported_targets": ["aarch64-apple-darwin", "x86_64-unknown-linux-gnu"], + "has_checksum": true, + "default_config": {}, + "version": "0.1.0" + }, "todo-worker": { "description": "Quickstart CRUD todo worker using the Node.js iii SDK", "image": "docker.io/andersonofl/todo-worker",