Skip to content

Commit

Permalink
Basic docs (#7)
Browse files Browse the repository at this point in the history
  • Loading branch information
keynmol authored Aug 10, 2022
1 parent 1b9027c commit 057b7b7
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
cache: 'sbt'

- name: Test
run: sbt --client 'ci; publishLocal'
run: sbt --client 'ci; publishLocal; lsp/doc'

- name: Publish ${{ github.ref }}
if: startsWith(github.ref, 'refs/tags/v')
Expand Down
34 changes: 26 additions & 8 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,6 @@ lazy val lsp = projectMatrix
.defaultAxes(default*)
.settings(
name := "langoustine-lsp",
Compile / doc / sources := {
if (!virtualAxes.value.contains(VirtualAxis.jvm)) Seq.empty
else (Compile / doc / sources).value
},
Compile / doc / target := (ThisBuild / baseDirectory).value / "website" / "api",
Compile / doc / scalacOptions ++= {
Seq("-project", "Langoustine")
},
scalacOptions ++= Seq("-Xmax-inlines", "64"),
libraryDependencies += "com.eed3si9n.verify" %%% "verify" % V.verify % Test,
testFrameworks += new TestFramework("verify.runner.Framework"),
Expand All @@ -90,6 +82,7 @@ lazy val lsp = projectMatrix
.jvmPlatform(scalaVersions)
.jsPlatform(scalaVersions)
.nativePlatform(scalaVersions)
.settings(docsSettings)

lazy val generate = projectMatrix
.in(file("modules/generate"))
Expand Down Expand Up @@ -174,3 +167,28 @@ usefulTasks := Seq(
)

logoColor := scala.Console.MAGENTA

lazy val docsSettings = Seq(
Compile / doc / scalacOptions ++= Seq(
"-project",
"Langoustine",
"-siteroot",
"docs",
"-project-version",
version.value,
/* "-project-logo", */
/* "docs/logo.svg", */
"-social-links:" +
"github::https://github.com/neandertech/langoustine",
"-project-footer",
s"Copyright (c) 2022, Neandertech",
"-source-links:github://neandertech/langoustine",
"-revision",
"master"
),
Compile / doc := {
val out = (Compile / doc).value
IO.copyDirectory((Compile / doc / target).value, file("website"))
out
}
)
80 changes: 80 additions & 0 deletions docs/_docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# Contributing guide

Notes:

1. Typos and small fix PRs are always welcome, no matter how small!
2. For larger changes please create an issue to discuss it first

## Dev process

1. Run tests with `test`
2. Re-generate LSP definitions with `generateLSP` (if you changed any code in `meta` or
`generate` modules, see below)
3. Generate docs with `generateDocs`, you should see the website in `./website/` folder
4. **Before pushing** make sure you run `preCI` which will

- Reformat the code
- Run Scalafix
- Ensure your changes don't get tripped up by the boring, automatable
steps of the build (like the ones mentioned above)

## Structure

The project consists of 3 main modules

### `meta` - which defines the meta-model of LSP specification

This module was essentially hand-crafted from the `metaModel.schema.json` file
which you can find in the repository.

It defines what is a `Request`, `Structure`, `OrType`, etc. which are then used in the LSP
specification itself.

It also defines json codecs and helper types to be able to parse the actual LSP
specification from JSON.

### `generate` - which converts LSP specification to Scala code

This module does all the heavy lifting required to massage the LSP spec into
Scala-compatible domain model and subsequently Scala 3 code:

1. Apply type transformations

For example, convert anonymous structures into a named structure in the companion
object

2. Define how types are rendered

i.e. `OrType` is usually rendered as a Scala 3 Union, whereas a union with a `Null`
component will be rendered as `Opt[A]` where `A` is a union type of remaining
components

3. Lay out JSON codecs to match both the `given` semantics of the language, and
the API methods available in uPickle

4. Render enumerations and type aliases
5. Selectively remove some types

For example, LSPAny is basically a JSON structure and there's
no real reason to massage it (it's recursive, which makes things hard)
so we need to replace it with `ujson.Value` everywhere.

This module also defines a main class, which accepts a target path
as an argument and, when run, generates all the LSP code in that location.

### `lsp` - rendered LSP specification and runtime classes

The `generated` folder contains all the code generated by the `generate` module described above.
Generated code is **excluded from Scalafmt**.

The rest of the code contains

- runtime datastructures required for LSP to run,
- `LSPBuilder` to define Language Servers
- integration with [jsonrpclib]
- runtime enumeration definitions
- some basic types (like `DocumentUri` and `uinteger`)
- specialised JSON codecs which are used as building blocks
for more complex codecs in the generated code

[jsonrpclib]: https://github.com/neandertech/jsonrpclib
28 changes: 28 additions & 0 deletions docs/_docs/examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Examples

LSP servers are not easy to demonstrate succintly, because the only way to confirm it
actually works is to use it with a LSP client - usually an IDE.

Additionally, various editors require various degrees of effort. Simplest
one I found so far is in Neovim Nightly, see https://www.vikasraj.dev/blog/lsp-neovim-retrospective#into-the-future.

## LSP for Tree Sitter grammars

[Grammar.js LSP](https://github.com/keynmol/grammar-js-lsp)

Designed to make my life easier when working with [Tree Sitter grammars](https://github.com/tree-sitter/tree-sitter-scala/),
this LSP parses a subset of `JavaScript` the [acorn](https://github.com/acornjs/acorn)
library, builds an index of rules and their reductions, and makes it easier
to preview rules and use things like go-to-definition.

Because this LSP needs to parse JS using a JS library, the entire server is packaged
using Scala.js as a Node.js application.

## LSP for a toy language

[Quickmaffs](https://github.com/neandertech/quickmaffs)

Designed to serve as _the_ testbed for Langoustine changes, it's a small primitive
arithmetic language with associated set of tooling and built-in LSP.

This particular example runs on the JVM and uses Scala CLI exclusively for packaging.
19 changes: 19 additions & 0 deletions docs/_docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Langoustine

A clean room implementation of the LSP protocol definitions.

By "clean room" we mean

1. Using only Scala libraries
2. Idiomatic Scala code
3. Using Scala 3 features

Most of the code is generated directly from the recently published LSP specification in JSON format.

[![langoustine-lsp Scala version support](https://index.scala-lang.org/neandertech/langoustine/langoustine-lsp/latest.svg)](https://index.scala-lang.org/neandertech/langoustine/langoustine-lsp)

* **SBT:** `libraryDependencies += "tech.neander" %% "langoustine-lsp" % "<version>"`
* **Mill**: `ivy"tech.neander::langoustine-lsp::<version>"`
* [**Scala CLI**](https://scala-cli.virtuslab.org) `//> using lib "tech.neander::langoustine-lsp::<version>"`

In all of those, [don't forget to add an extra % or : if targeting a non-JVM artifact](https://youforgotapercentagesignoracolon.com)
6 changes: 6 additions & 0 deletions docs/sidebar.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
index: index.md
subsection:
- title: Examples
page: examples.md
- title: Contributing guide
page: CONTRIBUTING.md

0 comments on commit 057b7b7

Please sign in to comment.