Skip to content

Commit 097c28b

Browse files
committed
fix(langserver): correct handling of imports containing backslashes, in RobotFramework you have to escape them
1 parent 22ef5f3 commit 097c28b

File tree

3 files changed

+101
-19
lines changed

3 files changed

+101
-19
lines changed

packages/language_server/src/robotcode/language_server/robotframework/diagnostics/library_doc.py

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,7 +1454,7 @@ def _find_library_internal(
14541454
robot_variables = resolve_robot_variables(working_dir, base_dir, command_line_variables, variables)
14551455

14561456
try:
1457-
name = robot_variables.replace_string(name.replace("\\", "\\\\"), ignore_errors=False)
1457+
name = robot_variables.replace_string(name, ignore_errors=False)
14581458
except DataError as error:
14591459
raise DataError(f"Replacing variables from setting 'Library' failed: {error}")
14601460

@@ -1518,6 +1518,7 @@ def get_library_doc(
15181518
) -> LibraryDoc:
15191519
import robot.running.testlibraries
15201520
from robot.libdocpkg.robotbuilder import KeywordDocBuilder
1521+
from robot.libraries import STDLIBS
15211522
from robot.output import LOGGER
15221523
from robot.output.loggerhelper import AbstractLogger
15231524
from robot.running.outputcapture import OutputCapturer
@@ -1604,7 +1605,14 @@ def get_test_library(
16041605
python_path=sys.path,
16051606
)
16061607

1607-
library_name = name
1608+
if name in STDLIBS and import_name.startswith(ROBOT_LIBRARY_PACKAGE + "."):
1609+
library_name = name
1610+
else:
1611+
if import_name.startswith(ROBOT_LIBRARY_PACKAGE + "."):
1612+
library_name = import_name[len(ROBOT_LIBRARY_PACKAGE + ".") :]
1613+
else:
1614+
library_name = import_name
1615+
16081616
library_name_path = Path(import_name)
16091617
if library_name_path.exists():
16101618
library_name = library_name_path.stem
@@ -1821,7 +1829,7 @@ def _find_variables_internal(
18211829
robot_variables = resolve_robot_variables(working_dir, base_dir, command_line_variables, variables)
18221830

18231831
try:
1824-
name = robot_variables.replace_string(name.replace("\\", "\\\\"), ignore_errors=False)
1832+
name = robot_variables.replace_string(name, ignore_errors=False)
18251833
except DataError as error:
18261834
raise DataError(f"Replacing variables from setting 'Variables' failed: {error}")
18271835

@@ -2094,7 +2102,7 @@ def find_file(
20942102

20952103
robot_variables = resolve_robot_variables(working_dir, base_dir, command_line_variables, variables)
20962104
try:
2097-
name = robot_variables.replace_string(name.replace("\\", "\\\\"), ignore_errors=False)
2105+
name = robot_variables.replace_string(name, ignore_errors=False)
20982106
except DataError as error:
20992107
raise DataError(f"Replacing variables from setting '{file_type}' failed: {error}")
21002108

@@ -2118,7 +2126,11 @@ class CompleteResult(NamedTuple):
21182126

21192127

21202128
def is_file_like(name: Optional[str]) -> bool:
2121-
return name is not None and (name.startswith((".", "/", os.sep)) or "/" in name or os.sep in name)
2129+
if name is None:
2130+
return False
2131+
2132+
base, filename = os.path.split(name)
2133+
return name.startswith(".") or bool(base) and filename != name
21222134

21232135

21242136
def iter_module_names(name: Optional[str] = None) -> Iterator[str]:
@@ -2154,13 +2166,18 @@ def iter_modules_from_python_path(
21542166

21552167
path = path.replace(".", os.sep) if path is not None and not path.startswith((".", "/", os.sep)) else path
21562168

2169+
needs_init = False
21572170
if path is None:
21582171
paths = sys.path
21592172
else:
21602173
paths = [str(Path(s, path)) for s in sys.path]
2174+
needs_init = True
21612175

21622176
for e in [Path(p) for p in set(paths)]:
21632177
if e.is_dir():
2178+
if needs_init and not (e / "__init__.py").is_file():
2179+
continue
2180+
21642181
for f in e.iterdir():
21652182
if not f.name.startswith(("_", ".")) and (
21662183
f.is_file()
@@ -2199,12 +2216,14 @@ def complete_library_import(
21992216
if name is not None:
22002217
robot_variables = resolve_robot_variables(working_dir, base_dir, command_line_variables, variables)
22012218

2202-
name = robot_variables.replace_string(name.replace("\\", "\\\\"), ignore_errors=True)
2219+
name = robot_variables.replace_string(name, ignore_errors=True)
2220+
2221+
file_like = is_file_like(name)
22032222

2204-
if name is None or not name.startswith((".", "/", os.sep)):
2223+
if name is None or not file_like:
22052224
result += list(iter_modules_from_python_path(name))
22062225

2207-
if name is None or (is_file_like(name) and (name.endswith(("/", os.sep)))):
2226+
if name is None or file_like:
22082227
name_path = Path(name if name else base_dir)
22092228
if name_path.is_absolute():
22102229
path = name_path
@@ -2264,7 +2283,7 @@ def complete_resource_import(
22642283
if name is not None:
22652284
robot_variables = resolve_robot_variables(working_dir, base_dir, command_line_variables, variables)
22662285

2267-
name = robot_variables.replace_string(name.replace("\\", "\\\\"), ignore_errors=True)
2286+
name = robot_variables.replace_string(name, ignore_errors=True)
22682287

22692288
if name is None or not name.startswith(".") and not name.startswith("/") and not name.startswith(os.sep):
22702289
result += list(iter_resources_from_python_path(name))
@@ -2299,14 +2318,18 @@ def iter_variables_from_python_path(
22992318

23002319
path = path.replace(".", os.sep) if path is not None and not path.startswith((".", "/", os.sep)) else path
23012320

2321+
needs_init = False
23022322
if path is None:
23032323
paths = sys.path
23042324
else:
23052325
paths = [str(Path(s, path)) for s in sys.path]
2326+
needs_init = True
23062327

23072328
for e in [Path(p) for p in set(paths)]:
23082329
if e.is_dir():
23092330
for f in e.iterdir():
2331+
if needs_init and not (e / "__init__.py").is_file():
2332+
continue
23102333
if not f.name.startswith(("_", ".")) and (
23112334
f.is_file()
23122335
and f.suffix in ALLOWED_VARIABLES_FILE_EXTENSIONS
@@ -2360,12 +2383,14 @@ def complete_variables_import(
23602383
if name is not None:
23612384
robot_variables = resolve_robot_variables(working_dir, base_dir, command_line_variables, variables)
23622385

2363-
name = robot_variables.replace_string(name.replace("\\", "\\\\"), ignore_errors=True)
2386+
name = robot_variables.replace_string(name, ignore_errors=True)
23642387

2365-
if name is None or not name.startswith(".") and not name.startswith("/") and not name.startswith(os.sep):
2388+
file_like = is_file_like(name)
2389+
2390+
if name is None or not file_like:
23662391
result += list(iter_variables_from_python_path(name))
23672392

2368-
if name is None or name.startswith((".", "/", os.sep)):
2393+
if name is None or file_like:
23692394
name_path = Path(name if name else base_dir)
23702395
if name_path.is_absolute():
23712396
path = name_path.resolve()

packages/language_server/src/robotcode/language_server/robotframework/parts/code_action_documentation.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,9 +412,9 @@ async def build_url(
412412
variables=await namespace.get_resolvable_variables(),
413413
)
414414
try:
415-
name = robot_variables.replace_string(name.replace("\\", "\\\\"), ignore_errors=False)
415+
name = robot_variables.replace_string(name, ignore_errors=False)
416416

417-
args = tuple(robot_variables.replace_string(v.replace("\\", "\\\\"), ignore_errors=False) for v in args)
417+
args = tuple(robot_variables.replace_string(v, ignore_errors=False) for v in args)
418418

419419
except (SystemExit, KeyboardInterrupt):
420420
raise

packages/language_server/src/robotcode/language_server/robotframework/parts/completion.py

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1520,16 +1520,36 @@ async def complete_import() -> Optional[List[CompletionItem]]:
15201520
if text_before_position != "" and all(c == "." for c in text_before_position):
15211521
return None
15221522

1523+
if "/" in name_token.value or os.sep in name_token.value:
1524+
part_splitter = ["/", os.sep]
1525+
else:
1526+
part_splitter = ["."]
1527+
1528+
reversed_text_before_position = list(reversed(text_before_position))
15231529
last_separator_index = (
15241530
len(text_before_position)
1525-
- next((i for i, c in enumerate(reversed(text_before_position)) if c in [".", "/", os.sep]), -1)
1531+
- next(
1532+
(
1533+
i
1534+
for i, c in enumerate(reversed_text_before_position)
1535+
if c in part_splitter
1536+
and (
1537+
c != "\\"
1538+
or (
1539+
c == "\\"
1540+
and len(reversed_text_before_position) > i + 1
1541+
and reversed_text_before_position[i + 1] == "\\"
1542+
)
1543+
)
1544+
),
1545+
-1,
1546+
)
15261547
- 1
15271548
)
15281549

15291550
first_part = (
15301551
text_before_position[
1531-
: last_separator_index
1532-
+ (1 if text_before_position[last_separator_index] in [".", "/", os.sep] else 0)
1552+
: last_separator_index + (1 if text_before_position[last_separator_index] in part_splitter else 0)
15331553
]
15341554
if last_separator_index < len(text_before_position)
15351555
else None
@@ -1693,9 +1713,25 @@ async def complete_ResourceImport( # noqa: N802
16931713
if text_before_position != "" and all(c == "." for c in text_before_position):
16941714
return None
16951715

1716+
reversed_text_before_position = list(reversed(text_before_position))
16961717
last_separator_index = (
16971718
len(text_before_position)
1698-
- next((i for i, c in enumerate(reversed(text_before_position)) if c in ["/", os.sep]), -1)
1719+
- next(
1720+
(
1721+
i
1722+
for i, c in enumerate(reversed_text_before_position)
1723+
if c in ["/", os.sep]
1724+
and (
1725+
c != "\\"
1726+
or (
1727+
c == "\\"
1728+
and len(reversed_text_before_position) > i + 1
1729+
and reversed_text_before_position[i + 1] == "\\"
1730+
)
1731+
)
1732+
),
1733+
-1,
1734+
)
16991735
- 1
17001736
)
17011737

@@ -1795,9 +1831,30 @@ async def complete_import() -> Optional[List[CompletionItem]]:
17951831
if text_before_position != "" and all(c == "." for c in text_before_position):
17961832
return None
17971833

1834+
if "/" in name_token.value or os.sep in name_token.value:
1835+
part_splitter = ["/", os.sep]
1836+
else:
1837+
part_splitter = ["."]
1838+
1839+
reversed_text_before_position = list(reversed(text_before_position))
17981840
last_separator_index = (
17991841
len(text_before_position)
1800-
- next((i for i, c in enumerate(reversed(text_before_position)) if c in ["/", os.sep]), -1)
1842+
- next(
1843+
(
1844+
i
1845+
for i, c in enumerate(reversed_text_before_position)
1846+
if c in part_splitter
1847+
and (
1848+
c != "\\"
1849+
or (
1850+
c == "\\"
1851+
and len(reversed_text_before_position) > i + 1
1852+
and reversed_text_before_position[i + 1] == "\\"
1853+
)
1854+
)
1855+
),
1856+
-1,
1857+
)
18011858
- 1
18021859
)
18031860

0 commit comments

Comments
 (0)