Merge branch 'main' of github.com:oraios/serena

This commit is contained in:
Dominik Jain committed 2025-06-23 20:47:59 +02:00
commit 725f252152
24 files changed
+1113 -862

No files matched your search

+2
View File
@@ -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
View File
@@ -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]
+2
View File
@@ -0,0 +1,2 @@
[pytest]
addopts = --snapshot-patch-pycharm-diff
+10 -32
View File
@@ -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
View File
@@ -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
+2 -2
View File
@@ -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):
@@ -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
View File
@@ -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.
"""
+2 -2
View File
@@ -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__)
+17 -2
View File
@@ -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
+49 -15
View File
@@ -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(