usse/scrape/venv/lib/python3.10/site-packages/esbonio/lsp/util/filepaths.py
2023-12-22 15:26:01 +01:00

134 lines
3.9 KiB
Python

import pathlib
from typing import Generator
from lsprotocol.types import CompletionItem
from lsprotocol.types import CompletionItemKind
from lsprotocol.types import Position
from lsprotocol.types import Range
from lsprotocol.types import TextEdit
from esbonio.lsp.rst import CompletionContext
def path_to_completion_item(
context: CompletionContext, path: pathlib.Path
) -> CompletionItem:
"""Create the ``CompletionItem`` for the given path.
In the case where there are multiple filepath components, this function needs to
provide an appropriate ``TextEdit`` so that the most recent entry in the path can
be easily edited - without clobbering the existing path.
Also bear in mind that this function must play nice with both role target and
directive argument completions.
"""
new_text = f"{path.name}"
kind = CompletionItemKind.Folder if path.is_dir() else CompletionItemKind.File
# If we can't find the '/' we may as well not bother with a `TextEdit` and let the
# `Roles` feature provide the default handling.
start = _find_start_char(context)
if start == -1:
insert_text = new_text
filter_text = None
text_edit = None
else:
start += 1
_, end = context.match.span()
prefix = context.match.group(0)[start:]
insert_text = None
filter_text = (
f"{prefix}{new_text}" # Needed so VSCode will actually show the results.
)
text_edit = TextEdit(
range=Range(
start=Position(line=context.position.line, character=start),
end=Position(line=context.position.line, character=end),
),
new_text=new_text,
)
return CompletionItem(
label=new_text,
kind=kind,
insert_text=insert_text,
filter_text=filter_text,
text_edit=text_edit,
)
def _find_start_char(context: CompletionContext) -> int:
matched_text = context.match.group(0)
idx = matched_text.find("/")
while True:
next_idx = matched_text.find("/", idx + 1)
if next_idx == -1:
break
idx = next_idx
return idx
def complete_filepaths(base: str, partial: str) -> Generator[pathlib.Path, None, None]:
"""Generate filepath completion suggestions relative to the given base.
This function is for "docutils style" behaviour where the path is relative to the
current document.
Parameters
----------
base
The directory containing the current document
partial
The existing path entered so far.
"""
candidate_dir = pathlib.Path(base) / pathlib.Path(partial)
if partial and not partial.endswith("/"):
candidate_dir = candidate_dir.parent
return candidate_dir.glob("*")
def complete_sphinx_filepaths(
srcdir: str, base: str, partial: str
) -> Generator[pathlib.Path, None, None]:
"""Generate filepath completion suggestions relative to the given base or ``srcdir``.
This function is for "sphinx style" behaviour where the path is relative to the
current document *unless* ``partial`` starts with a ``/`` character. In this case
completions should be relative to the given ``srcdir``.
Parameters
----------
srcdir
The ``srcdir`` of the project, used when ``partial`` starts with a ``/``.
base
The directory containing the current document.
partial
The existing path entered so far.
"""
if partial and partial.startswith("/"):
candidate_dir = pathlib.Path(srcdir)
# Be sure to take off the leading '/' character, otherwise the partial
# path will wipe out the srcdir when concatenated...
partial = partial[1:]
else:
candidate_dir = pathlib.Path(base)
candidate_dir /= pathlib.Path(partial)
if partial and not partial.endswith("/"):
candidate_dir = candidate_dir.parent
return candidate_dir.glob("*")