Skip to content

Commit

Permalink
Rewrite contributing (#2614)
Browse files Browse the repository at this point in the history
* Rewrite contributing

* Apply suggestions from code review

Co-authored-by: Dirceu Pereira Tiegs <[email protected]>

---------

Co-authored-by: Dirceu Pereira Tiegs <[email protected]>
  • Loading branch information
vinistock and dirceu authored Sep 25, 2024
1 parent d9420a2 commit b8d3954
Showing 1 changed file with 187 additions and 101 deletions.
288 changes: 187 additions & 101 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,149 +1,235 @@
# CONTRIBUTING

## Contributing to documentation
This repository contains three sub-projects:

Ruby LSP uses [Jekyll](https://jekyllrb.com/) to generate the documentation, whose source lives under the `/jekyll` folder.
- the **language server** (`ruby-lsp`), which exists at the top level of the repository. Most features are implemented here since everything implemented in the server is available to all editors
- the **VS Code extension**, which exists under the `vscode` directory. Any custom VS Code features are implemented here
- the **documentation** website, which exists under the `jekyll` directory. All user facing documentation for both the Ruby LSP and the Rails addon is contained here

```shell
cd jekyll
bundle install
bundle exec jekyll serve # Builds the site and serves it at http://localhost:4000/ruby-lsp
```
This contributing guide is split by each component.

## Testing changes
## Initial setup

### Tracing LSP requests and responses
To start contributing to the Ruby LSP, ensure that all dependencies are installed as follows:

LSP server tracing (logging) can be controlled through the `ruby lsp.trace.server` config key in the
`.vscode/settings.json` config file.
- `bundle install` on the top level will install Ruby dependencies
- `bundle install` in the `jekyll` directory will install documentation dependencies
- `yarn install` in the `vscode` directory will install Node.js dependencies for the VS Code extension

Possible values are:
## Contributing to the server

- `off`: no tracing
- `messages`: display requests and responses notifications
- `verbose`: display each request and response as JSON
This is the structure of the `ruby-lsp` gem:

### Manually testing a change
- `lib/ruby_indexer`: the code indexer to extract declarations from the workspace
- `lib/ruby_lsp/*.rb`: foundational pieces of the language server, such as workers, queues, handling requests and so on
- `lib/ruby_lsp/requests`: request implementation. These match one to one with the [language server specification](https://microsoft.github.io/language-server-protocol/specification)
- `lib/ruby_lsp/listeners`: Prism dispatcher listeners. Most of the server's infrastructure relies on a listener pattern to maximize performance while traversing ASTs. Note that a single request may depend on more than one listener

There are a few options for manually testing changes to Ruby LSP:
When adding or changing an existing feature, first identify which request is responsible for it in the
[specification](https://microsoft.github.io/language-server-protocol/specification). Then identify which file in the
server implements that request and start thinking about the implementation.

You can test against Ruby LSP's own code if using VS Code, and you have the `ruby-lsp` project open. Choose 'Ruby LSP: Restart' from the command menu and the VS Code extension will detect that you are working on `ruby-lsp`, and use the locally checked out code instead of the installed extension.
> [!NOTE]
>
> When using VS Code, open the `lsp.code-workspace` file instead of just opening the regular folder. It contains
> configurations for working with the sub projects side by side effectively
The other way is to use a separate project, and add a Gemfile entry pointing to your local checkout of `ruby-lsp`, e.g.:
### Debugging

```
gem "ruby-lsp", path: "../../Shopify/ruby-lsp"
#### Live debugging the server

It is possible to live debug the development instance of the language server that is currently running when using VS
Code:

1. `CMD/CTRL + SHIFT + P` to open the command palette
2. Search for `Debug the Ruby LSP server`. This command will restart the server in debug mode, allowing you to connect
with a debugger. Note that the debug mode applies only until the editor is closed or Ruby LSP restarts
3. After the server has launched in debug mode, attach the debugger by going in `Run and debug`, selecting the `Attach to existing server` task and clicking run
4. You should now be able to put breakpoints in the UI and triggering requests will stop at those breakpoints

Caveat: since you are debugging the language server instance that is currently running in your own editor, features will
not be available if the execution is currently suspended at a breakpoint.

#### Understanding Prism ASTs

The Ruby LSP uses Prism to parse and understand Ruby code. When working on a feature, it's very common to need to
inspect the structure of the Prism AST for a given code snippet, so that you can understand why a request is being
triggered a certain way.

If you're using VS Code, you can use the [show syntax tree
command](https://shopify.github.io/ruby-lsp/#show-syntax-tree) to inspect the structure of an AST for an entire document
or selection.

For other editors, using our IRB configurations is the easiest way of achieving the same:

1. `bundle exec irb` to launch IRB with our configurations. It will require all libraries for you
2. Then parse the Ruby code you'd like to understand better and start inspecting

```ruby
source = <<~RUBY
class Foo
def bar
end
end
RUBY

ast = Prism.parse(source).value
```

With both approaches, there is a risk of 'breaking' your local development experience, so keep an eye on the Ruby LSP output panel for exceptions as your make changes.
Check the [Prism documentation](https://ruby.github.io/prism/) for more related information.

### Running the test suite
#### Tracing LSP requests and responses

The test suite can be executed by running
```shell
# The default runner
bin/test
# or the spec reporter
SPEC_REPORTER=1 bin/test
# Warnings are turned off by default. If you wish to see warnings use
VERBOSE=1 bin/test
# Run a single test like this: "bin/test my_test.rb test_name_regex", e.g.
bin/test test/requests/diagnostics_expectations_test.rb test_diagnostics__def_bad_formatting
In VS Code, you can verify what's happening in the server by enabling tracing, which allows for different levels of
logging.

```jsonc
{
// Your JSON settings
//
// - `off`: no tracing
// - `messages`: display requests and responses notifications
// - `verbose`: display each request and response as JSON
"ruby lsp.trace.server": "messages"
}
```

### Expectation testing
#### Manually testing a change

To simplify the way we run tests over different pieces of Ruby code, we use a custom expectations test framework against
a set of Ruby fixtures.
After you made a change or added a new feature, you can verify it in the editor by restarting the language server. In VS
Code, this can be achieved by running the command `Ruby LSP: restart`, which will reboot the server and pick up your
changes.

All fixtures are defined under `test/fixtures`. By default, every request will be executed against every fixture
and the test framework will assert that nothing was raised to verify if all requests succeed in processing all fixtures.
For other editors, you must manually restart the language server to pick up the latest changes.

Expectations can be added on a per request and per fixture basis. For example, we can have expectations for semantic
highlighting for a fixture called `foo.rb`, but no expectations for the same fixture for any other requests.
#### Debugging tests

We define expectations as `.exp` files, of which there are two variants:
- `.exp.rb`, to indicate the resulting code after an operation
- `.exp.json`, consisting of a `result`, and an optional set of input `params`
In VS Code, we recommend:

To add a new test scenario
1. Setting breakpoints in the UI
2. Opening the test that will hit that breakpoint
3. Clicking the `debug` code lens button on top of examples

1. Add a new fixture `my_fixture.rb` file under `test/fixtures`
2. For applicable requests, add expectation files related to this fixture. For example,
`test/expectations/semantic_highlighting/my_fixture.exp.json`
Alternatively (and for other editors), adding a `binding.b` statement in the code and executing the test in the terminal
will also allow you to debug the code.

To add a new expectations test runner for a new request handler:
### Writing tests

- Add a new file under `test/requests/MY_REQUEST_expectations_test.rb`
There are two types of tests in the Ruby LSP. The first type is likely familiar: standard Minitest files with a bunch of
examples inside using the method declaration syntax.

```ruby
require "test_helper"
require_relative "support/expectations_test_runner"

class MyRequestExpectationsTest < ExpectationsTestRunner
# The first argument is the fully qualified name of the request class
# The second argument is the folder name where the expectation files are
expectations_tests RubyLsp::Requests::MyRequest, "my_request"
end
```
The second type of test is our fixture/expectation framework. Adding a new fixture under `test/fixtures` will
automatically make the framework run all requests against that fixture. By default, the framework only checks that the
features don't crash when running against the fixture. This is useful for ensuring that critical requests don't break
when using less common Ruby syntax.

- (optional) The default behaviour of running tests is not always enough for every request. You may need to override
the base run and assert method to achieve the right behaviour. See `diagnostics_expectations_test.rb` for an
example
To go beyond checking if the requests break for a fixture, you can add an expectation to `test/expectations/NAME_OF_THE_REQUEST`, which allows you to assert the expected response for a request and fixture combination.

```ruby
require "test_helper"
require_relative "support/expectations_test_runner"
For example, if we have a `test/fixtures/foo.rb`, then adding a `test/expectations/semantic_highlighting/foo.exp.json` will make the framework verify that when running semantic highlighting in the `foo.rb` fixture, the `foo.exp.json` response is expected.

class MyRequestExpectationsTest < ExpectationsTestRunner
expectations_tests RubyLsp::Requests::MyRequest, "my_request"
Check existing fixture and expectation combinations for examples.

def run_expectations(source)
# Run your request for the given source
end
#### When to use each type of test

def assert_expectations(source, expected)
# Invoke run_expectations and then customize how to assert the correct responses
end
end
The fixture/expectation framework is intended to be used mostly by full document requests (language server features that
are computed for the entire file every time).

Requests and features that are position specific or that operate under a different mechanism should just use regular Minitest tests.

#### Running the test suite

There are multiple ways to execute tests available.

```shell
# Run the entire test suite
bundle exec rake

# Run only indexing tests
bundle exec rake test:indexer

# Run only language server tests (excluding indexing)
bundle exec rake test

# Using the custom test framework to run a specific fixture example
# bin/test test/requests/the_request_you_want_to_run_test.rb name_of_fixture
bin/test test/requests/diagnostics_expectations_test.rb def_bad_formatting
```

To automatically create or update `.exp.json` files, you can use the `WRITE_EXPECTATIONS` environment variable. For example:
Additionally, we use RuboCop for linting and Sorbet for type checking.

```shell
# Run linting
bundle exec rubocop

```sh
WRITE_EXPECTATIONS=1 bin/test test/requests/code_lens_expectations_test.rb
# Run type checking
bundle exec srb tc
```

## Debugging with VS Code
## Contributing to the VS Code extension

Before starting on this section, ensure that [dependencies are installed](#initial-setup).

In addition to what's described here, the [VS Code extension API documentation](https://code.visualstudio.com/api) is a
great place to gather more context about how extensions interact with the editor.

The VS Code extension currently has the following main parts:

## Debugging Tests
- Version manager integrations for Ruby environment activation
- A [ruby/debug](https://github.com/ruby/debug) client implementation
- A [test controller](https://code.visualstudio.com/docs/editor/testing) implementation
- A [Copilot chat participant](https://code.visualstudio.com/api/extension-guides/chat)
- A dependencies tree implementation
- The LSP client
- A workspace abstraction to represent each active workspace in the editor

1. Open the test file
2. Set breakpoints in the code as desired
3. Click the debug button on top of test examples
### Testing changes

## Live debugging
We try to ensure thorough testing as much as possible. However, some tests are difficult to write, in particular those
that interact with VS Code widgets.

1. Under `Run and Debug`, select `Run extension` and click the start button (or press F5)
2. The extension host window opened will be running the development version of the VS Code extension. Putting break
points in the extension code will allow debugging
3. If you wish to debug the server process, go under `Run and Debug` in the extension host window,
select `Attach to existing server` and click the start button (or press F5)
3. Add breakpoints and perform the actions necessary for triggering the requests you wish to debug
For example, if running the test displays a dialog, the test has no easy way of clicking a button on it to continue
execution. For these situations we use `sinon` to stub expected invocations and responses.

## Screen Captures
Note: `client.test.ts` is an integration style test that boots the development version of the `ruby-lsp` gem and runs
requests against it.

We include animated screen captures to illustrate Ruby LSP features.
For recording new captures, use [LICEcap](https://www.cockos.com/licecap/).
For consistency, install the Ruby LSP profile included with this repo.
Configure LICEcap to record at 24fps, at 640 x 480.
If appropriate, you can adjust the height of the capture, but keep the width at 640.
#### Running tests

## Spell Checking
The easiest way to run tests is by selecting the `Extension tests` task in `Run and debug` and clicking run. That will
run all tests and the results will appear in VS Code's debug console.

VS Code users will be prompted to enable the [Code Spell
Checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) extension. By
default this will be enabled for all workspaces, but you can choose to selectively enable or disable it per workspace.
Alternatively, you can also run the tests through the terminal, which will download a test VS Code version inside the
repository and run tests against it. You can avoid the download by running the tests through the launch task.

If you introduce a word which the spell checker does not recognize, you can add it to the `cspell.json` configuration
alongside your PR.
Note: it is not possible to run a single test file or example.

### Live debugging

It is possible to live debug the development version of the extension. Detailed information can be found in the [VS Code
extension documentation]((https://code.visualstudio.com/api)), but this section includes a short description.

Live debugging involves two VS Code windows. The first one is where you will be modifying the code and the second window
will be where the development version of the extension is going to be running. You want to change the code in the first
window, reload and verify the changes in the second window.

1. Start by launching the extension debugging with the `Run extension` task in the `Run and debug` panel. This will open the second VS Code window where the development version of the extension is running
2. Make the desired changes in the first original VS Code window
3. Click the reload button in the [debug toolbar](https://code.visualstudio.com/Docs/editor/debugging#_user-interface) to load your recent changes into the second VS Code window
4. Perform the actions to verify your changes in the second window

If you wish to perform step by step debugging, all you have to do is add breakpoints through the UI in the first window
where you are modifying the code - not in the second window where the development version of the extension is running.

## Contributing to documentation

The Ruby LSP uses [Jekyll](https://jekyllrb.com/) to generate the documentation, whose source lives under the `/jekyll`
folder. Before making any changes, ensure you [performed initial setup](#initial-setup).

After that, follow these steps to make and verify your changes:

1. Make the desired changes
2. Launch jekyll in development
```shell
bundle exec jekyll serve
```
3. Verify your changes locally by visiting http://localhost:4000/ruby-lsp

0 comments on commit b8d3954

Please sign in to comment.