languageserver is an implementation of the Microsoft's Language Server Protocol for the language of R.
A few dependencies are required beforehand:
# On Debian, Ubuntu, etc.
apt install --assume-yes --no-install-recommends build-essential libcurl4-openssl-dev libssl-dev libxml2-dev libuv1-dev r-base
# On Fedora, Centos, etc.
dnf install --assumeyes --setopt=install_weak_deps=False @development-tools libcurl-devel libxml2-devel openssl-devel libuv-devel R
# On Alpine
apk add --no-cache curl-dev g++ gcc libxml2-dev linux-headers make R R-devlanguageserver is released on CRAN and can be easily installed by
install.packages("languageserver")The default diagnostics and formatting backends require optional R packages:
install.packages(c("lintr", "styler"))For a minimal source installation, install mandatory dependencies only and select the CLI backends below, or disable diagnostics. Completion, navigation and other language features work without lintr or styler. Selecting an unavailable backend reports an actionable error; the server does not choose a different backend.
To try the latest features, install the daily development build from our r-universe repository:
install.packages("languageserver", repos = c(
reditorsupport = "https://reditorsupport.r-universe.dev",
getOption("repos")
))Or install the latest development version from our GitHub repository:
# install.packages("remotes")
remotes::install_github("REditorSupport/languageserver")Workspace discovery and indexing run in the background. Clients that advertise LSP work-done progress support can show an Indexing R workspace indicator while R files are discovered, indexed, and parsed. Reports include file counts and a percentage once discovery completes. The client chooses whether to show this in its status bar, progress panel, or another location.
The following editors are supported by installing the corresponding extensions:
-
VS Code: vscode-R
-
Atom: atom-ide-r
-
Sublime Text: R-IDE
-
NeoVim: NeoVim's LSP client with settings
vim.lsp.config['r_language_server'] = { settings = { filetypes = { "r", "rmd" }, }, } vim.api.nvim_create_autocmd("FileType", { pattern = { "r", "rmd" }, callback = function() vim.lsp.start(vim.lsp.config["r_language_server"]) end, }
or, if you use coc.nvim, you can do one of two things:
-
Install coc-r-lsp with:
:CocInstall coc-r-lsp
-
or install the languageserver package in R
install.packages("languageserver") # or install the developement version # remotes::install_github("REditorSupport/languageserver")
Then add the following to your Coc config:
"languageserver": { "R": { "command": "/usr/bin/R", "args" : [ "--no-echo", "-e", "languageserver::run()"], "filetypes" : ["r"] } }
-
-
Emacs: eglot-mode (native LSP client)
(use-package ess :ensure t) (add-hook 'ess-r-mode-hook 'eglot-ensure)
To check if it is working, open an R file, place the cursor on a line and run
M-x ess-eval-line. -
Emacs: lsp-mode
-
JupyterLab: jupyterlab-lsp
-
BBEdit: preconfigured in version 14.0 and later; see the BBEdit LSP support page for complete details.
languageserver is still under active development, the following services have been implemented:
- textDocumentSync
- publishDiagnostics
- hoverProvider
- completionProvider
- completionItemResolve
- signatureHelpProvider
- definitionProvider
- referencesProvider
- documentHighlightProvider
- documentSymbolProvider
- workspaceSymbolProvider
- codeActionProvider
- codeLensProvider
- documentFormattingProvider
- documentRangeFormattingProvider
- documentOnTypeFormattingProvider
- renameProvider
- prepareRenameProvider
- documentLinkProvider
- documentLinkResolve
- colorProvider
- colorPresentation
- foldingRangeProvider
- selectionRangeProvider
- prepareCallHierarchy
- callHierarchyIncomingCalls
- callHierarchyOutgoingCalls
- prepareTypeHierarchy
- typeHierarchySupertypes
- typeHierarchySubtypes
- semanticTokens
- linkedEditingRange
- executeCommandProvider
- inlineValueProvider
- inlayHintProvider
Inline values are a debugger-facing feature rather than normal editor
annotations. When execution is paused, a compatible editor and debug adapter
request variable lookup ranges from the language server, evaluate them in the
selected stack frame, and render the resulting values beside the source. They
have no visible effect when the client or R debug adapter does not request
textDocument/inlineValue.
R6 member inference has a known limitation after field assignments inside a
method. The written reference keeps its class context, but field information
is not synchronized across aliases or the saved receivers for private and
super methods. For example, after alias <- self; self$data <- list(new = 1),
completion on alias$data$ may still use the earlier data value. The same
can occur when a private or superclass method returns self$data.
Argument completion, hover and signature help select an ordinary S3 method when
the input object's dispatch classes can be inferred from source or package
metadata. This includes literal classes, aliases, simple source factories,
package datasets and guarded constructor summaries such as data.frame() and
lm(). Namespace-qualified and piped calls use the same resolver.
For example, summary(iris, ...) uses summary.data.frame, while
predict(lm(mpg ~ wt, data = mtcars), ...) uses predict.lm. Method-specific
argument names, literal argument choices and parameter documentation are shown
without executing document code, constructors, methods or defaults. See
the demo script.
With datawizard installed, datawizard::rescale(iris, ...) uses its registered
rescale.data.frame method and offers arguments such as select, exclude
and append before the document is executed.
Unknown or ambiguous classes retain the generic's information. Dynamic method
registration, opaque reflection and unsupported constructor transformations
also retain the generic fallback. Group/internal generics and additional
parameters forwarded through sophisticated NextMethod() chains are outside
this inference path. A method's extra arguments are offered only when the
generic can accept them.
languageserver exposes the following settings via LSP configuration.
| settings | default | description |
|---|---|---|
r.lsp.debug |
false |
increase verbosity for debug purpose |
r.lsp.log_file |
null |
file to log debug messages, fallback to stderr if empty |
r.lsp.diagnostics |
true |
enable diagnostics from the selected backend |
r.lsp.diagnostics_backend |
"lintr" |
diagnostics backend: lintr or jarl |
r.lsp.formatting_backend |
"styler" |
formatting backend: styler or air |
r.lsp.backend_options |
{} |
per-CLI-backend executable, timeout in seconds (default 10), and environment overrides |
r.lsp.inlay_hints_minimum_arguments |
2 |
minimum supplied arguments before parameter-name inlay hints are shown |
r.lsp.inlay_hints_minimum_argument_length |
2 |
minimum argument-name length for an inlay hint, excluding an initial . |
r.lsp.rich_documentation |
true |
rich documentation with enhanced markdown features |
r.lsp.snippet_support |
true |
enable snippets in auto completion |
r.lsp.max_completions |
200 | maximum number of completion items |
r.lsp.s3_dispatch |
true |
use static S3 dispatch for argument completion, hover and signature help, independently of member completion |
r.lsp.member_completion |
true |
infer $ members and S4 @ slots for completion, signature help and hover from document syntax and cached package metadata without executing document code |
r.lsp.lint_cache |
false |
toggle caching of lint results |
r.lsp.parse_delay |
0.15 |
seconds to debounce parsing after an edit |
r.lsp.diagnostics_delay |
0.75 |
seconds to debounce diagnostics after the current parse |
r.lsp.parse_cache_max_mb |
64 |
maximum memory used by cached document parse versions |
r.lsp.diagnostics_cache_max_mb |
16 |
maximum memory used by cached diagnostics |
r.lsp.index_mode |
"auto" |
index R files in the complete workspace; use "off" to restore package-only loading |
r.lsp.index_include |
"**/*.R" |
glob or character vector of globs included in workspace indexing |
r.lsp.index_exclude |
common VCS, dependency, cache, and output directories | glob or character vector of globs excluded from workspace indexing |
r.lsp.index_max_files |
10000 |
maximum number of eligible R files discovered per workspace |
r.lsp.index_max_file_size_mb |
2 |
maximum size of a file included in the workspace index |
r.lsp.index_batch_size |
20 |
maximum number of shallow summaries built in one idle batch |
r.lsp.index_time_budget_ms |
25 |
approximate event-loop budget for each shallow-index batch |
r.lsp.index_persistent_cache |
true |
persist validated shallow summaries in the user cache directory |
r.lsp.server_capabilities |
{} |
override server capabilities defined in capabilities.R. See FAQ below. |
r.lsp.link_file_size_limit |
16384 | maximum file size (in bytes) that supports document links |
These settings could also specified in .Rprofile file via options(languageserver.<SETTING_NAME> = <VALUE>). For example,
options(languageserver.snippet_support = FALSE)will turn off snippet support globally. LSP configuration settings are always overriden by options().
Project indexing is deliberately two-tiered. Package R/ files, open files,
and the transitive dependencies of static source() or sys.source() calls
receive full semantic parsing. Other scripts receive only a lightweight symbol
and source-call summary, so they appear in workspace symbol search without
polluting completion, definition, references, or rename in unrelated scripts.
Static paths built from string literals, file.path(), and here::here() are
recognized; project code is never executed to resolve a path.
The default backend requires lintr >= 3.3.0-1.
Specify rules in .lintr or .lintr.R, including the .github/linters/
project locations supported by lintr; see the lintr
documentation.
Install Jarl >= 0.7.1 and Air >= 0.12.0 separately, then select them:
options(
languageserver.diagnostics_backend = "jarl",
languageserver.formatting_backend = "air",
languageserver.backend_options = list(
jarl = list(executable = "/path/to/jarl", timeout = 10),
air = list(executable = "/path/to/air", timeout = 10,
env = c(XDG_CONFIG_HOME = "/path/to/config"))
)
)Without an executable override, commands are resolved on PATH. Equivalent LSP
settings use the names in the table above, with JSON objects for backend_options
and env. R options take precedence over workspace settings. Rule/style settings
belong in .lintr, jarl.toml or air.toml. Relevant R backend options and
languageserver.formatting_style are copied into workers for each invocation.
Jarl and Air receive current editor contents over stdin, including unsaved changes,
with the original absolute filename for configuration discovery. The working
directory is the nearest existing directory of the document. Pathless buffers use
the applicable workspace directory, or the server's working directory in a
folderless session, and a synthetic __languageserver_untitled__.R filename that
is not created. Lintr retains its existing text-only handling of pathless buffers.
Jarl currently supports R diagnostics; Air supports full-document R formatting.
CLI literate-document diagnostics return no results. Unsupported formatting modes
or document types return an explicit request error. Clients supporting dynamic
formatting registration, including vscode-R, receive updated providers after
workspace settings arrive and whenever the backend changes. Clients using static
capabilities retain the modes selected at startup; configure the backend through
R options before starting those clients. Their handlers still enforce the current
backend after a workspace setting change, but restarting with the same late
workspace settings cannot update their static capabilities.
On-type formatting with Air uses the server's local indentation fallback for
newlines, without starting a CLI. Lintr and styler retain their R
Markdown/Quarto, selection, and on-type behavior. Air follows its project formatting
settings rather than overriding them with LSP tabSize/insertSpaces. CLI edits use
LF line endings and otherwise preserve the formatter's terminal-newline output.
Changing the backend, execution options, or native configuration cancels obsolete work and refreshes diagnostics. Configuration files are watched where supported; a bounded background poll also refreshes idle buffers, including after a nearer configuration file is created or deleted. A missing backend produces one availability notification per relevant configuration state; explicit formatting requests receive an error.
An R option can supply an adapter descriptor instead of a built-in name:
options(languageserver.diagnostics_backend = list(
id = "my-linter",
document_types = "r",
needs_workspace_globals = FALSE,
config_files = "my-linter.toml",
run = function(request) {
# Inspect request$content and return plain LSP Diagnostic lists.
list()
}
))Formatting descriptors additionally declare modes, a subset of document,
range, ranges, and on_type, and return plain LSP TextEdit lists. Document
types are r and literate. Requests contain immutable buffer contents (a vector
of lines), URI, original/virtual path, language, workspace root, working directory,
resolved options, and formatting mode/ranges or on-type position/character. Ranges
and on-type positions use zero-based row/col code-point coordinates; returned
LSP positions must use UTF-16. Diagnostics receive the descriptor's id as their
source, keeping lintr code actions restricted to lintr diagnostics.
Adapters requesting workspace globals receive request$globals, including package
imports and static source dependencies. Configuration filenames in config_files
are checked in document ancestors; absolute paths are also supported. Use a stable,
unique ID and replace the descriptor when captured mutable configuration changes.
Custom functions must be serializable into an R worker and must not publish LSP
notifications or construct response objects. JSON settings select named built-ins;
custom descriptors are supplied through R options.
Server capabilities are defined in
capabilities.R.
Users could override the capabilities by specifying the LSP configuration setting
server_capabilities or
options(languageserver.server_capabilities) in .Rprofile. For example, to turn off definitionProvider, one could either use LSP configuration
"r": {
"lsp": {
"server_capabilities": {
"definitionProvider": false
}
}
}or R options
options(
languageserver.server_capabilities = list(
definitionProvider = FALSE
)
)The default formatting backend uses styler. It uses styler::tidyverse_style(indent_by = options$tabSize) as the default style where options is the formatting
options.
The formatting style can be customized by specifying languageserver.formatting_style option which
is supposed to be a function that accepts an options argument mentioned above. You could consider to put the code in .Rprofile.
styler::tidyverse_style provides numerous arguments to customize the formatting behavior. For example, to make it only work at indention scope:
options(languageserver.formatting_style = function(options) {
styler::tidyverse_style(scope = "indention", indent_by = options$tabSize)
})To disable assignment operator fix (replacing = with <-):
options(languageserver.formatting_style = function(options) {
style <- styler::tidyverse_style(indent_by = options$tabSize)
style$token$force_assignment_op <- NULL
style
})To further customize the formatting style, please refer to Customizing styler.