Skip to content

Commit 92a4826

Browse files
author
Zhe Yu
committed
feat(cli): Support using LSP workspace folder as default project root
1 parent cd79d07 commit 92a4826

2 files changed

Lines changed: 52 additions & 1 deletion

File tree

‎src/vectorcode/lsp_main.py‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import traceback
88
import uuid
99
from typing import cast
10+
from urllib.parse import urlparse
1011

1112
import shtab
1213
from chromadb.types import Where
@@ -88,6 +89,15 @@ async def execute_command(ls: LanguageServer, args: list[str]):
8889
parsed_args = await parse_cli_args(args)
8990
logger.info("Parsed command arguments: %s", parsed_args)
9091
if parsed_args.project_root is None:
92+
workspace_folders = ls.workspace.folders
93+
if len(workspace_folders.keys()) == 1:
94+
_, workspace_folder = workspace_folders.popitem()
95+
lsp_dir = urlparse(workspace_folder.uri).path
96+
if os.path.isdir(lsp_dir):
97+
logger.debug(f"Using LSP workspace {lsp_dir} as project root.")
98+
DEFAULT_PROJECT_ROOT = lsp_dir
99+
elif len(workspace_folders) > 1: # pragma: nocover
100+
logger.info("Too many LSP workspace folders. Ignoring them...")
91101
if DEFAULT_PROJECT_ROOT is not None:
92102
parsed_args.project_root = DEFAULT_PROJECT_ROOT
93103
logger.warning("Using DEFAULT_PROJECT_ROOT: %s", DEFAULT_PROJECT_ROOT)

‎tests/test_lsp.py‎

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from unittest.mock import AsyncMock, MagicMock, patch
44

55
import pytest
6+
from lsprotocol.types import WorkspaceFolder
67
from pygls.exceptions import JsonRpcInternalError, JsonRpcInvalidRequest
78
from pygls.server import LanguageServer
89

@@ -20,6 +21,7 @@ def mock_language_server():
2021
ls.progress.create_async = AsyncMock()
2122
ls.progress.begin = MagicMock()
2223
ls.progress.end = MagicMock()
24+
ls.workspace = MagicMock()
2325
return ls
2426

2527

@@ -92,7 +94,6 @@ async def test_execute_command_query_default_proj_root(
9294
patch("builtins.open", MagicMock()) as mock_open,
9395
):
9496
global DEFAULT_PROJECT_ROOT
95-
9697
mock_config.project_root = None
9798
mock_parse_cli_args.return_value = mock_config
9899
mock_get_query_result_files.return_value = ["/test/file.txt"]
@@ -115,6 +116,46 @@ async def test_execute_command_query_default_proj_root(
115116
mock_language_server.progress.end.assert_called()
116117

117118

119+
@pytest.mark.asyncio
120+
async def test_execute_command_query_workspace_dir(mock_language_server, mock_config):
121+
workspace_folder = WorkspaceFolder(uri="file:///dummy_dir", name="dummy_dir")
122+
with (
123+
patch(
124+
"vectorcode.lsp_main.parse_cli_args", new_callable=AsyncMock
125+
) as mock_parse_cli_args,
126+
patch("vectorcode.lsp_main.ClientManager"),
127+
patch("vectorcode.lsp_main.get_collection", new_callable=AsyncMock),
128+
patch(
129+
"vectorcode.lsp_main.build_query_results", new_callable=AsyncMock
130+
) as mock_get_query_result_files,
131+
patch("os.path.isfile", return_value=True),
132+
patch("os.path.isdir", return_value=True),
133+
patch("builtins.open", MagicMock()) as mock_open,
134+
):
135+
mock_language_server.workspace = MagicMock()
136+
mock_language_server.workspace.folders = {"dummy_dir": workspace_folder}
137+
mock_config.project_root = None
138+
mock_parse_cli_args.return_value = mock_config
139+
mock_get_query_result_files.return_value = ["/test/file.txt"]
140+
141+
# Configure the MagicMock object to return a string when read() is called
142+
mock_file = MagicMock()
143+
mock_file.__enter__.return_value.read.return_value = "{}" # Return valid JSON
144+
mock_open.return_value = mock_file
145+
146+
# Mock the merge_from method
147+
mock_config.merge_from = AsyncMock(return_value=mock_config)
148+
149+
result = await execute_command(mock_language_server, ["query", "test"])
150+
151+
assert isinstance(result, list)
152+
mock_language_server.progress.begin.assert_called()
153+
mock_language_server.progress.end.assert_called()
154+
assert (
155+
mock_get_query_result_files.call_args.args[1].project_root == "/dummy_dir"
156+
)
157+
158+
118159
@pytest.mark.asyncio
119160
async def test_execute_command_ls(mock_language_server, mock_config):
120161
mock_config.action = CliAction.ls

0 commit comments

Comments
 (0)