Skip to content

Commit

Permalink
Merge pull request #435 from swyddfa/develop
Browse files Browse the repository at this point in the history
New Release
  • Loading branch information
alcarney authored Jul 31, 2022
2 parents ea8311a + cde4949 commit fc8d659
Show file tree
Hide file tree
Showing 44 changed files with 995 additions and 376 deletions.
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ repos:
- id: trailing-whitespace

- repo: https://github.com/psf/black
rev: 22.3.0
rev: 22.6.0
hooks:
- id: black

Expand All @@ -19,7 +19,7 @@ repos:
args: [--config=lib/esbonio/setup.cfg]

- repo: https://github.com/asottile/reorder_python_imports
rev: v3.1.0
rev: v3.8.2
hooks:
- id: reorder-python-imports
args: [--application-directories=lib:esbonio]
18 changes: 17 additions & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,23 @@
],
"python": "${command:python.interpreterPath}",
"cwd": "${workspaceFolder}/docs"
}
},
{
"name": "Python: Attach",
"type": "python",
"request": "attach",
"connect": {
"host": "localhost",
"port": 5678
},
"pathMappings": [
{
"localRoot": "${workspaceFolder}",
"remoteRoot": "."
}
],
"justMyCode": false
},
],
"compounds": [
{
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,13 @@ The language server is still in early development, but already provides the foll
<img src="./resources/images/hover-demo.png" alt="Hover Demo"></img>
</p>

**Implementations**

<p align="center">
<img src="./resources/images/implementation-demo.gif" alt="Implementations Demo"></img>
</p>


## `code/` - A VSCode extension for editing Sphinx projects
[![Visual Studio Marketplace Version](https://img.shields.io/visual-studio-marketplace/v/swyddfa.esbonio?style=flat-square)![Visual Studio Marketplace Installs](https://img.shields.io/visual-studio-marketplace/i/swyddfa.esbonio?style=flat-square)![Visual Studio Marketplace Downloads](https://img.shields.io/visual-studio-marketplace/d/swyddfa.esbonio?style=flat-square)](https://marketplace.visualstudio.com/items?itemName=swyddfa.esbonio)[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://github.com/swyddfa/esbonio/blob/develop/code/LICENSE)

Expand Down
5 changes: 5 additions & 0 deletions code/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ Goto definition is currently implemented for objects linked to by

![Goto Defintion Demo](../resources/images/definition-demo.gif)

### Goto Implementation

Goto implementation is available for roles and directives

![Goto Implementation Demo](../resources/images/implementation-demo.gif)
### Diagnostics

Errors from a build are published to VSCode as diagnostics
Expand Down
9 changes: 3 additions & 6 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from pygls.lsp.types import CompletionItem
from pygls.lsp.types import CompletionItemKind

from esbonio.lsp.roles import TargetCompletion
from esbonio.lsp.roles import Roles, TargetCompletion
from esbonio.lsp.rst import CompletionContext

import esbonio.lsp
Expand Down Expand Up @@ -63,7 +63,6 @@

autodoc_pydantic_model_show_json = True


intersphinx_mapping = {
"ipython": ("https://ipython.readthedocs.io/en/stable/", None),
"python": ("https://docs.python.org/3/", None),
Expand Down Expand Up @@ -165,7 +164,5 @@ def setup(app: Sphinx):
)


def esbonio_setup(rst):
roles = rst.get_feature("esbonio.lsp.roles.Roles")
if roles:
roles.add_target_completion_provider(LspMethod())
def esbonio_setup(roles: Roles):
roles.add_target_completion_provider(LspMethod())
7 changes: 7 additions & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ Here is a quick summary of the features implemented by the language server.
:align: center
:target: /_images/hover-demo.png

.. collection-item:: Implementation

The language server implements :lsp:`textDocument/implementation` so you can easily find the implementation of a given role or directive.

.. figure:: ../resources/images/implementation-demo.gif
:align: center
:target: /_images/implementation-demo.gif

- See the :ref:`lsp_getting_started` guide for details on how to get up and
running.
Expand Down
1 change: 1 addition & 0 deletions docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ sphinx
autodoc-pydantic
sphinx-panels
furo
pytest_lsp
-e lib/esbonio
-e lib/esbonio-extensions[tutorial]
41 changes: 37 additions & 4 deletions lib/esbonio/README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,48 @@
![Esbonio logo](https://github.com/swyddfa/esbonio/blob/release/resources/io.github.swyddfa.Esbonio.svg?raw=true)
# Esbonio [![PyPI](https://img.shields.io/pypi/v/esbonio?style=flat-square)](https://pypi.org/project/esbonio) [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/esbonio?style=flat-square)](https://pypi.org/project/esbonio)
# Esbonio

Esbonio - to explain.
[![PyPI](https://img.shields.io/pypi/v/esbonio?style=flat-square)](https://pypi.org/project/esbonio)[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/esbonio?style=flat-square)](https://pypi.org/project/esbonio)![PyPI - Downloads](https://img.shields.io/pypi/dm/esbonio?style=flat-square)[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://github.com/swyddfa/esbonio/blob/develop/lib/esbonio/LICENSE)

**This package is in early development**
**esbonio - (v.) to explain**

A [Language Server](https://microsoft.github.io/language-server-protocol/) that aims to make it easier to work with Sphinx documentation projects in editors that support it.
A [Language Server](https://microsoft.github.io/language-server-protocol/) that aims to make it easier to work with [reStructuredText](https://docutils.sourceforge.io/rst.html) tools such as [Sphinx](https://www.sphinx-doc.org/en/master/)

It's still in early development, but already provides the following features.

## Completion

![Completion Demo](https://github.com/swyddfa/esbonio/raw/release/resources/images/completion-demo.gif)

## Definitions

![Definition Demo](https://github.com/swyddfa/esbonio/raw/release/resources/images/definition-demo.gif)


## Diagnostics

![Diagnostics Demo](https://github.com/swyddfa/esbonio/raw/release/resources/images/diagnostic-sphinx-errors-demo.png)

## Document Links

![Document Link Demo](https://github.com/swyddfa/esbonio/raw/release/resources/images/document-links-demo.png)

## Document Symbols

![Document Symbols](https://github.com/swyddfa/esbonio/raw/release/resources/images/document-symbols-demo.png)

## Hover

![Hover Demo](https://github.com/swyddfa/esbonio/raw/release/resources/images/hover-demo.png)

## Implementations

![Implementations Demo](https://github.com/swyddfa/esbonio/raw/release/resources/images/implementation-demo.gif)
## Installation

The Language Server can be installed via pip.

Be sure to check out the [Getting Started](https://swyddfa.github.io/esbonio/docs/latest/en/lsp/getting-started.html) guide for details on integrating the server with your editor of choice.

```
$ pip install esbonio
```
1 change: 1 addition & 0 deletions lib/esbonio/changes/425.fix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Diagnostics for issues found in ``.. included::`` files should now have the correct filepath.
1 change: 1 addition & 0 deletions lib/esbonio/changes/428.fix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Extensions defined within Sphinx extensions or ``conf.py`` files can now take advantage of dependency injection
1 change: 1 addition & 0 deletions lib/esbonio/changes/429.api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
It is now possible to manually load an extension by calling the ``load_extension`` method on a language server object.
1 change: 1 addition & 0 deletions lib/esbonio/changes/431.api.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
``LanguageFeatures`` can now respond to ``textDocument/implementation`` requests by providing an ``implementation`` method and a collection of ``implementation_triggers``.
1 change: 1 addition & 0 deletions lib/esbonio/changes/431.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The language server now supports ``textDocument/implementation`` requests for roles and directives.
1 change: 1 addition & 0 deletions lib/esbonio/changes/433.enhancement.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Line numbers for diagnostics for issues found within Python docstrings should now be more accurate.
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.
114 changes: 41 additions & 73 deletions lib/esbonio/esbonio/lsp/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import enum
import importlib
import inspect
import json
import logging
import textwrap
import traceback
import typing
from typing import Any
from typing import Callable
from typing import Dict
from typing import Iterable
from typing import Optional
from typing import Type

from pygls.lsp.methods import CODE_ACTION
Expand All @@ -20,6 +16,7 @@
from pygls.lsp.methods import DOCUMENT_LINK
from pygls.lsp.methods import DOCUMENT_SYMBOL
from pygls.lsp.methods import HOVER
from pygls.lsp.methods import IMPLEMENTATION
from pygls.lsp.methods import INITIALIZE
from pygls.lsp.methods import INITIALIZED
from pygls.lsp.methods import SHUTDOWN
Expand All @@ -44,6 +41,7 @@
from pygls.lsp.types import FileOperationRegistrationOptions
from pygls.lsp.types import Hover
from pygls.lsp.types import HoverParams
from pygls.lsp.types import ImplementationParams
from pygls.lsp.types import InitializedParams
from pygls.lsp.types import InitializeParams
from pygls.lsp.types import MarkupContent
Expand All @@ -55,6 +53,7 @@
from .rst import DefinitionContext
from .rst import DocumentLinkContext
from .rst import HoverContext
from .rst import ImplementationContext
from .rst import LanguageFeature
from .rst import RstLanguageServer
from .symbols import SymbolVisitor
Expand All @@ -66,6 +65,8 @@
"DefinitionContext",
"DocumentLinkContext",
"HoverContext",
"ImplementationContext",
"LanguageFeature",
"RstLanguageServer",
"create_language_server",
]
Expand Down Expand Up @@ -151,7 +152,11 @@ def on_shutdown(ls: RstLanguageServer, *args):

@server.feature(TEXT_DOCUMENT_DID_OPEN)
def on_open(ls: RstLanguageServer, params: DidOpenTextDocumentParams):
pass

# TODO: Delete me when we've dropped Python 3.6 and a pygls release that
# remembers the language id is available.
doc = ls.workspace.get_document(params.text_document.uri)
doc.language_id = params.text_document.language_id # type: ignore

@server.feature(TEXT_DOCUMENT_DID_CHANGE)
def on_change(ls: RstLanguageServer, params: DidChangeTextDocumentParams):
Expand Down Expand Up @@ -314,6 +319,33 @@ def on_definition(ls: RstLanguageServer, params: DefinitionParams):

return definitions

@server.feature(IMPLEMENTATION)
def on_implementation(ls: RstLanguageServer, params: ImplementationParams):
uri = params.text_document.uri
pos = params.position

doc = ls.workspace.get_document(uri)
line = ls.line_at_position(doc, pos)
location = ls.get_location_type(doc, pos)

implementations = []

for feature in ls._features.values():
for pattern in feature.implementation_triggers:
for match in pattern.finditer(line):
if not match:
continue

start, stop = match.span()
if start <= pos.character and pos.character <= stop:
context = ImplementationContext(
doc=doc, location=location, match=match, position=pos
)
ls.logger.debug("Implementation context: %s", context)
implementations += feature.implementation(context)

return implementations

@server.feature(DOCUMENT_LINK)
def on_document_link(ls: RstLanguageServer, params: DocumentLinkParams):
uri = params.text_document.uri
Expand Down Expand Up @@ -367,25 +399,6 @@ def preview(ls: RstLanguageServer, *args) -> Dict[str, Any]:
def _load_module(server: RstLanguageServer, modname: str):
"""Load an extension module by calling its ``esbonio_setup`` function, if it exists."""

setup = _get_setup_function(modname)
if not setup:
return

args = _get_setup_arguments(server, setup, modname)
if not args:
return

try:
setup(**args)
logger.debug("Loaded module '%s'", modname)
except Exception:
logger.error(
"Error while setting up module '%s'\n%s", modname, traceback.format_exc()
)


def _get_setup_function(modname: str) -> Optional[Callable]:

try:
module = importlib.import_module(modname)
except ImportError:
Expand All @@ -394,57 +407,12 @@ def _get_setup_function(modname: str) -> Optional[Callable]:
)
return None

if not hasattr(module, "esbonio_setup"):
logger.error(
"Unable to load module '%s', missing 'esbonio_setup' function", modname
)
return None

return module.esbonio_setup


def _get_setup_arguments(
server: RstLanguageServer, setup: Callable, modname: str
) -> Optional[Dict[str, Any]]:
"""Given a setup function, try to construct the collection of arguments to pass to
it.
"""
annotations = typing.get_type_hints(setup)
parameters = {
p.name: annotations[p.name]
for p in inspect.signature(setup).parameters.values()
}

args = {}
for name, type_ in parameters.items():

if issubclass(server.__class__, type_):
args[name] = server
continue

if issubclass(type_, LanguageFeature):
# Try and obtain an instance of the requested language feature.
feature = server.get_feature(type_)
if feature is not None:
args[name] = feature
continue

logger.debug(
"Skipping module '%s', server missing requested feature: '%s'",
modname,
type_,
)
return None

logger.error(
"Skipping module '%s', parameter '%s' has unsupported type: '%s'",
modname,
name,
type_,
)
setup = getattr(module, "esbonio_setup", None)
if setup is None:
logger.error("Skipping module '%s', missing 'esbonio_setup' function", modname)
return None

return args
server.load_extension(modname, setup)


def dump(obj) -> str:
Expand Down
Loading

0 comments on commit fc8d659

Please sign in to comment.