diff --git a/docs/source/conf.py b/docs/source/conf.py index 226d5d64..a79ba0c0 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -12,10 +12,12 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # -# import os -# import sys -# sys.path.insert(0, os.path.abspath('.')) +import os +import sys +sys.path.insert(0, os.path.abspath('./ext')) + import importlib.metadata +import pathlib import re from docutils import nodes @@ -45,17 +47,23 @@ # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ + # Built-in extensions "sphinx.ext.autodoc", "sphinx.ext.intersphinx", "sphinx.ext.napoleon", + # 3rd party extensions "myst_parser", "sphinx_design", + # Local extensions + "examples", ] autodoc_member_order = "groupwise" autodoc_typehints = "description" autodoc_typehints_description_target = "all" +example_server_dir = pathlib.Path(__file__).parent.parent.parent / "examples" / "servers" + intersphinx_mapping = { "python": ("https://docs.python.org/3/", None), } diff --git a/docs/source/examples/code-actions.rst b/docs/source/examples/code-actions.rst new file mode 100644 index 00000000..3a6b729c --- /dev/null +++ b/docs/source/examples/code-actions.rst @@ -0,0 +1,6 @@ +Code Actions +============ + +.. example-server:: code_actions.py + :start-at: import re + diff --git a/docs/source/examples/code-lens.rst b/docs/source/examples/code-lens.rst new file mode 100644 index 00000000..074362c0 --- /dev/null +++ b/docs/source/examples/code-lens.rst @@ -0,0 +1,7 @@ +Code Lens +========= + +.. example-server:: code_lens.py + :start-at: import logging + + diff --git a/docs/source/examples/colors.rst b/docs/source/examples/colors.rst new file mode 100644 index 00000000..30e056f7 --- /dev/null +++ b/docs/source/examples/colors.rst @@ -0,0 +1,9 @@ +Document Color +============== + +.. example-server:: colors.py + :start-at: import logging + + + + diff --git a/docs/source/examples/formatting.rst b/docs/source/examples/formatting.rst new file mode 100644 index 00000000..82b715a2 --- /dev/null +++ b/docs/source/examples/formatting.rst @@ -0,0 +1,10 @@ +Document Formatting +=================== + +.. example-server:: formatting.py + :start-at: import logging + + + + + diff --git a/docs/source/examples/goto.rst b/docs/source/examples/goto.rst new file mode 100644 index 00000000..81dfdf1d --- /dev/null +++ b/docs/source/examples/goto.rst @@ -0,0 +1,12 @@ +Goto "X" +======== + +.. example-server:: goto.py + :start-at: import logging + + + + + + + diff --git a/docs/source/examples/hover.rst b/docs/source/examples/hover.rst new file mode 100644 index 00000000..0e535e97 --- /dev/null +++ b/docs/source/examples/hover.rst @@ -0,0 +1,12 @@ +Hover +===== + +.. example-server:: hover.py + :start-at: import logging + + + + + + + diff --git a/docs/source/examples/inlay-hints.rst b/docs/source/examples/inlay-hints.rst new file mode 100644 index 00000000..9b7389a5 --- /dev/null +++ b/docs/source/examples/inlay-hints.rst @@ -0,0 +1,13 @@ +Inlay Hints +=========== + +.. example-server:: inlay_hints.py + :start-at: import re + + + + + + + + diff --git a/docs/source/examples/json-server.rst b/docs/source/examples/json-server.rst new file mode 100644 index 00000000..1c2f4835 --- /dev/null +++ b/docs/source/examples/json-server.rst @@ -0,0 +1,13 @@ +JSON Server +=========== + +.. example-server:: json_server.py + :start-at: import argparse + + + + + + + + diff --git a/docs/source/examples/publish-diagnostics.rst b/docs/source/examples/publish-diagnostics.rst new file mode 100644 index 00000000..81799cea --- /dev/null +++ b/docs/source/examples/publish-diagnostics.rst @@ -0,0 +1,13 @@ +Publish Diagnostics +=================== + +.. example-server:: publish_diagnostics.py + :start-at: import logging + + + + + + + + diff --git a/docs/source/examples/pull-diagnostics.rst b/docs/source/examples/pull-diagnostics.rst new file mode 100644 index 00000000..32a426c1 --- /dev/null +++ b/docs/source/examples/pull-diagnostics.rst @@ -0,0 +1,13 @@ +Pull Diagnostics +================ + +.. example-server:: pull_diagnostics.py + :start-at: import logging + + + + + + + + diff --git a/docs/source/examples/rename.rst b/docs/source/examples/rename.rst new file mode 100644 index 00000000..e0665117 --- /dev/null +++ b/docs/source/examples/rename.rst @@ -0,0 +1,13 @@ +Rename +====== + +.. example-server:: rename.py + :start-at: import logging + + + + + + + + diff --git a/docs/source/ext/examples.py b/docs/source/ext/examples.py new file mode 100644 index 00000000..df1fa438 --- /dev/null +++ b/docs/source/ext/examples.py @@ -0,0 +1,86 @@ +"""Documentation for the example servers""" +from __future__ import annotations + +import os +import pathlib +import typing +import importlib.util as imutil + +from docutils import nodes +from docutils.parsers.rst import directives +from sphinx.util.docutils import SphinxDirective +from sphinx.util.logging import getLogger + +if typing.TYPE_CHECKING: + from sphinx.application import Sphinx + +logger = getLogger(__name__) + +class ExampleServerDirective(SphinxDirective): + """Automate the process of documenting example servers. + + Currently, this doesn't do *that* much, it + + - Inserts the code using a ``.. literalinclude::`` directive. + - Extracts the server module's docstring and inserts it into the page as nicely + rendered text. + + But perhaps we can do something more interesting in the future! + """ + + required_arguments = 1 + option_spec = { + "start-at": directives.unchanged, + } + + def get_docstring(self, filename: pathlib.Path): + """Given the filepath to a module, return its docstring.""" + + base = filename.stem + spec = imutil.spec_from_file_location(f"examples.{base}", filename) + + try: + module = imutil.module_from_spec(spec) + spec.loader.exec_module(module) + except Exception: + logger.exception("Unable to import example server") + return [] + + if (docstring := module.__doc__) is not None: + return docstring.splitlines() + + return [] + + def run(self): + server_dir = self.config.example_server_dir + name = self.arguments[0] + + if not (filename := pathlib.Path(server_dir, name)).exists(): + raise RuntimeError(f"Unable to find example server: {filename}") + + # Tell Sphinx to rebuild a document if this file changes + self.env.note_dependency(str(filename)) + + # An "absolute" path given to `literalinclude` is actually relative to the + # projects srcdir + relpath = os.path.relpath(str(filename), start=str(self.env.app.srcdir)) + content = [ + f".. literalinclude:: /{relpath}", + " :language: python", + ] + + if (start_at := self.options.get("start-at")) is not None: + content.append(f" :start-at: {start_at}") + + # Confusingly, these are processed in reverse order... + self.state_machine.insert_input(content, "") + self.state_machine.insert_input( + self.get_docstring(filename), str(filename) + ) + + return [] + + +def setup(app: Sphinx): + app.add_config_value("example_server_dir", "", rebuild='env') + app.add_directive("example-server", ExampleServerDirective) diff --git a/docs/source/getting-started.rst b/docs/source/getting-started.rst new file mode 100644 index 00000000..e5d91f92 --- /dev/null +++ b/docs/source/getting-started.rst @@ -0,0 +1,113 @@ +Getting Started +=============== + +.. _example-servers: + +Example Servers +--------------- + +.. toctree:: + :hidden: + :glob: + + examples/* + +.. tip:: + + If you use VSCode, we recommend you try these servers out in the :ref:`pygls-playground ` extension + +Each of the following example servers are focused on implementing a particular subset of the Language Server Protocol. + +.. grid:: 1 2 2 4 + :gutter: 2 + + .. grid-item-card:: Code Actions + :link: /examples/code-actions + :link-type: doc + :text-align: center + + :octicon:`light-bulb` + + .. grid-item-card:: Code Lens + :link: /examples/code-lens + :link-type: doc + :text-align: center + + :octicon:`eye` + + .. grid-item-card:: Colors + :link: /examples/colors + :link-type: doc + :text-align: center + + :octicon:`paintbrush` + + .. grid-item-card:: Formatting + :link: /examples/formatting + :link-type: doc + :text-align: center + + :octicon:`typography` + + .. grid-item-card:: Goto "X" + :link: /examples/goto + :link-type: doc + :text-align: center + + :octicon:`search` + + .. grid-item-card:: Hover + :link: /examples/hover + :link-type: doc + :text-align: center + + :octicon:`book` + + .. grid-item-card:: Inlay Hints + :link: /examples/inlay-hints + :link-type: doc + :text-align: center + + :octicon:`info` + + .. grid-item-card:: Publish Diagnostics + :link: /examples/publish-diagnostics + :link-type: doc + :text-align: center + + :octicon:`alert` + + .. grid-item-card:: Pull Diagnostics + :link: /examples/pull-diagnostics + :link-type: doc + :text-align: center + + :octicon:`alert` + + .. grid-item-card:: Rename + :link: /examples/rename + :link-type: doc + :text-align: center + + :octicon:`pencil` + +These servers are dedicated to demonstrating features of *pygls* itself + +.. grid:: 1 2 2 4 + :gutter: 2 + + .. grid-item-card:: JSON Server + :link: /examples/json-server + :link-type: doc + :text-align: center + + :octicon:`code` + + +Tutorial +-------- + +.. note:: + + Coming soon\ :sup:`TM` + diff --git a/docs/source/index.rst b/docs/source/index.rst index 093b0fd1..e706215c 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -21,7 +21,7 @@ allows you to write your own `language server`_ in just a few lines of code :hidden: :caption: User Guide - tutorial + getting-started user-guide How To reference @@ -40,12 +40,12 @@ The documentation is divided up into the following sections .. grid:: 1 2 2 2 :gutter: 2 - .. grid-item-card:: Tutorial - :link: /tutorial + .. grid-item-card:: Getting Started + :link: /getting-started :link-type: doc :text-align: center - Step-by-step guides on writing your first language server with *pygls*. + First steps with *pygls*. .. grid-item-card:: How To Guides :link: /howto