diff --git a/.vscode/launch.json b/.vscode/launch.json index fd0075e..03d17d7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -17,6 +17,19 @@ ], "cwd": "${env:TEMP}/nx-post-support-vscode-debug", "outFiles": ["${env:TEMP}/nx-post-support-vscode-debug/dist/**/*.js"], + "linux": { + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "${workspaceFolder}", + "${workspaceFolder}/test/test.tcl" + ], + "cwd": "${workspaceFolder}", + "outFiles": ["${workspaceFolder}/dist/**/*.js"], + "resolveSourceMapLocations": [ + "${workspaceFolder}/dist/**/*.js", + "!**/node_modules/**" + ] + }, "sourceMaps": true, "resolveSourceMapLocations": [ "${env:TEMP}/nx-post-support-vscode-debug/dist/**/*.js", @@ -50,6 +63,19 @@ ], "cwd": "${env:TEMP}/nx-post-support-vscode-debug", "outFiles": ["${env:TEMP}/nx-post-support-vscode-debug/dist/**/*.js"], + "linux": { + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "${workspaceFolder}", + "${workspaceFolder}/test/test.tcl" + ], + "cwd": "${workspaceFolder}", + "outFiles": ["${workspaceFolder}/dist/**/*.js"], + "resolveSourceMapLocations": [ + "${workspaceFolder}/dist/**/*.js", + "!**/node_modules/**" + ] + }, "sourceMaps": true, "resolveSourceMapLocations": [ "${env:TEMP}/nx-post-support-vscode-debug/dist/**/*.js", diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 9fc060b..fb7f391 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -15,6 +15,13 @@ "${workspaceFolder}", "${env:TEMP}\\nx-post-support-vscode-debug" ], + "linux": { + "command": "npm", + "args": ["run", "compile:debug"], + "options": { + "cwd": "${workspaceFolder}" + } + }, "problemMatcher": [], "presentation": { "reveal": "always", diff --git a/server/src/tools/tcl_command_completion.py b/server/src/tools/tcl_command_completion.py index 06f099e..b96caeb 100644 --- a/server/src/tools/tcl_command_completion.py +++ b/server/src/tools/tcl_command_completion.py @@ -1009,7 +1009,13 @@ def _current_command_segment(line_prefix: str) -> str: starts.append(brace_starts[-1] + 1) if bracket_starts: starts.append(bracket_starts[-1] + 1) - return line_prefix[max(starts) :] + start = max(starts) + if brace_starts and start == brace_starts[-1] + 1: + # An unfinished braced body or expression can itself contain commands + # (for example, ``if {[string compare ...``). Scan that inner context + # independently, while keeping completed braced arguments opaque. + return _current_command_segment(line_prefix[start:]) + return line_prefix[start:] def _tokenize_command_segment(segment: str) -> list[str]: diff --git a/server/tests/python_tests/test_completion_context.py b/server/tests/python_tests/test_completion_context.py index f0c7346..b6a8d30 100644 --- a/server/tests/python_tests/test_completion_context.py +++ b/server/tests/python_tests/test_completion_context.py @@ -204,6 +204,29 @@ def test_string_completion_handles_nested_commands_and_is_values(): } +def test_string_completion_inside_braced_conditions_and_bodies(): + for prefix in ("if {", "while {", "proc example {} { if {"): + subcommands = _argument_completion_labels(prefix + "[string ") + assert subcommands is not None + assert {"compare", "equal", "is"} <= subcommands + assert _argument_completion_labels(prefix + "[string compare -") == { + "-length", "-nocase" + } + assert _argument_completion_labels( + prefix + "[string compare -nocase " + ) == {"-length"} + + +def test_closed_braced_arguments_do_not_change_completion_context(): + assert _argument_completion_labels("puts {[string compare }") is None + assert _argument_completion_labels( + "if {[string equal a b]} {string compare " + ) == {"-length", "-nocase"} + assert _argument_completion_labels( + "if {[string equal a b] && [string is integer " + ) == {"-failindex", "-strict"} + + def test_dict_array_namespace_file_and_info_subcommands(): dict_items = _argument_completion_labels("dict ") assert dict_items is not None