Skip to content

Latest commit

 

History

History
228 lines (145 loc) · 12.5 KB

CONTRIBUTING.md

File metadata and controls

228 lines (145 loc) · 12.5 KB

Contributing

Thanks for your interest. Below is an informal spec of how the plugin's server communicates with the actual compiler. If you're a ReScript editor plugin implementor, you should probably read this to understand the various important nuances and copy it.

Repo Structure

.
├── client // Language Client. VSCode UI
│   └── src
│       └── extension.ts // Language Client entry point
├── analysis // Native binary powering hover, autocomplete, etc.
│   ├── src // Analysis library
│   ├── bin // Analysis binary
├── package.json // The extension manifest
├── server // Language Server. Usable standalone
│   ├── src
│   │   ├── server.ts // Language Server Module
│   │   ├── cli.ts // LSP CLI
│   └── analysis_binaries // Prod-time platform-specific analysis binaries
│       ├── darwin
│       ├── linux
│       └── win32
└── tools // ReScript Tools
    ├── bin // OCaml Binary
    ├── src // ReScript Tools library
    └── binaries // Prod-time platform-specific binaries
        ├── darwin
        ├── linux
        └── win32

Install Dependencies

  • Run npm install at the root. This will also install the npm modules for both the client and server folders.

OCaml Code

This is needed for the analysis and tools folder, which is native code.

At the root:

# If you haven't created the switch, do it. OPAM(https://opam.ocaml.org)
opam switch 5.2.0 # can also create local switch with opam switch create . 5.2.0

# Install dev dependencies from OPAM
opam install . --deps-only

# For IDE support, install the OCaml language server and OCaml Formatter
opam install ocaml-lsp-server ocamlformat

You need dune to build the OCaml source code, if it is not available in your shell try running eval $(opam env). If dune is present, run make build to build the OCaml projects and copy the binaries to the root.

Build & Run

  • npm run compile. You don't need this if you're developing this repo in VSCode. The compilation happens automatically in the background.
  • make.

Test

  • Open VS Code to the project root.

  • Switch to the Debug viewlet (command palette -> View: Show Run and Debug).

  • Select Client + Server from the drop down, launch it (green arrow):

    image

    If you're getting some Promise-related error alert: this is a VSCode and/or template bug.

    • If that newly launched VSCode test instance has no project in its explorer view, drag in a random project.
    • Kill all your node processes.
    • Redo the launch.
  • In the [Extension Development Host] instance of VSCode that just opened, open a .res file.

  • Try various features.

  • When you make a change, Go to the same Debug viewlet's Call Stack panel and restart the client and the server:

    image
  • For the native analysis and tools binary tests: make test.

Change the Grammar

The real source of truth for our grammar is at https://github.com/rescript-lang/rescript-sublime. We port that sublime-syntax grammar over to this weaker TextMate language grammar for VSCode and the rest. There are some subtle differences between the 2 grammars; currently we manually sync between them.

  • Modify grammars/rescript.tmLanguage.json.

For more grammar inspirations, check:

Snippets

Snippets are also synced from https://github.com/rescript-lang/rescript-sublime. VSCode snippets docs here.

Binary Invocation

We call a few binaries and it's tricky to call them properly cross-platform. Here are some tips:

  • We try to call the binaries synchronously to avoid races.
  • Make sure you cater to calling a binary and passing e.g. a path with whitespace in it.
  • execFile and its sync version do the above for free.
  • execFile does not work on windows for batch scripts, which is what Node scripts are wrapped in. Use exec. See more here.
  • Thankfully, many of our binaries are native, so we can keep using execFile most of the time.

General Coding Guidance

  • server/ is a standalone LSP server. Keep it light, don't add deps unless absolutely necessarily, and don't accidentally use a runtime dep from the top level package.json.
  • This codebase stayed alive by not trying to babysit long-living processes. Be fast, call a binary and shut down.

Rough Description Of How The Plugin Works

Editor Diagnostics

They should be synced in from lib/bs/.compiler.log build. Don't take them from other places.

.compiler.log

The build output is streamed into lib/bs/.compiler.log. Here are its various states, numbered here:

  1. Doesn't exist: artifacts not built yet, or cleaned away.
  2. Present, without a final line #Done: still building.
  3. Present, with the final line #Done: finished building.

Barring FS errors, there should be no other state to .compiler.log. Among others, this means the file is never present but empty.

The compiler log contains exactly the same things you'd see in a regular terminal bsb guild, except:

  • The errors are indented 2 spaces
  • The extra #Start and #Done (which aren't indented).

A parser for the diagnostics is here.

State 1

Artifacts cleaning through bsb -clean removes .compiler.log and turns into state 1. If that's the case, remove the diagnostics in the editor too. One could argue that they should be kept, but that's misleading UX-wise, and harder to implement correctly.

Streaming Update of Diagnostics

After saving a file and running the build, the results stream into the log file. Unfortunately, UX-wise, in the editor, this might look like the diagnostics are suddenly gone then coming back in file by file. This looks bad. To remediate:

  • If it's in state 2, update those particular files' diagnostics but don't wipe the files' diagnostics yet.
  • If in state 3, finish by clean up the rest of the old diagnostics. This means there's a bit of bookeeping needed here. Make sure you get it right. It's possible for a build to be interrupted (and therefore state 4 never reached) and restarted.

Even this fix isn't great. Ideally, the editor's diagnostics can be greyed out while we're updating them...

Keep in mind that you might be tracking multiple .compiler.logs. You should do the above for each.

Stale Diagnostics Detection

To check whether the artifacts are stale, do not check .bsb.lock at the project root. This is unreliable, since it's possible that bsb wasn't running in watcher mode. We also don't want to encourage overuse of the watcher mode, though it seems increasingly common.

We currently do that; we wish we aren't.

Files from Other Projects

It's possible to open files from different projects into the same editor instance. In that case, also read that file's project's .compiler.log.

The bad alternatives are:

  • Not show that file's project's errors. That's wrong for several reasons (looks like the file has no error, assumes an editor window has a default project, etc.).
  • Show only that file's error. That's just weird, the errors are already read from that project's .compiler.log. Might as well show all of them (?).

Running bsb in the Editor

Don't do that unless you've prompted the user. This plugin currently prompts the user upon opening thr first the first file of a project. It's not great, but otherwise lots of folks forget to start a bsb in the terminal to see the freshest diagnostics.

Drawbacks:

  • Running an implicit bsb -w means you've acquired the build watch mode lockfile. The user won't be able to run his/her own bsb -w in the terminal.
  • Running a one-shot bsb doesn't conflict, but is a waste. It's also incorrect, as there might be external file system changes you're not detecting, e.g. version control changes.
  • The build might be a step in a bigger build. The editor running bsb -w by itself might clash with that.
  • If you have multiple files with different project roots open, running all of the bsb -ws is too intense.

Format

To find the location of bsc.exe to run the formatter:

  • Search in the file's directory's node_modules/bs-platform/{platform}/bsc.exe. If not found, recursively search upward (because monorepos).
    • Do not directly use node_modules/.bin/bsc if you can help it. That's a Nodejs wrapper. Slow startup. We don't want our formatting to be momentarily stalled because some Nodejs cache went cold.
    • platform can be darwin, linux, win32 or freebsd.

Formatting Newline

The formatted result should be taken as-is, without any extra string trimming and newline addition/removal by the editor plugin.

Formatting Errors

The errors returned from bsc.exe -format should be discarded; in theory, they should have been duplicates of the errors from .compiler.log.

In the future, we should consier showing the format errors when .compiler.log isn't found.

Analysis bin

Analysis bin is what we currently call the OCaml code that does deeper language related analysis, and that powers most of the language specific functionality like hovers, completion, and so on. Here's a list of PRs and other resources you can have a look at if you're interested in contributing to the analysis bin:

  • Implementing "code lens" for function definitions. PR + commits have a bunch of comments intended to be educational as to what's done where, and why. #513
  • Cristiano fixes a bug where autocomplete wasn't working in switch branches, because the analysis did not cover that context. Contains a bunch of good comments on how the test setup works, etc. #415

We're happy to gather more resources over time here, including more in-depth getting started guides.

Releasing the VSCode extension and standalone LSP package

This below will automatically release the LSP package as well.

  1. Bump the version to an even minor version number in package.json and server/package.json and their lockfiles. It's very important that it's an even minor like 1.8.0, and not 1.7.0. This is because even minors are reserved for actual releases, and uneven minors for pre-releases. Commit and push the version bump.
  2. Let CI build your version bump commit.
  3. Tag the commit with the version number (e.g. git tag 1.6.0) and push the tag (e.g. git push origin 1.6.0). Another build will trigger, which should automatically:
    • create a rescript-vscode-<version-number>.vsix file
    • publish that extension version to the VSCode marketplace
    • create an automatic release on GitHub

If that somehow does not work, you can do the above steps manually:

  1. Download the autogenerated .vsix from the previous successful CI run, unzip it, and rename it to rescript-vscode-<version-number>.vsix (rescript-vscode-1.3.0.vsix for example).
  2. Go to the appropriate VSCode Marketplace Publisher, select the three dots next to the extension name, and choose Update. Upload your .vsix there.
  3. Not done! Make a new manual release here, and make sure you attach the generated .vsix onto that new release as well. This is for folks who don't use the VSCode marketplace.

For beta releases, ask folks to use the pre-release version installable from the VSCode Marketplace.

Releasing the @rescript/tools package

The tools package is released by bumping the version in tools/package.json and run node scripts/updateVersion.js, running npm i in the tools/ folder, and then pushing those changes with the commit message publish tools.