115 lines
3.4 KiB
Python
115 lines
3.4 KiB
Python
|
import os.path
|
||
|
import pathlib
|
||
|
from typing import List
|
||
|
from typing import Optional
|
||
|
from typing import Tuple
|
||
|
|
||
|
import pygls.uris as Uri
|
||
|
from lsprotocol.types import CompletionItem
|
||
|
from lsprotocol.types import Location
|
||
|
from lsprotocol.types import Position
|
||
|
from lsprotocol.types import Range
|
||
|
from pygls.workspace import Document
|
||
|
|
||
|
from esbonio.lsp.directives import Directives
|
||
|
from esbonio.lsp.rst import CompletionContext
|
||
|
from esbonio.lsp.rst import DefinitionContext
|
||
|
from esbonio.lsp.rst import DocumentLinkContext
|
||
|
from esbonio.lsp.sphinx import SphinxLanguageServer
|
||
|
from esbonio.lsp.util.filepaths import complete_sphinx_filepaths
|
||
|
from esbonio.lsp.util.filepaths import path_to_completion_item
|
||
|
|
||
|
|
||
|
class Images:
|
||
|
def __init__(self, rst: SphinxLanguageServer):
|
||
|
self.rst = rst
|
||
|
self.logger = rst.logger.getChild(self.__class__.__name__)
|
||
|
|
||
|
def complete_arguments(
|
||
|
self, context: CompletionContext, domain: str, name: str
|
||
|
) -> List[CompletionItem]:
|
||
|
if domain or name not in {"figure", "image"}:
|
||
|
return []
|
||
|
|
||
|
if not self.rst.app:
|
||
|
return []
|
||
|
|
||
|
srcdir = self.rst.app.srcdir
|
||
|
partial = context.match.group("argument")
|
||
|
base = os.path.dirname(Uri.to_fs_path(context.doc.uri))
|
||
|
items = complete_sphinx_filepaths(srcdir, base, partial) # type: ignore[arg-type]
|
||
|
|
||
|
return [path_to_completion_item(context, p) for p in items]
|
||
|
|
||
|
def find_definitions(
|
||
|
self,
|
||
|
context: DefinitionContext,
|
||
|
directive: str,
|
||
|
domain: Optional[str],
|
||
|
argument: str,
|
||
|
) -> List[Location]:
|
||
|
if domain or directive not in {"figure", "image"}:
|
||
|
return []
|
||
|
|
||
|
uri = self.resolve_path(context.doc, argument)
|
||
|
if not uri:
|
||
|
return []
|
||
|
|
||
|
return [
|
||
|
Location(
|
||
|
uri=uri,
|
||
|
range=Range(
|
||
|
start=Position(line=0, character=0),
|
||
|
end=Position(line=1, character=0),
|
||
|
),
|
||
|
)
|
||
|
]
|
||
|
|
||
|
def resolve_link(
|
||
|
self,
|
||
|
context: DocumentLinkContext,
|
||
|
directive: str,
|
||
|
domain: Optional[str],
|
||
|
argument: str,
|
||
|
) -> Tuple[Optional[str], Optional[str]]:
|
||
|
if domain or directive not in {"figure", "image"}:
|
||
|
return None, None
|
||
|
|
||
|
if argument.startswith("https://") or argument.startswith("http://"):
|
||
|
return argument, None
|
||
|
|
||
|
return self.resolve_path(context.doc, argument), None
|
||
|
|
||
|
def resolve_path(self, doc: Document, argument: str) -> Optional[str]:
|
||
|
if argument.startswith("/"):
|
||
|
if not self.rst.app:
|
||
|
return None
|
||
|
|
||
|
basedir = pathlib.Path(self.rst.app.srcdir)
|
||
|
|
||
|
# Remove the leading '/' otherwise is will wipe out the basedir when
|
||
|
# concatenated
|
||
|
argument = argument[1:]
|
||
|
|
||
|
else:
|
||
|
basedir = pathlib.Path(Uri.to_fs_path(doc.uri)).parent
|
||
|
|
||
|
try:
|
||
|
fullpath = basedir / argument
|
||
|
fpath = fullpath.resolve()
|
||
|
|
||
|
if fpath.exists():
|
||
|
return Uri.from_fs_path(str(fpath))
|
||
|
except Exception:
|
||
|
self.logger.debug("Unable to resolve filepath '%s'", fullpath)
|
||
|
|
||
|
return None
|
||
|
|
||
|
|
||
|
def esbonio_setup(rst: SphinxLanguageServer, directives: Directives):
|
||
|
images = Images(rst)
|
||
|
|
||
|
directives.add_argument_definition_provider(images)
|
||
|
directives.add_argument_completion_provider(images)
|
||
|
directives.add_argument_link_provider(images)
|