mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-14 12:17:02 +00:00
Possibility to restrict paths to search for
The LLM also tries passing the relative_path param to the tool
This commit is contained in:
1 parent
993df0c25c
commit
81e0a324a5
4 files changed
+63
-37
No files matched your search
@@ -20,10 +20,16 @@ class InMemorySerenaConfig(SerenaConfigBase):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
agent = SerenaAgent(project=REPO_ROOT)
|
||||
agent = SerenaAgent(project=REPO_ROOT, serena_config=InMemorySerenaConfig())
|
||||
|
||||
# apply a tool
|
||||
find_refs_tool = agent.get_tool(FindReferencingSymbolsTool)
|
||||
print("Finding the symbol 'SyncLanguageServer'\n")
|
||||
result = agent.execute_task(lambda: find_refs_tool.apply(name_path="SolidLanguageServer", relative_path="src/solidlsp/ls.py"))
|
||||
find_file_tool = agent.get_tool(FindFileTool)
|
||||
search_pattern_tool = agent.get_tool(SearchForPatternTool)
|
||||
|
||||
result = agent.execute_task(
|
||||
lambda: search_pattern_tool.apply(
|
||||
".*Pyright.*|.*Omnisharp.*", restrict_search_to_code_files=False, relative_path="src/solidlsp/ls.py"
|
||||
)
|
||||
)
|
||||
pprint(json.loads(result))
|
||||
+19
-14
@@ -2178,6 +2178,7 @@ class SearchForPatternTool(Tool):
|
||||
context_lines_after: int = 0,
|
||||
paths_include_glob: str | None = None,
|
||||
paths_exclude_glob: str | None = None,
|
||||
relative_path: str = "",
|
||||
restrict_search_to_code_files: bool = False,
|
||||
max_answer_chars: int = _DEFAULT_MAX_ANSWER_LENGTH,
|
||||
) -> str:
|
||||
@@ -2191,6 +2192,8 @@ class SearchForPatternTool(Tool):
|
||||
:param context_lines_after: Number of lines of context to include after each match
|
||||
:param paths_include_glob: optional glob pattern specifying files to include in the search; if not provided, search globally.
|
||||
:param paths_exclude_glob: optional glob pattern specifying files to exclude from the search (takes precedence over paths_include_glob).
|
||||
:param relative_path: only subpaths of this path (relative to the repo root) will be analyzed. If a path to a single
|
||||
file is passed, only that will be searched. The path must exist, otherwise a FileNotFoundError will be raised.
|
||||
:param max_answer_chars: if the output is longer than this number of characters,
|
||||
no content will be returned. Don't adjust unless there is really no other way to get the content
|
||||
required for the task. Instead, if the output is too long, you should
|
||||
@@ -2203,34 +2206,36 @@ class SearchForPatternTool(Tool):
|
||||
which is why it is the default.
|
||||
:return: A JSON object mapping file paths to lists of matched consecutive lines (with context, if requested).
|
||||
"""
|
||||
# this was previously a kwarg and was true by default
|
||||
# However, the LLM doesn't really know which files are taken into account by the language server
|
||||
# and which onees
|
||||
abs_path = os.path.join(self.get_project_root(), relative_path)
|
||||
if not os.path.exists(abs_path):
|
||||
raise FileNotFoundError(f"Relative path {relative_path} does not exist.")
|
||||
|
||||
if restrict_search_to_code_files:
|
||||
matches = self.language_server.search_files_for_pattern(
|
||||
pattern=pattern,
|
||||
relative_path=relative_path,
|
||||
context_lines_before=context_lines_before,
|
||||
context_lines_after=context_lines_after,
|
||||
paths_include_glob=paths_include_glob,
|
||||
paths_exclude_glob=paths_exclude_glob,
|
||||
)
|
||||
else:
|
||||
# we walk through all files in the project starting from the root
|
||||
project_root = self.get_project_root()
|
||||
rel_paths_to_search = []
|
||||
for root, dirs, files in os.walk(project_root):
|
||||
# don't explore ignored dirs
|
||||
dirs[:] = [d for d in dirs if not self.agent.path_is_gitignored(os.path.join(root, d))]
|
||||
for file in files:
|
||||
file_path = os.path.join(root, file)
|
||||
if not self.agent.path_is_gitignored(file_path):
|
||||
relative_path = os.path.relpath(file_path, project_root)
|
||||
rel_paths_to_search.append(relative_path)
|
||||
if os.path.isfile(abs_path):
|
||||
rel_paths_to_search = [relative_path]
|
||||
else:
|
||||
dirs, rel_paths_to_search = scan_directory(
|
||||
path=abs_path,
|
||||
recursive=True,
|
||||
is_ignored_dir=self.agent.path_is_gitignored,
|
||||
is_ignored_file=self.agent.path_is_gitignored,
|
||||
relative_to=self.get_project_root(),
|
||||
)
|
||||
# TODO (maybe): not super efficient to walk through the files again and filter if glob patterns are provided
|
||||
# but it probably never matters and this version required no further refactoring
|
||||
matches = search_files(
|
||||
rel_paths_to_search,
|
||||
pattern,
|
||||
root_path=self.get_project_root(),
|
||||
paths_include_glob=paths_include_glob,
|
||||
paths_exclude_glob=paths_exclude_glob,
|
||||
)
|
||||
|
||||
@@ -10,6 +10,7 @@ import pathlib
|
||||
import stat
|
||||
import subprocess
|
||||
import threading
|
||||
|
||||
from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
|
||||
+34
-20
@@ -5,7 +5,6 @@ import logging
|
||||
import os
|
||||
import pathlib
|
||||
import pickle
|
||||
import re
|
||||
import subprocess
|
||||
import threading
|
||||
from abc import ABC, abstractmethod
|
||||
@@ -20,6 +19,7 @@ import pathspec
|
||||
import tqdm
|
||||
|
||||
from serena.text_utils import MatchedConsecutiveLines, search_files
|
||||
from serena.util.file_system import match_path
|
||||
from solidlsp import ls_types
|
||||
from solidlsp.ls_config import Language, LanguageServerConfig
|
||||
from solidlsp.ls_exceptions import LanguageServerException
|
||||
@@ -697,11 +697,13 @@ class SolidLanguageServer(ABC):
|
||||
for ref in references
|
||||
]
|
||||
|
||||
def retrieve_full_file_content(self, relative_file_path: str) -> str:
|
||||
def retrieve_full_file_content(self, file_path: str) -> str:
|
||||
"""
|
||||
Retrieve the full content of the given file.
|
||||
"""
|
||||
with self.open_file(relative_file_path) as file_data:
|
||||
if os.path.isabs(file_path):
|
||||
file_path = os.path.relpath(file_path, self.repository_root_path)
|
||||
with self.open_file(file_path) as file_data:
|
||||
return file_data.contents
|
||||
|
||||
def retrieve_content_around_line(
|
||||
@@ -1188,8 +1190,11 @@ class SolidLanguageServer(ABC):
|
||||
symbol_body = symbol_body[symbol_start_column:]
|
||||
return symbol_body
|
||||
|
||||
def request_parsed_files(self) -> list[str]:
|
||||
"""Retrieves relative paths of all files analyzed by the Language Server."""
|
||||
def request_parsed_files(self, relative_path: str = "") -> list[str]:
|
||||
"""Retrieves relative paths of all files analyzed by the Language Server.
|
||||
|
||||
:param relative_path: will only retrieve files that are subpaths of this.
|
||||
"""
|
||||
if not self.server_started:
|
||||
self.logger.log(
|
||||
"request_parsed_files called before Language Server started",
|
||||
@@ -1197,23 +1202,30 @@ class SolidLanguageServer(ABC):
|
||||
)
|
||||
raise LanguageServerException("Language Server not started")
|
||||
rel_file_paths = []
|
||||
for root, dirs, files in os.walk(self.repository_root_path, followlinks=True):
|
||||
dirs[:] = [d for d in dirs if not self.is_ignored_path(os.path.join(root, d))]
|
||||
for file in files:
|
||||
rel_file_path = os.path.relpath(os.path.join(root, file), start=self.repository_root_path)
|
||||
try:
|
||||
if not self.is_ignored_path(rel_file_path):
|
||||
rel_file_paths.append(rel_file_path)
|
||||
except FileNotFoundError:
|
||||
self.logger.log(
|
||||
f"File {rel_file_path} not found (possibly due it being a symlink), skipping it in request_parsed_files",
|
||||
logging.WARNING,
|
||||
)
|
||||
return rel_file_paths
|
||||
start_path = os.path.join(self.repository_root_path, relative_path)
|
||||
if not os.path.exists(start_path):
|
||||
raise FileNotFoundError(f"Relative path {start_path} not found.")
|
||||
if os.path.isfile(start_path):
|
||||
return [relative_path]
|
||||
else:
|
||||
for root, dirs, files in os.walk(start_path, followlinks=True):
|
||||
dirs[:] = [d for d in dirs if not self.is_ignored_path(os.path.join(root, d))]
|
||||
for file in files:
|
||||
rel_file_path = os.path.relpath(os.path.join(root, file), start=self.repository_root_path)
|
||||
try:
|
||||
if not self.is_ignored_path(rel_file_path):
|
||||
rel_file_paths.append(rel_file_path)
|
||||
except FileNotFoundError:
|
||||
self.logger.log(
|
||||
f"File {rel_file_path} not found (possibly due it being a symlink), skipping it in request_parsed_files",
|
||||
logging.WARNING,
|
||||
)
|
||||
return rel_file_paths
|
||||
|
||||
def search_files_for_pattern(
|
||||
self,
|
||||
pattern: re.Pattern | str,
|
||||
pattern: str,
|
||||
relative_path: str = "",
|
||||
context_lines_before: int = 0,
|
||||
context_lines_after: int = 0,
|
||||
paths_include_glob: str | None = None,
|
||||
@@ -1223,6 +1235,7 @@ class SolidLanguageServer(ABC):
|
||||
Search for a pattern across all files analyzed by the Language Server.
|
||||
|
||||
:param pattern: Regular expression pattern to search for, either as a compiled Pattern or string
|
||||
:param relative_path:
|
||||
:param context_lines_before: Number of lines of context to include before each match
|
||||
:param context_lines_after: Number of lines of context to include after each match
|
||||
:param paths_include_glob: Glob pattern to filter which files to include in the search
|
||||
@@ -1232,11 +1245,12 @@ class SolidLanguageServer(ABC):
|
||||
if isinstance(pattern, str):
|
||||
pattern = re.compile(pattern)
|
||||
|
||||
relative_file_paths = self.request_parsed_files()
|
||||
relative_file_paths = self.request_parsed_files(relative_path=relative_path)
|
||||
return search_files(
|
||||
relative_file_paths,
|
||||
pattern,
|
||||
file_reader=self.retrieve_full_file_content,
|
||||
root_path=self.repository_root_path,
|
||||
context_lines_before=context_lines_before,
|
||||
context_lines_after=context_lines_after,
|
||||
paths_include_glob=paths_include_glob,
|
||||
|
||||
Reference in new issue
Block a user