Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework "Configuration" and "Manually testing HLS" documentations #3772

Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 42 additions & 22 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,83 +90,97 @@ Settings like this are typically be provided by the language-specific LSP client
## Configuring your project build

`haskell-language-server` has to compile your project in order to give you diagnostics, which means that it needs to know how to do so.
This is handled by the [hie-bios](https://github.com/mpickering/hie-bios) project.
This is handled under the hood by the [hie-bios](https://github.com/mpickering/hie-bios) application.
In turn, `hie-bios` needs some configuration to identify all files, GHC options, etc., needed to compile a project.

**For a full explanation of how `hie-bios` determines the project build configuration, and how to configure it manually, refer to the [hie-bios README](https://github.com/mpickering/hie-bios/blob/master/README.md).**
There are several ways to provide this configuration to `hie-bios`, detailed below.

At the moment, `haskell-language-server` has support to automatically detect your project build configuration to handle most use cases.
### Implicit configuration
If no `hie.yaml` file is present, `haskell-language-server` automatically detects your `hie-bios` configuration.
**For most cases, this works just fine, and is the recommended way.**

*So using a explicit `hie.yaml` file will not likely fix your ide setup*. It will do it almost only if you see an error like `Multi Cradle: No prefixes matched`
See [the hie-bios documentation](https://github.com/haskell/hie-bios#implicit-configuration) for more information on implicit configuration.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this is quite accurate. I believe we in fact will use implicit-hie in this instance, not the hie-bios implicit auto-configuration...

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 739e58f


### Explicit, generated configuration
Maybe using the implicit configuration does not suit you.
E.g., it does not work, or you prefer to have explicit configuration in your project.
In that case, you can automatically generate a `hie.yaml` file, using [implicit-hie](https://github.com/Avi-D-coder/implicit-hie):

```shell
gen-hie > hie.yaml # In the root directory of your project
```

### Explicit, manual configuration
Maybe using the generated `hie.yaml` file does not suit you.
E.g., it still does not work, or you want to fine-tune the configuration.
We recommend to start from the `hie.yaml` file generated by `implicit-hie` (see previous section) and modify it to suit your needs.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this recommendation is no longer quite right for cabal projects at least. For cabal projects you can often use the "empty" cabal cradle, which often even works better than the one generated by implicit-hie. However for stack I believe the implicit-hie one is better.

This situation is a mess, unfortunately.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reworked and simplified a bit this section in 63ba56a

Basically rather than trying to re-explain the hie-bios documentation, we directly link to the relevant documentation


If the automatic detection fails with that error you can configure `hie-bios` using a `hie.yaml` file in the root of the workspace.
A `hie.yaml` file **explicitly** describes how to setup the environment to compile the various parts of your project.
For that you need to know what *components* your project has, and the path associated with each one.
So you will need some knowledge about
[stack](https://docs.haskellstack.org/en/stable/build_command/#components) or [cabal](https://cabal.readthedocs.io/en/latest/cabal-commands.html?#cabal-v2-build) components.

You also can use [implicit-hie](https://github.com/Avi-D-coder/implicit-hie) to automatically generate `hie.yaml` files for
the most common stack and cabal configurations
**For a full explanation of how to configure it manually, refer to the [hie-bios documentation](https://github.com/mpickering/hie-bios/blob/master/README.md).**

#### Examples of explicit `hie-yaml` configurations
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should include the "empty cabal cradle" which mostly works for everything now:

cradle:
  cabal:

(you might ask "why doesn't implicit-hie generate this?" which is a good question...)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, I added it in 63ba56a


For example, to state that you want to use `stack` then the configuration file
would look like:
##### Single Stack component

```yaml
cradle:
stack:
component: "haskell-language-server:lib"
```

If you use `cabal` then you probably need to specify which component you want
to use.
##### Single Cabal component

```yaml
cradle:
cabal:
component: "lib:haskell-language-server"
```

If you have a project with multiple components, you can use a cabal-multi
cradle:
##### Multiple Stack components

```yaml
cradle:
cabal:
stack:
- path: "./test/functional/"
component: "haskell-language-server:func-test"
- path: "./test/utils/"
component: "haskell-language-server:hls-test-utils"
- path: "./exe/Main.hs"
component: "haskell-language-server:exe:haskell-language-server"
- path: "./exe/Wrapper.hs"
component: "haskell-language-server:exe:haskell-language-server-wrapper"
- path: "./src"
component: "lib:haskell-language-server"
component: "haskell-language-server:lib"
- path: "./ghcide/src"
component: "ghcide:lib:ghcide"
- path: "./ghcide/exe"
component: "ghcide:exe:ghcide"
```

Equivalently, you can use stack:
##### Multiple Cabal components

```yaml
cradle:
stack:
cabal:
- path: "./test/functional/"
component: "haskell-language-server:func-test"
- path: "./test/utils/"
component: "haskell-language-server:hls-test-utils"
- path: "./exe/Main.hs"
component: "haskell-language-server:exe:haskell-language-server"
- path: "./exe/Wrapper.hs"
component: "haskell-language-server:exe:haskell-language-server-wrapper"
- path: "./src"
component: "haskell-language-server:lib"
component: "lib:haskell-language-server"
- path: "./ghcide/src"
component: "ghcide:lib:ghcide"
- path: "./ghcide/exe"
component: "ghcide:exe:ghcide"
```

Or you can explicitly state the program which should be used to collect
##### Custom program
You can explicitly state the program which should be used to collect
the options by supplying the path to the program. It is interpreted
relative to the current working directory if it is not an absolute path.

Expand Down Expand Up @@ -196,6 +210,12 @@ dependencies:
- someDep
```

### Common problems
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should go in the Troubleshooting page, perhaps?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, done in 386f87c


#### Multi Cradle: No prefixes matched
The error message `Multi Cradle: No prefixes matched` usually means that implicit configuration failed.
In that case, you must use explicit configuration.

### How to show local documentation on hover

Haskell Language Server can display Haddock documentation on hover and completions if the project and
Expand Down
96 changes: 45 additions & 51 deletions docs/contributing/contributing.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,79 +108,73 @@ $ cabal run haskell-language-server:func-test -- -p "hlint enables"

## Using HLS on HLS code

Project source code should load without `hie.yaml` setup.
Refer to the [HLS project configuration guidelines](../configuration.md#configuring-your-project-build) as they also apply to the HLS project itself.

In other cases:

1. Check if `hie.yaml` (& `hie.yml`) files left from previous configurations.

2. If the main project needs special configuration, note that other internal subprojects probably also would need configuration.

To create an explicit configuration for all projects - use [implicit-hie](https://github.com/Avi-D-coder/implicit-hie) generator directly:

```shell
gen-hie > hie.yaml # into the main HLS directory
```

that configuration should help.

3. Inspect & tune configuration explicitly.

[Configuring project build](../configuration.md#configuring-your-project-build) applies to HLS project source code loading just as to any other.

Note: HLS may implicitly detect codebase as a Stack project (see [hie-bios implicit configuration documentation](https://github.com/haskell/hie-bios/blob/master/README.md#implicit-configuration)). To use Cabal, try creating an `hie.yaml` file:
Note: HLS implicitly detects the HLS codebase as a Stack project (since there is a `stack.yaml` file).
If you want HLS to use Cabal, create this `hie.yaml` file at the root of the project:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍


```yaml
cradle:
cabal:
```

### Manually testing your hacked HLS
If you want to test HLS while hacking on it, follow the steps below.

#### Using Cabal
## Manually testing your hacked HLS
If you want to test HLS while hacking on it (you can even test it on HLS codebase itself, see previous section), you need to:

- Whenever you want to build HLS, call `cabal install exe:haskell-language-server --overwrite-policy=always`.
At the end of the output you will find the path the HLS executable was installed to, i.e.:
1. (Once) Find the path to the hacked HLS you build
2. (Once) Configure your editor to use it
3. (Every time you change the HLS code) Rebuild HLS
4. (Every time you change the HLS code) Restart the LSP workspace

```
...
Resolving dependencies...
Symlinking 'haskell-language-server' to
'/home/user/.cabal/bin/haskell-language-server'
Symlinking 'haskell-language-server-wrapper' to
'/home/user/.cabal/bin/haskell-language-server-wrapper'
```
### Find the path to the hacked HLS you build
Note that unless you change the GHC version or the HLS version between builds, the path should remain the same, this is why you need to set it only once.

In this example output, the path would be `/home/user/.cabal/bin/haskell-language-server`.
#### Using Cabal
Run:
```shell
$ cabal build exe:haskell-language-server && cabal list-bin exe:haskell-language-server
[..]
<some long path>/haskell-language-server
```
Comment on lines +133 to +138
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I know I am a little bit late to the party, but I feel like using the cabal build binary for testing can be slightly unexpected.
When you switch branches and run cabal build just to make sure it compiles, or to speed up HLS to load everything, then you suddenly update your test binary.
Such behaviour might be unexpected to non-expert users, perhaps even expert users. I, for one, definitely prefer cabal install since I have a couple of local HLS installations lying around.

That's why we previously recommended using cabal install, updating your tested HLS binary is a very conscious decision. Additionally, it allows you to set program-suffixes and other custom locations.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Arguably the opposite argument is as inconvenient for others 😅

  1. cabal install will install in a global location outside of the project, so it makes it harder to keep things isolated
  2. It might replace the stable binary they had installed in the past, and they might get confused about how to get back to the stable binary
  3. With the cabal build approach, you can "undo" all the steps in the documentation and land back on your original setup. With the cabal install approach, "undoing" the steps may not be enough to get back to the original setup (because you also need to know how to get back the original binary, which you may have installed long ago, and you did not know which version you had).

I guess different people have different priorities 😅 And it's completely ok by the way!
In my opinion the new version of the documentation is more welcoming towards non-experts (ability to undo), and while some experts may get surprised by the scenario you mentioned, I think they would be able to identify and work around.

In any case it might also be beneficial to add this caveat to the documentation, if you suspect many will get confused by it

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough, the arguments are also convincing. Ideally, we'd have a command cabal build --copy-to-local-bindir, that copies the binary to dist-newstyle/bin/ or something similar.

What do you think about instructions such as cabal install --installdir dist-newstyle/bin/? Arguably, we shouldn't touch dist-newstyle, but I think that's relatively safe.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FWIW I use cabal build and it's fine, but it seems reasonable to include both as alternatives?


- Open some codebase on which you want to test your local HLS in your favorite editor (it can also be the HLS codebase itself: see previous section for configuration)
- Configure this editor to use your custom HLS executable by using the path you obtained previously.
- Restart HLS in your project:
- With VS Code: Press `CTRL + Shift + P` and type `Haskell: Restart Haskell LSP Server`
- With Emacs: `lsp-workspace-restart`
#### Using Stack
Run:
```shell
$ echo $(pwd)/$(stack path --dist-dir)/build/haskell-language-server/haskell-language-server
[..]
<some long path>/haskell-language-server
```

##### VS Code
### Configure your editor to use it

When using VS Code you can set up a test project to use a specific HLS executable:
#### VS Code
When using VS Code you can set up each project to use a specific HLS executable:

- If it doesn't already exist in your project directory, create a directory called `.vscode`.
- In the `.vscode` directory create a file called `settings.json` with the below contents. The path used here is the one obtained by using the `cabal` install command.

- In the `.vscode` directory create a file called `settings.json` with the below contents.
```json
{
"haskell.serverExecutablePath": "/home/user/.cabal/bin/haskell-language-server"
"haskell.serverExecutablePath": "/path/to/your/hacked/haskell-language-server"
}
```

#### Using Stack
#### Emacs
There are several ways to configure the HLS server path:
- `M-x customize-group<RET>lsp-haskell<RET>Lsp Haskell Server Path`
- Evaluate `(setq lsp-haskell-server-path "/path/to/your/hacked/haskell-language-server")`
- Create a file `.dir-locals.el` with the following content:
```lisp
((haskell-mode . ((lsp-haskell-server-path . "/path/to/your/hacked/haskell-language-server"))))
```

### Rebuild HLS
- With Stack: `stack build haskell-language-server:exe:haskell-language-server`
- With Cabal: `cabal build exe:haskell-language-server`

- Open some codebase on which you want to test your local HLS in your favorite editor (it can also be the HLS codebase itself: see previous section for configuration)
- Configure this editor to use your custom HLS executable
- To obtain the path to your local HLS executable: `$(stack path --dist-dir)/build/haskell-language-server/haskell-language-server`
### Restart the LSP workspace

- Build HLS
- `stack build haskell-language-server:exe:haskell-language-server`
- With VS Code: Press `Ctrl + Shift + p` and type `Haskell: Restart Haskell LSP Server`
- With Emacs: `M-x lsp-workspace-restart`

## Style guidelines

Expand Down