mirror of
https://github.com/tiennm99/serena.git
synced 2026-10-14 08:18:36 +00:00
Merge pull request #225 from oraios/editing-improvements
Editing improvements
This commit is contained in:
24 files changed
+1113
-862
No files matched your search
@@ -13,6 +13,8 @@ Status of the `main` branch. Changes prior to the next official version change w
|
||||
Fixes:
|
||||
* Fix `ExecuteShellCommandTool` and `GetCurrentConfigTool` hanging on Windows
|
||||
* Fix project activation by name via `--project` not working (was broken in previous release)
|
||||
* Improve handling of indentation and newlines in symbolic editing tools
|
||||
* Fix that `insert_after_symbol` was failing for insertions at the end of a file that did not end with a newline
|
||||
|
||||
# 2025-06-20
|
||||
|
||||
|
||||
+3
-3
@@ -73,7 +73,7 @@ google = [
|
||||
Homepage = "https://github.com/oraios/serena"
|
||||
|
||||
[tool.hatch.build.targets.wheel]
|
||||
packages = ["src/serena", "src/multilspy", "src/interprompt", "src/solidlsp"]
|
||||
packages = ["src/serena", "src/interprompt", "src/solidlsp"]
|
||||
|
||||
[tool.black]
|
||||
line-length = 140
|
||||
@@ -82,7 +82,7 @@ target-version = [
|
||||
]
|
||||
exclude = '''
|
||||
/(
|
||||
src/multilspy
|
||||
src/solidlsp/language_servers/.*/static,
|
||||
)/
|
||||
'''
|
||||
|
||||
@@ -138,7 +138,7 @@ type-check = [
|
||||
target-version = "py311"
|
||||
line-length = 140
|
||||
exclude = [
|
||||
"src/multilspy",
|
||||
"src/solidlsp/language_servers/**/static",
|
||||
]
|
||||
|
||||
[tool.ruff.format]
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
[pytest]
|
||||
addopts = --snapshot-patch-pycharm-diff
|
||||
+10
-32
@@ -1693,27 +1693,16 @@ class ReplaceSymbolBodyTool(Tool, ToolMarkerCanEdit):
|
||||
r"""
|
||||
Replaces the body of the symbol with the given `name_path`.
|
||||
|
||||
Important:
|
||||
You don't need to provide an adjusted indentation,
|
||||
as the tool will automatically add the indentation of the original symbol to each line. For example,
|
||||
for replacing a method in python, you can just write (using the standard python indentation):
|
||||
body="def my_method_replacement(self, ...):\n first_line\n second_line...". So each line after the first line only has
|
||||
an indentation of 4 (the indentation relative to the first character),
|
||||
since the additional indentation will be added by the tool. Same for more deeply nested
|
||||
cases. You always only need to write the relative indentation to the first character of the first line, and that
|
||||
in turn should not have any indentation.
|
||||
ALWAYS REMEMBER TO USE THE CORRECT INDENTATION IN THE BODY!
|
||||
|
||||
:param name_path: for finding the symbol to replace, same logic as in the `find_symbol` tool.
|
||||
:param relative_path: the relative path to the file containing the symbol
|
||||
:param body: the new symbol body.
|
||||
|
||||
:param body: the new symbol body. Important: Begin directly with the symbol definition and provide no
|
||||
leading indentation for the first line (but do indent the rest of the body according to the context).
|
||||
"""
|
||||
self.symbol_manager.replace_body(
|
||||
name_path,
|
||||
relative_file_path=relative_path,
|
||||
body=body,
|
||||
use_same_indentation=True,
|
||||
use_same_indentation=False,
|
||||
)
|
||||
return SUCCESS_RESULT
|
||||
|
||||
@@ -1733,17 +1722,12 @@ class InsertAfterSymbolTool(Tool, ToolMarkerCanEdit):
|
||||
Inserts the given body/content after the end of the definition of the given symbol (via the symbol's location).
|
||||
A typical use case is to insert a new class, function, method, field or variable assignment.
|
||||
|
||||
:param name_path: for finding the symbol to insert after, same logic as in the `find_symbol` tool.
|
||||
:param name_path: name path of the symbol after which to insert content (definitions in the `find_symbol` tool apply)
|
||||
:param relative_path: the relative path to the file containing the symbol
|
||||
:param body: the body/content to be inserted. Important: the inserted code will automatically have the
|
||||
same indentation as the symbol's body, so you do not need to provide any additional indentation.
|
||||
:param body: the body/content to be inserted. The inserted code shall begin with the next line after
|
||||
the symbol.
|
||||
"""
|
||||
self.symbol_manager.insert_after_symbol(
|
||||
name_path,
|
||||
relative_file_path=relative_path,
|
||||
body=body,
|
||||
use_same_indentation=True,
|
||||
)
|
||||
self.symbol_manager.insert_after_symbol(name_path, relative_file_path=relative_path, body=body, use_same_indentation=False)
|
||||
return SUCCESS_RESULT
|
||||
|
||||
|
||||
@@ -1763,17 +1747,11 @@ class InsertBeforeSymbolTool(Tool, ToolMarkerCanEdit):
|
||||
A typical use case is to insert a new class, function, method, field or variable assignment.
|
||||
It also can be used to insert a new import statement before the first symbol in the file.
|
||||
|
||||
:param name_path: for finding the symbol to insert before, same logic as in the `find_symbol` tool.
|
||||
:param name_path: name path of the symbol before which to insert content (definitions in the `find_symbol` tool apply)
|
||||
:param relative_path: the relative path to the file containing the symbol
|
||||
:param body: the body/content to be inserted. Important: the inserted code will automatically have the
|
||||
same indentation as the symbol's body, so you do not need to provide any additional indentation.
|
||||
:param body: the body/content to be inserted before the line in which the referenced symbol is defined
|
||||
"""
|
||||
self.symbol_manager.insert_before_symbol(
|
||||
name_path,
|
||||
relative_file_path=relative_path,
|
||||
body=body,
|
||||
use_same_indentation=True,
|
||||
)
|
||||
self.symbol_manager.insert_before_symbol(name_path, relative_file_path=relative_path, body=body, use_same_indentation=False)
|
||||
return SUCCESS_RESULT
|
||||
|
||||
|
||||
|
||||
@@ -23,8 +23,7 @@ prompt: |
|
||||
use `find_symbol` with the name path `Foo/__init__` and `include_body=True`. If you don't know yet which methods in `Foo` you need to read or edit,
|
||||
you can use `find_symbol` with the name path `Foo`, `include_body=False` and `depth=1` to get all (top-level) methods of `Foo` before proceeding
|
||||
to read the desired methods with `include_body=True`.
|
||||
Note that you never need to add additional indentation, as all symbol editing tools will automatically add the indentation of the symbol that
|
||||
you are replacing or inserting above or below. In particular, keep in mind the description of the `replace_symbol_body` tool. If you want to add some new code at the end of the file, you should
|
||||
In particular, keep in mind the description of the `replace_symbol_body` tool. If you want to add some new code at the end of the file, you should
|
||||
use the `insert_after_symbol` tool with the last top-level symbol in the file. If you want to add an import, often a good strategy is to use
|
||||
`insert_before_symbol` with the first top-level symbol in the file.
|
||||
You can understand relationships between symbols by using the `find_referencing_symbols` tool. If not explicitly requested otherwise by a user,
|
||||
|
||||
+71
-48
@@ -1,7 +1,7 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
from collections.abc import Iterator, Sequence
|
||||
from collections.abc import Iterable, Iterator, Reversible, Sequence
|
||||
from contextlib import contextmanager
|
||||
from dataclasses import asdict, dataclass, field
|
||||
from difflib import SequenceMatcher
|
||||
@@ -256,6 +256,13 @@ class Symbol(ToStringMixin):
|
||||
def symbol_kind(self) -> SymbolKind:
|
||||
return self.symbol_root["kind"]
|
||||
|
||||
def is_neighbouring_definition_separated_by_empty_line(self) -> bool:
|
||||
"""
|
||||
:return: whether a symbol definition of this symbol's kind is usually separated from the
|
||||
previous/next definition by at least one empty line.
|
||||
"""
|
||||
return self.symbol_kind in (SymbolKind.Function, SymbolKind.Method, SymbolKind.Class, SymbolKind.Interface, SymbolKind.Struct)
|
||||
|
||||
@property
|
||||
def relative_path(self) -> str | None:
|
||||
location = self.symbol_root.get("location")
|
||||
@@ -689,26 +696,36 @@ class SymbolManager:
|
||||
if start_pos is None or end_pos is None:
|
||||
raise ValueError(f"Symbol at {location} does not have a defined body range.")
|
||||
start_line, start_col = start_pos["line"], start_pos["character"]
|
||||
|
||||
if use_same_indentation:
|
||||
indent = " " * start_col
|
||||
body_lines = body.splitlines()
|
||||
body = body_lines[0] + "\n" + "\n".join(indent + line for line in body_lines[1:])
|
||||
|
||||
# make sure body always ends with at least one newline
|
||||
if not body.endswith("\n"):
|
||||
body += "\n"
|
||||
# make sure the replacement adds no additional newlines (before or after) - all newlines
|
||||
# and whitespace before/after should remain the same, so we strip it entirely
|
||||
body = body.strip()
|
||||
|
||||
self._lang_server.delete_text_between_positions(location.relative_path, start_pos, end_pos)
|
||||
self._lang_server.insert_text_at_position(location.relative_path, start_line, start_col, body)
|
||||
|
||||
def insert_after_symbol(
|
||||
self,
|
||||
name_path: str,
|
||||
relative_file_path: str,
|
||||
body: str,
|
||||
*,
|
||||
use_same_indentation: bool = True,
|
||||
at_new_line: bool = True,
|
||||
) -> None:
|
||||
@staticmethod
|
||||
def _count_leading_newlines(text: Iterable) -> int:
|
||||
cnt = 0
|
||||
for c in text:
|
||||
if c == "\n":
|
||||
cnt += 1
|
||||
elif c == "\r":
|
||||
continue
|
||||
else:
|
||||
break
|
||||
return cnt
|
||||
|
||||
@classmethod
|
||||
def _count_trailing_newlines(cls, text: Reversible) -> int:
|
||||
return cls._count_leading_newlines(reversed(text))
|
||||
|
||||
def insert_after_symbol(self, name_path: str, relative_file_path: str, body: str, *, use_same_indentation: bool = True) -> None:
|
||||
"""
|
||||
Inserts content after the symbol with the given name in the given file.
|
||||
"""
|
||||
@@ -722,13 +739,9 @@ class SymbolManager:
|
||||
f"Found symbols at locations: \n" + json.dumps([s.location.to_dict() for s in symbol_candidates], indent=2)
|
||||
)
|
||||
symbol = symbol_candidates[-1]
|
||||
return self.insert_after_symbol_at_location(
|
||||
symbol.location, body, at_new_line=at_new_line, use_same_indentation=use_same_indentation
|
||||
)
|
||||
return self.insert_after_symbol_at_location(symbol.location, body, use_same_indentation=use_same_indentation)
|
||||
|
||||
def insert_after_symbol_at_location(
|
||||
self, location: SymbolLocation, body: str, *, at_new_line: bool = True, use_same_indentation: bool = True
|
||||
) -> None:
|
||||
def insert_after_symbol_at_location(self, location: SymbolLocation, body: str, *, use_same_indentation: bool = True) -> None:
|
||||
"""
|
||||
Appends content after the given symbol
|
||||
|
||||
@@ -750,12 +763,23 @@ class SymbolManager:
|
||||
if pos is None:
|
||||
raise ValueError(f"Symbol at {location} does not have a defined end position.")
|
||||
|
||||
line, col = pos["line"], pos["character"]
|
||||
if at_new_line:
|
||||
line += 1
|
||||
col = 0
|
||||
if not body.startswith("\n"):
|
||||
body = "\n" + body
|
||||
# start at the beginning of the next line
|
||||
col = 0
|
||||
line = pos["line"] + 1
|
||||
# make sure a suitable number of leading empty lines is used (at least 0/1 depending on the symbol type,
|
||||
# otherwise as many as the caller wanted to insert)
|
||||
original_leading_newlines = self._count_leading_newlines(body)
|
||||
body = body.lstrip("\r\n")
|
||||
min_empty_lines = 0
|
||||
if symbol.is_neighbouring_definition_separated_by_empty_line():
|
||||
min_empty_lines = 1
|
||||
num_leading_empty_lines = max(min_empty_lines, original_leading_newlines)
|
||||
if num_leading_empty_lines:
|
||||
body = ("\n" * num_leading_empty_lines) + body
|
||||
# make sure the one line break succeeding the original symbol, which we repurposed as prefix via
|
||||
# `line += 1`, is replaced
|
||||
body = body.rstrip("\r\n") + "\n"
|
||||
|
||||
if use_same_indentation:
|
||||
symbol_start_pos = symbol.body_start_position
|
||||
assert symbol_start_pos is not None, f"Symbol at {location=} does not have a defined start position."
|
||||
@@ -778,20 +802,11 @@ class SymbolManager:
|
||||
# > test test
|
||||
# > second line
|
||||
# > dataclass_instance.status = "active" # Reassign dataclass field
|
||||
col = 0
|
||||
|
||||
with self._edited_symbol_location(location):
|
||||
self._lang_server.insert_text_at_position(location.relative_path, line=line, column=col, text_to_be_inserted=body)
|
||||
|
||||
def insert_before_symbol(
|
||||
self,
|
||||
name_path: str,
|
||||
relative_file_path: str,
|
||||
body: str,
|
||||
*,
|
||||
at_new_line: bool = True,
|
||||
use_same_indentation: bool = True,
|
||||
) -> None:
|
||||
def insert_before_symbol(self, name_path: str, relative_file_path: str, body: str, *, use_same_indentation: bool = True) -> None:
|
||||
"""
|
||||
Inserts content before the symbol with the given name in the given file.
|
||||
"""
|
||||
@@ -805,11 +820,9 @@ class SymbolManager:
|
||||
f"Found symbols at locations: \n" + json.dumps([s.location.to_dict() for s in symbol_candidates], indent=2)
|
||||
)
|
||||
symbol = symbol_candidates[0]
|
||||
self.insert_before_symbol_at_location(symbol.location, body, at_new_line=at_new_line, use_same_indentation=use_same_indentation)
|
||||
self.insert_before_symbol_at_location(symbol.location, body, use_same_indentation=use_same_indentation)
|
||||
|
||||
def insert_before_symbol_at_location(
|
||||
self, location: SymbolLocation, body: str, *, at_new_line: bool = True, use_same_indentation: bool = True
|
||||
) -> None:
|
||||
def insert_before_symbol_at_location(self, location: SymbolLocation, body: str, *, use_same_indentation: bool = True) -> None:
|
||||
"""
|
||||
Inserts content before the given symbol
|
||||
|
||||
@@ -820,18 +833,28 @@ class SymbolManager:
|
||||
symbol_start_pos = symbol.body_start_position
|
||||
if symbol_start_pos is None:
|
||||
raise ValueError(f"Symbol at {location} does not have a defined start position.")
|
||||
line = symbol_start_pos["line"]
|
||||
col = symbol_start_pos["character"]
|
||||
|
||||
if use_same_indentation:
|
||||
indent = " " * (col)
|
||||
indent = " " * (symbol_start_pos["character"])
|
||||
body = "\n".join(indent + line for line in body.splitlines())
|
||||
|
||||
# similar problems as in insert_after_symbol_at_location, see comment there
|
||||
if at_new_line:
|
||||
col = 0
|
||||
line -= 1
|
||||
if not body.endswith("\n"):
|
||||
body += "\n"
|
||||
# insert position is the start of line where the symbol is defined
|
||||
line = symbol_start_pos["line"]
|
||||
col = 0
|
||||
|
||||
original_trailing_empty_lines = self._count_trailing_newlines(body) - 1
|
||||
|
||||
# ensure eol is present at end
|
||||
body = body.rstrip() + "\n"
|
||||
|
||||
# add suitable number of trailing empty lines after the body (at least 0/1 depending on the symbol type,
|
||||
# otherwise as many as the caller wanted to insert)
|
||||
min_trailing_empty_lines = 0
|
||||
if symbol.is_neighbouring_definition_separated_by_empty_line():
|
||||
min_trailing_empty_lines = 1
|
||||
num_trailing_newlines = max(min_trailing_empty_lines, original_trailing_empty_lines)
|
||||
body += "\n" * num_trailing_newlines
|
||||
|
||||
assert location.relative_path is not None
|
||||
|
||||
self._lang_server.insert_text_at_position(location.relative_path, line=line, column=col, text_to_be_inserted=body)
|
||||
|
||||
@@ -10,11 +10,11 @@ import stat
|
||||
import threading
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import FileUtils, PlatformUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
|
||||
|
||||
class ClangdLanguageServer(SolidLanguageServer):
|
||||
|
||||
@@ -5,9 +5,9 @@ import pathlib
|
||||
import stat
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import FileUtils, PlatformUtils
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
|
||||
|
||||
class DartLanguageServer(SolidLanguageServer):
|
||||
|
||||
@@ -16,12 +16,12 @@ from pathlib import PurePath
|
||||
from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
from solidlsp.ls_utils import FileUtils, PlatformUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.settings import SolidLSPSettings
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
|
||||
@@ -8,10 +8,10 @@ import threading
|
||||
from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
|
||||
|
||||
class Gopls(SolidLanguageServer):
|
||||
|
||||
@@ -13,11 +13,11 @@ from time import sleep
|
||||
from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import DefinitionParams, InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import PlatformId, PlatformUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import DefinitionParams, InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
|
||||
|
||||
class Intelephense(SolidLanguageServer):
|
||||
|
||||
@@ -10,10 +10,10 @@ import pathlib
|
||||
from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
|
||||
|
||||
class JediServer(SolidLanguageServer):
|
||||
|
||||
@@ -10,11 +10,11 @@ import pathlib
|
||||
import stat
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import FileUtils, PlatformUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
|
||||
@@ -13,12 +13,12 @@ from collections.abc import Iterable
|
||||
from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_exceptions import LanguageServerException
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import DotnetVersion, FileUtils, PlatformId, PlatformUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
|
||||
|
||||
def breadth_first_file_scan(root) -> Iterable[str]:
|
||||
|
||||
@@ -11,10 +11,10 @@ import threading
|
||||
from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
|
||||
|
||||
class PyrightServer(SolidLanguageServer):
|
||||
|
||||
@@ -12,11 +12,11 @@ import threading
|
||||
from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import FileUtils, PlatformUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
|
||||
|
||||
class RustAnalyzer(SolidLanguageServer):
|
||||
|
||||
@@ -13,10 +13,10 @@ import threading
|
||||
from typing import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
|
||||
|
||||
class Solargraph(SolidLanguageServer):
|
||||
|
||||
+2
-2
@@ -14,11 +14,11 @@ from time import sleep
|
||||
from overrides import override
|
||||
|
||||
from solidlsp.ls import SolidLanguageServer
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
from solidlsp.ls_config import LanguageServerConfig
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import PlatformId, PlatformUtils
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import InitializeParams
|
||||
from solidlsp.lsp_protocol_handler.server import ProcessLaunchInfo
|
||||
|
||||
# Platform-specific imports
|
||||
if os.name != "nt": # Unix-like systems
|
||||
|
||||
+7
-13
@@ -20,7 +20,11 @@ import tqdm
|
||||
|
||||
from serena.text_utils import MatchedConsecutiveLines, search_files
|
||||
from solidlsp import ls_types
|
||||
from solidlsp.ls_config import Language, LanguageServerConfig
|
||||
from solidlsp.ls_exceptions import LanguageServerException
|
||||
from solidlsp.ls_handler import SolidLanguageServerHandler
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import FileUtils, PathUtils, TextUtils
|
||||
from solidlsp.lsp_protocol_handler import lsp_types
|
||||
from solidlsp.lsp_protocol_handler import lsp_types as LSPTypes
|
||||
from solidlsp.lsp_protocol_handler.lsp_constants import LSPConstants
|
||||
@@ -30,10 +34,6 @@ from solidlsp.lsp_protocol_handler.server import (
|
||||
ProcessLaunchInfo,
|
||||
StringDict,
|
||||
)
|
||||
from solidlsp.ls_config import Language, LanguageServerConfig
|
||||
from solidlsp.ls_exceptions import LanguageServerException
|
||||
from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_utils import FileUtils, PathUtils, TextUtils
|
||||
|
||||
GenericDocumentSymbol = Union[LSPTypes.DocumentSymbol, LSPTypes.SymbolInformation, ls_types.UnifiedSymbolInformation]
|
||||
|
||||
@@ -409,9 +409,7 @@ class SolidLanguageServer(ABC):
|
||||
)
|
||||
del self.open_file_buffers[uri]
|
||||
|
||||
def insert_text_at_position(
|
||||
self, relative_file_path: str, line: int, column: int, text_to_be_inserted: str
|
||||
) -> ls_types.Position:
|
||||
def insert_text_at_position(self, relative_file_path: str, line: int, column: int, text_to_be_inserted: str) -> ls_types.Position:
|
||||
"""
|
||||
Insert text at the given line and column in the given file and return
|
||||
the updated cursor position after inserting the text.
|
||||
@@ -1035,9 +1033,7 @@ class SolidLanguageServer(ABC):
|
||||
lines = file_content.split("\n")
|
||||
end_line = len(lines)
|
||||
end_column = len(lines[-1])
|
||||
return ls_types.Range(
|
||||
start=ls_types.Position(line=0, character=0), end=ls_types.Position(line=end_line, character=end_column)
|
||||
)
|
||||
return ls_types.Range(start=ls_types.Position(line=0, character=0), end=ls_types.Position(line=end_line, character=end_column))
|
||||
|
||||
def request_dir_overview(self, relative_dir_path: str) -> dict[str, list[tuple[str, ls_types.SymbolKind, int, int]]]:
|
||||
"""
|
||||
@@ -1139,9 +1135,7 @@ class SolidLanguageServer(ABC):
|
||||
|
||||
# ----------------------------- FROM HERE ON MODIFICATIONS BY MISCHA --------------------
|
||||
|
||||
def retrieve_symbol_body(
|
||||
self, symbol: ls_types.UnifiedSymbolInformation | LSPTypes.DocumentSymbol | LSPTypes.SymbolInformation
|
||||
) -> str:
|
||||
def retrieve_symbol_body(self, symbol: ls_types.UnifiedSymbolInformation | LSPTypes.DocumentSymbol | LSPTypes.SymbolInformation) -> str:
|
||||
"""
|
||||
Load the body of the given symbol. If the body is already contained in the symbol, just return it.
|
||||
"""
|
||||
|
||||
@@ -12,6 +12,8 @@ from typing import Any
|
||||
|
||||
import psutil
|
||||
|
||||
from solidlsp.ls_exceptions import LanguageServerException
|
||||
from solidlsp.ls_request import LanguageServerRequest
|
||||
from solidlsp.lsp_protocol_handler.lsp_requests import LspNotification
|
||||
from solidlsp.lsp_protocol_handler.lsp_types import ErrorCodes
|
||||
from solidlsp.lsp_protocol_handler.server import (
|
||||
@@ -28,8 +30,6 @@ from solidlsp.lsp_protocol_handler.server import (
|
||||
make_request,
|
||||
make_response,
|
||||
)
|
||||
from solidlsp.ls_request import LanguageServerRequest
|
||||
from solidlsp.ls_exceptions import LanguageServerException
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -19,6 +19,10 @@ from solidlsp.ls_logger import LanguageServerLogger
|
||||
from solidlsp.ls_types import UnifiedSymbolInformation
|
||||
|
||||
|
||||
class InvalidTextLocationError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class TextUtils:
|
||||
"""
|
||||
Utilities for text operations.
|
||||
@@ -49,7 +53,8 @@ class TextUtils:
|
||||
"""
|
||||
idx = 0
|
||||
while line > 0:
|
||||
assert idx < len(text), (idx, len(text), text)
|
||||
if idx >= len(text):
|
||||
raise InvalidTextLocationError
|
||||
if text[idx] == "\n":
|
||||
line -= 1
|
||||
idx += 1
|
||||
@@ -88,7 +93,17 @@ class TextUtils:
|
||||
Inserts the given text at the given line and column.
|
||||
Returns the modified text and the new line and column.
|
||||
"""
|
||||
change_index = TextUtils.get_index_from_line_col(text, line, col)
|
||||
try:
|
||||
change_index = TextUtils.get_index_from_line_col(text, line, col)
|
||||
except InvalidTextLocationError:
|
||||
num_lines_in_text = text.count("\n") + 1
|
||||
max_line = num_lines_in_text - 1
|
||||
if line == max_line + 1 and col == 0: # trying to insert at new line after full text
|
||||
# insert at end, adding missing newline
|
||||
change_index = len(text)
|
||||
text_to_be_inserted = "\n" + text_to_be_inserted
|
||||
else:
|
||||
raise
|
||||
new_text = text[:change_index] + text_to_be_inserted + text[change_index:]
|
||||
new_l, new_c = TextUtils._get_updated_position_from_line_and_column_and_edit(line, col, text_to_be_inserted)
|
||||
return new_text, new_l, new_c
|
||||
|
||||
@@ -28,19 +28,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import dataclasses
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import threading
|
||||
from collections.abc import Callable
|
||||
from typing import Any, Union
|
||||
|
||||
import psutil
|
||||
|
||||
from ..ls_exceptions import LanguageServerException
|
||||
from .lsp_requests import LspNotification, LspRequest
|
||||
from .lsp_types import ErrorCodes
|
||||
|
||||
StringDict = dict[str, Any]
|
||||
@@ -127,4 +120,3 @@ def content_length(line: bytes) -> int | None:
|
||||
except ValueError:
|
||||
raise ValueError(f"Invalid Content-Length header: {value}")
|
||||
return None
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -134,6 +134,18 @@ def test_delete_symbol(test_case, snapshot):
|
||||
NEW_PYTHON_FUNCTION = """def new_inserted_function():
|
||||
print("This is a new function inserted before another.")"""
|
||||
|
||||
NEW_PYTHON_CLASS_WITH_LEADING_NEWLINES = """
|
||||
|
||||
class NewInsertedClass:
|
||||
pass
|
||||
"""
|
||||
|
||||
NEW_PYTHON_CLASS_WITH_TRAILING_NEWLINES = """class NewInsertedClass:
|
||||
pass
|
||||
|
||||
|
||||
"""
|
||||
|
||||
NEW_TYPESCRIPT_FUNCTION = """function newInsertedFunction(): void {
|
||||
console.log("This is a new function inserted before another.");
|
||||
}"""
|
||||
@@ -147,11 +159,13 @@ NEW_TYPESCRIPT_FUNCTION_AFTER = """function newFunctionAfterClass(): void {
|
||||
|
||||
|
||||
class InsertInRelToSymbolTest(EditingTest):
|
||||
def __init__(self, language: Language, rel_path: str, symbol_name: str, new_content: str):
|
||||
def __init__(
|
||||
self, language: Language, rel_path: str, symbol_name: str, new_content: str, mode: Literal["before", "after"] | None = None
|
||||
):
|
||||
super().__init__(language, rel_path)
|
||||
self.symbol_name = symbol_name
|
||||
self.new_content = new_content
|
||||
self.mode: Literal["before", "after"] | None = None
|
||||
self.mode: Literal["before", "after"] | None = mode
|
||||
|
||||
def set_mode(self, mode: Literal["before", "after"]):
|
||||
self.mode = mode
|
||||
@@ -159,9 +173,9 @@ class InsertInRelToSymbolTest(EditingTest):
|
||||
def _apply_edit(self, symbol_manager: SymbolManager) -> None:
|
||||
assert self.mode is not None
|
||||
if self.mode == "before":
|
||||
symbol_manager.insert_before_symbol(self.symbol_name, self.rel_path, self.new_content)
|
||||
symbol_manager.insert_before_symbol(self.symbol_name, self.rel_path, self.new_content, use_same_indentation=False)
|
||||
elif self.mode == "after":
|
||||
symbol_manager.insert_after_symbol(self.symbol_name, self.rel_path, self.new_content)
|
||||
symbol_manager.insert_after_symbol(self.symbol_name, self.rel_path, self.new_content, use_same_indentation=False)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("mode", ["before", "after"])
|
||||
@@ -211,18 +225,38 @@ def test_insert_in_rel_to_symbol(test_case: InsertInRelToSymbolTest, mode: Liter
|
||||
test_case.run_test(content_after_ground_truth=snapshot)
|
||||
|
||||
|
||||
PYTHON_REPLACED_BODY = """
|
||||
def modify_instance_var(self):
|
||||
# This body has been replaced
|
||||
self.instance_var = "Replaced!"
|
||||
self.reassignable_instance_var = 999
|
||||
@pytest.mark.python
|
||||
def test_insert_python_class_before(snapshot):
|
||||
InsertInRelToSymbolTest(
|
||||
Language.PYTHON,
|
||||
PYTHON_TEST_REL_FILE_PATH,
|
||||
"VariableDataclass",
|
||||
NEW_PYTHON_CLASS_WITH_TRAILING_NEWLINES,
|
||||
mode="before",
|
||||
).run_test(snapshot)
|
||||
|
||||
|
||||
@pytest.mark.python
|
||||
def test_insert_python_class_after(snapshot):
|
||||
InsertInRelToSymbolTest(
|
||||
Language.PYTHON,
|
||||
PYTHON_TEST_REL_FILE_PATH,
|
||||
"VariableDataclass",
|
||||
NEW_PYTHON_CLASS_WITH_LEADING_NEWLINES,
|
||||
mode="after",
|
||||
).run_test(snapshot)
|
||||
|
||||
|
||||
PYTHON_REPLACED_BODY = """def modify_instance_var(self):
|
||||
# This body has been replaced
|
||||
self.instance_var = "Replaced!"
|
||||
self.reassignable_instance_var = 999
|
||||
"""
|
||||
|
||||
TYPESCRIPT_REPLACED_BODY = """
|
||||
function printValue() {
|
||||
// This body has been replaced
|
||||
console.warn("New value: " + this.value);
|
||||
}
|
||||
TYPESCRIPT_REPLACED_BODY = """function printValue() {
|
||||
// This body has been replaced
|
||||
console.warn("New value: " + this.value);
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
@@ -233,7 +267,7 @@ class ReplaceBodyTest(EditingTest):
|
||||
self.new_body = new_body
|
||||
|
||||
def _apply_edit(self, symbol_manager: SymbolManager) -> None:
|
||||
symbol_manager.replace_body(self.symbol_name, self.rel_path, self.new_body)
|
||||
symbol_manager.replace_body(self.symbol_name, self.rel_path, self.new_body, use_same_indentation=False)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
Reference in new issue
Block a user