Skip to content

Commit

Permalink
lsp: Use pygls Documents for get_initial_doctree
Browse files Browse the repository at this point in the history
The check for whether we should parse a document in
`get_initial_doctree` now relies on `get_location_type` rather than
checking the file extension. This means that files that don't have the
traditional `*.rst` extension but are treated by the client as
reStructuredText documents should now work as expected.

Additionally, by reading the document's source from the `Document`
maintained by `pygls` rather than reading from disk, document symbol
requests are more accurate taking into account any unsaved changes in
the user's editor.

This also removes the need for direct file system access - poentially
opening the door for running the vanilla docutils server in Pyodide?!
  • Loading branch information
alcarney committed Jul 31, 2022
1 parent 90bde7f commit 160996f
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 18 deletions.
1 change: 1 addition & 0 deletions lib/esbonio/changes/434.enhancement.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Document symbol requests made for unsaved files now use the language client's version rather than the version on disk.
1 change: 1 addition & 0 deletions lib/esbonio/changes/434.fix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The server should now handle document symbol requests for files that are treated as reStructuredText files by a language client but don't have an ``*.rst`` extension.
15 changes: 10 additions & 5 deletions lib/esbonio/esbonio/lsp/rst/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -575,12 +575,17 @@ def get_initial_doctree(self, uri: str) -> Optional[Any]:
uri
Returns the doctree that corresponds with the given uri.
"""
filename = pathlib.Path(Uri.to_fs_path(uri))
try:
return read_initial_doctree(filename, self.logger)
except FileNotFoundError:
self.logger.debug(traceback.format_exc())

doc = self.workspace.get_document(uri)
loctype = self.get_location_type(doc, Position(line=1, character=0))

if loctype != "rst":
return None

self.logger.debug("Getting initial doctree for: '%s'", Uri.to_fs_path(uri))

try:
return read_initial_doctree(doc, self.logger)
except Exception:
self.logger.error(traceback.format_exc())
return None
Expand Down
24 changes: 11 additions & 13 deletions lib/esbonio/esbonio/lsp/rst/io.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
import logging
import pathlib
import typing
from typing import Any
from typing import Callable
from typing import IO
from typing import Optional
from typing import Type

import pygls.uris as uri
from docutils import nodes
from docutils.core import Publisher
from docutils.io import FileInput
from docutils.io import NullOutput
from docutils.io import StringInput
from docutils.parsers.rst import Directive
from docutils.parsers.rst import directives
from docutils.parsers.rst import Parser
from docutils.parsers.rst import roles
from docutils.readers.standalone import Reader
from docutils.utils import Reporter
from docutils.writers import Writer
from pygls.workspace import Document
from sphinx.environment import default_settings

from esbonio.lsp.util.patterns import DIRECTIVE
Expand Down Expand Up @@ -174,9 +175,9 @@ def new_document(self) -> nodes.document:


def read_initial_doctree(
filename: pathlib.Path, logger: logging.Logger
document: Document, logger: logging.Logger
) -> Optional[nodes.document]:
"""Parse the given reStructuredText file into its "initial" doctree.
"""Parse the given reStructuredText document into its "initial" doctree.
An "initial" doctree can be thought of as the abstract syntax tree of a
reStructuredText document. This method disables all role and directives
Expand All @@ -185,29 +186,26 @@ def read_initial_doctree(
Parameters
----------
filename
Returns the doctree that corresponds with the given file.
document
The document containing the reStructuredText source.
logger
Logger to log debug info to.
"""

parser = Parser()

if filename.suffix.replace(".", "") not in parser.supported:
return None

logger.debug("Getting initial doctree for: '%s'", filename)
with disable_roles_and_directives():
publisher = Publisher(
reader=InitialDoctreeReader(logger),
parser=parser,
writer=DummyWriter(),
source_class=FileInput,
source_class=StringInput,
destination=NullOutput(),
)
publisher.process_programmatic_settings(None, default_settings, None)
publisher.set_source(source_path=str(filename))
publisher.set_source(
source=document.source, source_path=uri.to_fs_path(document.uri)
)
publisher.publish()

return publisher.document

0 comments on commit 160996f

Please sign in to comment.