-
Notifications
You must be signed in to change notification settings - Fork 335
LSP: Getting started
The cue command has a built-in language server that clients can access using LSP - the Language Server Protocol. Language servers provide language-specific support for editors.
The earliest version of CUE that has a language server is v0.15.0-alpha.1.
Guidance for every possible editor is beyond our means. In general, you need to:
- Install the
cuebinary and make sure it's available on yourPATH. v0.15.0-alpha.1 is the earliest version of CUE that has a language server. - Your editor should be configured to use the command
cue lsp serveto start the language server.
The official CUE extension is available on the VSCode Marketplace and OpenVSX. If cue is installed on your machine, the extension should find it and enable the language server automatically.
Our CUE plugin is available on the marketplace. Language server support is not yet available for this extension (work in progress), but the LSP4IJ plugin can be used, for example.
The lsp-mode package works with CUE's language server. Something like this should work:
(use-package lsp-mode
:commands (lsp lsp-deferred)
:init
(with-eval-after-load 'lsp-mode
(add-to-list 'lsp-language-id-configuration '(cue-mode . "cue"))
(lsp-register-client (make-lsp-client
:new-connection (lsp-stdio-connection (list "cue" "lsp" "serve"))
:activation-fn (lsp-activate-on "cue")
:server-id 'cuelsp))
)
:hook ((cue-mode . lsp-deferred))
)
(use-package cue-mode)[language-server.cuelsp]
command = "cue"
args = ["lsp", "serve"]
[[language]]
name = "cue"
language-servers = [ "cuelsp" ]
file-types = ["cue"]
auto-format = true
comment-token = "//"
indent = { tab-width = 3, unit = "\t" }Tested with Neovim v0.11.x. Install nvim-lspconfig via :
git clone https://github.com/neovim/nvim-lspconfig ~/.config/nvim/pack/nvim/start/nvim-lspconfig
or via a 3rd-party plugin manager.
Then use something like this minimal ~/.config/nvim/init.lua:
vim.cmd([[syntax on]])
vim.cmd([[filetype plugin indent on]])
-- Enable the CUE language server
vim.lsp.enable("cue")
-- Go-to-definition on Ctrl-]
vim.keymap.set("n", "<C-]>", vim.lsp.buf.definition, { desc = "LSP Definition" })
-- Hover on Ctrl-h
vim.keymap.set("n", "<C-h>", vim.lsp.buf.hover, { desc = "LSP Hover" })
-- Optional: set trace level logging (logs at ~/.local/state/nvim/lsp.log)
vim.lsp.set_log_level('trace')
-- Optional: format on save
vim.api.nvim_create_autocmd("BufWritePre", {
pattern = "*.cue",
callback = function()
vim.lsp.buf.format({ async = false })
end,
})Add to your ~/.vim/after/plugin/vim-lsp.vim
if executable('cue')
au User lsp_setup call lsp#register_server({
\ 'name': 'cue lsp',
\ 'cmd': {server_info->['cue', 'lsp', 'serve']},
\ 'allowlist': ['cue'],
\ })
endifA community-supported extension is available
The foundation of CUE's language server is based on Go's gopls LSP server. As such, it supports the same remote mode of operation. This means you can configure it to listen on a socket (e.g. cue lsp serve -listen='unix;/run/user/1000/cuelsp/cuelsp') and then configure your editor to connect to that socket (e.g. cue lsp -remote='unix;/run/user/1000/cuelsp/cuelsp')
As of CUE v0.15.0-alpha.1, the CUE language server supports:
There are two modes for this. Firstly, from a value, goto-definition will take you to all the places where that value is defined. For example:
```cue
num: int
num: > 6
num: 17
out: 3 + num
```
If you place your cursor on the `num` in the last line, and goto-definition, then you will be shown all 3 definitions of `num` on the previous lines.
The other mode is to place your cursor on a field name itself:
```cue
#Schema {
name!: string
age!: int
}
data: #Schema
data: age: 0
```
If you place your cursor on the `age` field in the last line, and goto-definition, then you will be taken to the definition of `age` within the `#Schema` struct.
Completions are provided both for field names and for values.
```cue
num: int
num: > 6
num: 17
out: 3 + n
```
As you type the `n` on the last line, `num` will be suggested.
```cue
#Schema {
name!: string
age!: int
}
data: #Schema
data: a
```
As you type the `a` on the last line, `age:` will be suggested as a field name.
Hover is used to display relevant documentation in your editor as you "hover over" (or similar) a value. This works exactly the same way as Goto-definition; it works for field values:
```cue
// num must be an int
num: int
// num must be greater than six
num: > 6
num: 17
out: 3 + num
```
Hovering over `num` on the last line will show the docs from lines 1 and 3.
It also works for fields:
```cue
#Schema {
// The name of the thing
name!: string
// The age of the thing
age!: int
}
data: #Schema
data: age: 0
```
Hovering over `age` on the last line will show the docs for the `age` field within `#Schema`.
The CUE language server can format entire files. This means you can configure your editor to "format on save" if you wish to.
The language server does not currently attempt to fully evaluate CUE; this could take a long time, and/or require additional configuration. Instead, it performs a "static analysis": analysing your code without running it. This is very similar to how type-checking is implemented in many "statically typed" languages. Although a simple unification of struct fields is performed, and attempts are made to resolve references, there is no evaluation of expressions in general. Field names are not tested against patterns, and dynamic fields are not evaluated.
Dynamic indexing with a constant will work. For example:
[8, {a: "hi"}, {a: true}][1].aIf you goto-definition from the final a, then that will jump to the a: "hi" field because the constant index [1] is understood. But if this constant index is made into a dynamic expression then the language server will not attempt to evaluate that expression. For example:
n: 1
[8, {a: "hi"}, {a: true}][n*1].aAs of v0.15.0-alpha.1, the language server will currently only support CUE files within modules, and that have a package declaration.