Skip to content

Commit

Permalink
docs: Add architecture diagram to reference section
Browse files Browse the repository at this point in the history
  • Loading branch information
alcarney committed Jan 10, 2024
1 parent 38e4517 commit 980990a
Show file tree
Hide file tree
Showing 3 changed files with 284 additions and 1 deletion.
2 changes: 2 additions & 0 deletions docs/lsp/reference.rst
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.. _lsp-reference:

Reference
=========

Expand Down
282 changes: 282 additions & 0 deletions docs/lsp/reference/architecture.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,282 @@
Server Architecture
===================

.. raw:: html

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->

<div class="figure align-default">
<svg
width="100%"
viewBox="0 0 297 210"
style="background: var(--color-content-background)"
version="1.1"
id="svg5"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs2">
<marker
style="overflow:visible"
id="Arrow1Mstart"
refX="0.0"
refY="0.0"
orient="auto">
<path
transform="scale(0.4) translate(10,0)"
style="fill:var(--color-content-foreground);fill-rule:evenodd;fill:context-stroke;stroke:context-stroke;stroke-width:1.0pt"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
id="path35046" />
</marker>
<marker
style="overflow:visible;"
id="Arrow1Mend"
refX="0.0"
refY="0.0"
orient="auto">
<path
transform="scale(0.4) rotate(180) translate(10,0)"
style="fill:var(--color-content-foreground);fill-rule:evenodd;fill:context-stroke;stroke:context-stroke;stroke-width:1.0pt;"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
id="path35049" />
</marker>
<marker
style="overflow:visible;"
id="Arrow1Lend"
refX="0.0"
refY="0.0"
orient="auto">
<path
transform="scale(0.8) rotate(180) translate(12.5,0)"
style="fill:var(--color-content-foreground);fill-rule:evenodd;fill:context-stroke;stroke:context-stroke;stroke-width:1.0pt;"
d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
id="path35043" />
</marker>
<marker
style="overflow:visible"
id="Arrow1Mend-1"
refX="0"
refY="0"
orient="auto">
<path
transform="matrix(-0.4,0,0,-0.4,-4,0)"
style="fill:var(--color-content-foreground);fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path35049-8" />
</marker>
<marker
style="overflow:visible"
id="Arrow1Mend-9"
refX="0"
refY="0"
orient="auto">
<path
transform="matrix(-0.4,0,0,-0.4,-4,0)"
style="fill:var(--color-content-foreground);fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
id="path35049-2" />
</marker>
</defs>
<g
id="layer1">
<rect
style="fill:var(--color-content-background);stroke:var(--color-content-foreground);stroke-width:1;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
id="rect1192"
width="121.70834"
height="129.64583"
x="166.15813"
y="31.75" />
<rect
style="fill:var(--color-content-background);stroke:var(--color-content-foreground);stroke-width:1;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
id="rect1192-3"
width="52.916668"
height="129.64583"
x="5.2916665"
y="31.75001" />
<rect
style="fill:var(--color-sidebar-background);fill-rule:evenodd;stroke:var(--color-content-foreground);stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect31"
width="68.096382"
height="25.12426"
x="211.90756"
y="46.209999" />
<rect
style="fill:var(--color-sidebar-background);fill-rule:evenodd;stroke:var(--color-content-foreground);stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect31-3"
width="68.096382"
height="25.12426"
x="211.83255"
y="123.03125" />
<rect
style="fill:var(--color-sidebar-background);fill-rule:evenodd;stroke:var(--color-content-foreground);stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect31-5"
width="68.096382"
height="25.12426"
x="211.83257"
y="75.406258" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:var(--color-content-foreground);fill-opacity:1;stroke:none;stroke-width:0.264583"
x="215.90004"
y="60.854172"
id="text3354"><tspan
id="tspan3352"
style="font-size:5.64444px;stroke-width:0.264583"
x="215.90004"
y="60.854172">Language Feature #1</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:var(--color-content-foreground);fill-opacity:1;stroke:none;stroke-width:0.264583"
x="215.5123"
y="89.815636"
id="text3354-6"><tspan
id="tspan3352-2"
style="font-size:5.64444px;stroke-width:0.264583"
x="215.5123"
y="89.815636">Language Feature #2</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:var(--color-content-foreground);fill-opacity:1;stroke:none;stroke-width:0.264583"
x="190.10786"
y="96.331848"
id="text3354-6-0"><tspan
id="tspan3352-2-9"
style="font-size:5.64444px;text-align:center;text-anchor:middle;stroke-width:0.264583"
x="190.10786"
y="96.331848">Language </tspan><tspan
style="font-size:5.64444px;text-align:center;text-anchor:middle;stroke-width:0.264583"
x="190.10786"
y="103.3874"
id="tspan25491">Server</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:var(--color-content-foreground);fill-opacity:1;stroke:none;stroke-width:0.264583"
x="31.655663"
y="97.075394"
id="text3354-6-0-6"><tspan
id="tspan3352-2-9-2"
style="font-size:5.64444px;text-align:center;text-anchor:middle;stroke-width:0.264583"
x="31.655663"
y="97.075394">Language </tspan><tspan
style="font-size:5.64444px;text-align:center;text-anchor:middle;stroke-width:0.264583"
x="31.655663"
y="104.13094"
id="tspan25491-6">Client</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:var(--color-content-foreground);fill-opacity:1;stroke:none;stroke-width:0.264583"
x="92.105232"
y="99.029205"
id="text3354-6-0-6-2"><tspan
style="font-size:5.64444px;text-align:center;text-anchor:middle;stroke-width:0.264583"
x="92.105232"
y="99.029205"
id="tspan25491-6-7">LSP Protocol</tspan></text>
<g
id="g34651"
transform="translate(-5.8208336)">
<rect
style="fill:var(--color-content-background);stroke:var(--color-content-foreground);stroke-width:1.07275;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect1192-3-5"
width="24.358648"
height="129.64583"
x="129.62888"
y="31.750002" />
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:var(--color-content-foreground);fill-opacity:1;stroke:none;stroke-width:0.264583"
x="141.52403"
y="98.416359"
id="text3354-6-0-3"><tspan
style="font-size:5.64444px;text-align:center;text-anchor:middle;stroke-width:0.264583"
x="141.52403"
y="98.416359"
id="tspan25491-0">Engine</tspan></text>
</g>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:var(--color-content-foreground);fill-opacity:1;stroke:none;stroke-width:0.264583"
x="105.41908"
y="-245.68881"
id="text3354-6-9"
transform="rotate(90.528171)"><tspan
style="font-size:5.64444px;stroke-width:0.264583"
x="105.41908"
y="-245.68881"
id="tspan20015">.....</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-weight:normal;font-size:5.64444px;line-height:1.25;font-family:sans-serif;fill:var(--color-content-foreground);fill-opacity:1;stroke:none;stroke-width:0.264583"
x="215.92711"
y="137.34949"
id="text3354-6-9-2"><tspan
id="tspan3352-2-1-7"
style="font-size:5.64444px;stroke-width:0.264583"
x="215.92711"
y="137.34949">Language Feature #N</tspan></text>
<path
style="fill:none;stroke:var(--color-content-foreground);stroke-width:0.83959;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Mend)"
d="M 59.546327,89.945827 H 121.22207"
id="path34806" />
<path
style="fill:none;stroke:var(--color-content-foreground);stroke-width:0.83959;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-end:url(#Arrow1Mend-9)"
d="M 122.87663,104.76253 H 61.200895"
id="path34806-0" />
<path
style="fill:none;stroke:var(--color-content-foreground);stroke-width:0.417578;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;marker-start:url(#Arrow1Mstart);marker-end:url(#Arrow1Mend-1)"
d="m 149.53571,96.847604 h 15.25657"
id="path34806-7" />
</g>
</svg>
<p class="caption">A rough sketch of how the language server(s) in Esbonio are architected.</p>
</div>


.. glossary::

Language Server
A language server is a subclass of the ``LanguageServer`` class provided by the `pygls`_ library.

In Esbonio, all the features you would typically associate with a language server, e.g. completions are not actually implemented by the language server.
These features are provided through a number of "language features" (see below).
Instead a language server acts a container for all the active language features and provides an API they can use to query aspects of the environment.

Esbonio currently provides two language servers

- :class:`~esbonio.lsp.rst.RstLanguageServer`: Base language server, meant for "vanilla" docutils projects.
- :class:`~esbonio.lsp.sphinx.SphinxLanguageServer` Language server, specialising in Sphinx projects.

Language Feature
Language features are subclasses of :class:`~esbonio.lsp.rst.LanguageFeature`.
They are typically based on a single aspect of reStructuredText (e.g. :class:`~esbonio.lsp.roles.Roles`).

Language Features (where it makes sense) should be server agnostic, that way the same features can be reused across different envrionments.

Engine
For lack of a better name... an "engine" is responsible for mapping messages from the LSP Protocol into function calls within the language server.
Unlike the other components of the architecture, an "engine" isn't formally defined and there is no API to implement.
Instead it's just the term used to refer to all the ``@server.feature()`` handlers that define how LSP messages should be handled.

Currently we provide just a single "engine" :func:`~esbonio.lsp.create_language_server`.
As an example, here is how it handles ``textDocument/completion`` requests.

.. literalinclude:: ../../../lib/esbonio/esbonio/lsp/__init__.py
:language: python
:dedent:
:start-after: # <engine-example>
:end-before: # </engine-example>

There is nothing in Esbonio that would prevent you from writing your own if you so desired.

Extension Module
Ordinary Python modules are used to group related functionality together.
Taking inspiration from how Sphinx is architected, language servers are assembled by passing the list of modules to load to the :func:`~esbonio.lsp.create_language_server`.
This assembly process calls any functions with the name ``esbonio_setup`` allowing for ``LanguageFeatures`` to be configured and loaded into the server.

Startup Module
As mentioned above, language servers are assembled and this is done inside a startup module.
A startup module in Esbonio is any Python script or module runnable by a ``python -m <modulename>`` command that results in a running language server.
A good use case for a custom entry point would be starting up a language server instance pre configured with all the extensions required by your project.

.. _pygls: https://pygls.readthedocs.io/en/latest/index.html
1 change: 0 additions & 1 deletion docs/lsp/reference/previews.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ Preview Implementation

This page gives an overview of how the preview feature in ``esbonio`` is implemented.


.. figure:: /images/preview-architecture.svg
:align: center

Expand Down

0 comments on commit 980990a

Please sign in to comment.