|
| 1 | +# Contributing |
| 2 | + |
| 3 | +This document provides information for developers who want to contribute to the |
| 4 | +RLS or run it in a heavily customised configuration. |
| 5 | + |
| 6 | +The RLS is open source and we'd love you to contribute to the project. Testing, |
| 7 | +reporting issues, writing documentation, writing tests, writing code, and |
| 8 | +implementing clients are all extremely valuable. |
| 9 | + |
| 10 | +Here is the list of known [issues](https://github.com/rust-lang-nursery/rls/issues). |
| 11 | +These are [good issues to start on](https://github.com/rust-lang-nursery/rls/issues?q=is%3Aopen+is%3Aissue+label%3Aeasy). |
| 12 | + |
| 13 | +We're happy to help however we can. The best way to get help is either to |
| 14 | +leave a comment on an issue in this repo, or to ping us (nrc or jntrnr) in #rust-tools |
| 15 | +on IRC. |
| 16 | + |
| 17 | +We'd love for existing and new tools to use the RLS. If that sounds interesting |
| 18 | +please get in touch by filing an issue or on IRC. |
| 19 | + |
| 20 | + |
| 21 | +## Building |
| 22 | + |
| 23 | +Since the RLS is closely linked to the compiler and is in active development, |
| 24 | +you'll need a recent nightly compiler to build it. |
| 25 | + |
| 26 | +Use `cargo build` to build. |
| 27 | + |
| 28 | +See [README.md](README.md) for more setup steps. |
| 29 | + |
| 30 | +## Running and testing |
| 31 | + |
| 32 | +You can run the rls by hand with: |
| 33 | + |
| 34 | +``` |
| 35 | +cargo run |
| 36 | +``` |
| 37 | + |
| 38 | +Though more commonly, you'll use an IDE plugin to invoke it for you. |
| 39 | + |
| 40 | +Test using `RUST_TEST_THREADS=1 cargo test`. |
| 41 | + |
| 42 | +Testing is unfortunately minimal. There is support for regression tests, but not |
| 43 | +many actual tests exists yet. There is signifcant [work to do](https://github.com/rust-lang-nursery/rls/issues/12) |
| 44 | +before we have a comprehensive testing story. |
| 45 | + |
| 46 | + |
| 47 | +## Standard library support |
| 48 | + |
| 49 | +The way it works is that when the libraries are built, the compiler can emit all |
| 50 | +the data that the RLS needs. This can be read by the RLS on startup and used to |
| 51 | +provide things like type on hover without having access to the source code for |
| 52 | +the libraries. |
| 53 | + |
| 54 | +The compiler gives every definition an id, and the RLS matches up these ids. In |
| 55 | +order for the RLS to work, the id of a identifier used in the IDE and the id of |
| 56 | +its declaration in a library must match exactly. Since ids are very unstable, |
| 57 | +the data used by the RLS for libraries must match exactly with the crate that |
| 58 | +your source code links with. |
| 59 | + |
| 60 | +You need a version of the above data which exactly matches the standard |
| 61 | +libraries you will use with your project. Rustup takes care of this for you and |
| 62 | +is the preferred (and easiest) method for installing this data. If you want to |
| 63 | +use the RLS with a Rust compiler/libraries you have built yourself, then you'll |
| 64 | +need to take some extra steps. |
| 65 | + |
| 66 | + |
| 67 | +### Install with rustup |
| 68 | + |
| 69 | +You'll need to be using [rustup](https://www.rustup.rs/) to manage your Rust |
| 70 | +compiler toolchains. The RLS does not yet support cross-compilation - your |
| 71 | +compiler host and target must be exactly the same. |
| 72 | + |
| 73 | +You must be using nightly (you need to be using nightly for the RLS to work at |
| 74 | +the moment in any case). To install a nightly toolchain use `rustup install |
| 75 | +nightly`. To switch to using that nightly toolchain by default use `rustup |
| 76 | +default nightly`. |
| 77 | + |
| 78 | +Add the RLS data component using `rustup component add rust-analysis`. |
| 79 | + |
| 80 | +Everything should now work! You may need to restart the RLS. |
| 81 | + |
| 82 | + |
| 83 | +### Build it yourself |
| 84 | + |
| 85 | +When you build Rust, add `-Zsave-analysis-api` to your stage 2 flags, e.g., by |
| 86 | +setting the environment variable: |
| 87 | + |
| 88 | +``` |
| 89 | +export RUSTFLAGS_STAGE2='-Zsave-analysis-api' |
| 90 | +``` |
| 91 | + |
| 92 | +When the build has finished, you should have a bunch of JSON data in a directory like |
| 93 | +`~/rust1/build/x86_64-unknown-linux-gnu/stage1-std/x86_64-unknown-linux-gnu/release/deps/save-analysis`. |
| 94 | + |
| 95 | +You need to copy all those files (should be around 16) into a new directory: |
| 96 | +`~/rust1/build/x86_64-unknown-linux-gnu/stage2/lib/rustlib/x86_64-unknown-linux-gnu/analysis` |
| 97 | +(assuming you are running the stage 2 compiler you just built. You'll need to |
| 98 | +modify the root directory (`~/rust1` here) and the host triple |
| 99 | +(`x86_64-unknown-linux-gnu` in both places)). |
| 100 | + |
| 101 | + |
| 102 | +Finally, to run the RLS you'll need to set things up to use the newly built |
| 103 | +compiler, something like: |
| 104 | + |
| 105 | +``` |
| 106 | +export RUSTC="~/rust1/build/x86_64-unknown-linux-gnu/stage2/bin/rustc" |
| 107 | +``` |
| 108 | + |
| 109 | +Either before you run the RLS, or before you run the IDE which will start the |
| 110 | +RLS. |
| 111 | + |
| 112 | + |
| 113 | +### Details |
| 114 | + |
| 115 | +Rustup (or you, manually) will install the rls data (which is a bunch of json |
| 116 | +files) into `$SYSROOT/lib/rustlib/$TARGET_TRIPLE/analysis`, where `$SYSROOT` is |
| 117 | +your Rust sysroot, this can be found using `rustc --print=sysroot`. |
| 118 | +`$TARGET_TRIPLE` is the triple which defines the compilation target. Since the |
| 119 | +RLS currently does not support cross-compilation, this must match your host |
| 120 | +triple. It will look something like `x86_64-unknown-linux-gnu`. |
| 121 | + |
| 122 | +For example, on my system RLS data is installed at: |
| 123 | + |
| 124 | +``` |
| 125 | +/home/ncameron/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/x86_64-unknown-linux-gnu/analysis |
| 126 | +``` |
| 127 | + |
| 128 | +This data is only for the standard libraries, project-specific data is stored |
| 129 | +inside your project's target directory. |
| 130 | + |
| 131 | + |
| 132 | +## Implementation overview |
| 133 | + |
| 134 | +The goal of the RLS project is to provide an awesome IDE experience *now*. That |
| 135 | +means not waiting for incremental compilation support in the compiler. However, |
| 136 | +Rust is a somewhat complex language to analyse and providing precise and |
| 137 | +complete information about programs requires using the compiler. |
| 138 | + |
| 139 | +The RLS has two data sources - the compiler and Racer. The compiler is always |
| 140 | +right, and always precise. But can sometimes be too slow for IDEs. Racer is |
| 141 | +nearly always fast, but can't handle some constructs (e.g., macros) or can only |
| 142 | +handle them with limited precision (e.g., complex generic types). |
| 143 | + |
| 144 | +The RLS tries to provide data using the compiler. It sets a time budget and |
| 145 | +queries both the compiler and Racer. If the compiler completes within the time |
| 146 | +budget, we use that data. If not, we use Racer's data. |
| 147 | + |
| 148 | +We link both Racer and the compiler into the RLS, so we don't need to shell out |
| 149 | +to either (though see notes on the build process below). We also customise our |
| 150 | +use of the compiler (via standard APIs) so that we can read modified files |
| 151 | +directly from memory without saving them to disk. |
| 152 | + |
| 153 | +### Building |
| 154 | + |
| 155 | +The RLS tracks changes to files, and keeps the changed file in memory (i.e., the |
| 156 | +RLS does not need the IDE to save a file before providing data). These changed |
| 157 | +files are tracked by the 'Virtual File System' (which is a bit of a grandiose |
| 158 | +name for a pretty simple file cache at the moment, but I expect this area to |
| 159 | +grow significantly in the future). The VFS is in a [separate |
| 160 | +crate](https://github.com/nrc/rls-vfs). |
| 161 | + |
| 162 | +We want to start building before the user needs information (it would be too |
| 163 | +slow to start a build when data is requested). However, we don't want to start a |
| 164 | +build on every keystroke (this would be too heavy on user resources). Nor is |
| 165 | +there any point starting multiple builds when we would throw away the data from |
| 166 | +some of them. We therefore try to queue up and coalesce builds. This is further |
| 167 | +documented in [src/build.rs](src/build.rs). |
| 168 | + |
| 169 | +When we do start a build, we may also need to build dependent crates. We |
| 170 | +therefore do a full `cargo build`. However, we do not compile the last crate |
| 171 | +(the one the user is editing in the IDE). We only run Cargo to get a command |
| 172 | +line to build that crate. Furthermore, we cache that command line, so for most |
| 173 | +builds (where we don't need to build dependent crates, and where we can be |
| 174 | +reasonably sure they haven't changed since a previous build) we don't run Cargo |
| 175 | +at all. |
| 176 | + |
| 177 | +The command line we got from Cargo, we chop up and feed to the in-process |
| 178 | +compiler. We then collect error messages and analysis data in JSON format |
| 179 | +(although this is inefficient and [should |
| 180 | +change](https://github.com/rust-lang-nursery/rls/issues/25)). |
| 181 | + |
| 182 | +### Analysis data |
| 183 | + |
| 184 | +From the compiler, we get a serialised dump of its analysis data (from name |
| 185 | +resolution and type checking). We combine data from all crates and the standard |
| 186 | +libraries and combine this into an index for the whole project. We cross- |
| 187 | +reference and store this data in HashMaps and use it to look up data for the |
| 188 | +IDE. |
| 189 | + |
| 190 | +Reading, processing, and storing the analysis data is handled by the |
| 191 | +[rls-analysis crate](https://github.com/nrc/rls-analysis). |
| 192 | + |
| 193 | +### Communicating with IDEs |
| 194 | + |
| 195 | +The RLS communicates with IDEs via |
| 196 | +the [Language Server protocol](https://github.com/Microsoft/language-server-protocol/blob/master/protocol.md). |
| 197 | + |
| 198 | +The LS protocol uses JSON sent over stdin/stdout. The JSON is rather dynamic - |
| 199 | +we can't make structs to easily map to many of the protocol objects. The client |
| 200 | +sends commands and notifications to the RLS. Commands must get a reply, |
| 201 | +notifications do not. Usually the structure of the reply is dictated by the |
| 202 | +protocol spec. The RLS can also send notifications to the client. So for a long |
| 203 | +running task (such as a build), the RLS will reply quickly to acknowledge the |
| 204 | +request, then send a message later with the result of the task. |
| 205 | + |
| 206 | +Associating requests with replies is done using an id which must be handled by |
| 207 | +the RLS. |
| 208 | + |
| 209 | + |
| 210 | +### Extensions to the Language Server Protocol |
| 211 | + |
| 212 | +The RLS uses some custom extensions to the Language Server Protocol. Currently |
| 213 | +these are all sent from the RLS to an LSP client and are only used to improve |
| 214 | +the user experience by showing progress indicators. |
| 215 | + |
| 216 | +* `rustDocument/diagnosticsBegin`: notification, no arguments. Sent before a |
| 217 | + build starts and before any diagnostics from a build are sent. |
| 218 | +* `rustDocument/diagnosticsEnd`: notification, no arguments. Sent when a build |
| 219 | + is complete (successfully or not, or even skipped) and all post-build analysis |
| 220 | + by the RLS is complete. |
| 221 | + |
| 222 | + |
0 commit comments