Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
4 changes: 2 additions & 2 deletions DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
Type: Package
Package: languageserver
Title: Language Server Protocol
Version: 0.3.20
Date: 2026-09-19
Version: 0.3.21
Date: 2026-10-30
Authors@R:
c(person(given = "Randy",
family = "Lai",
Expand Down
106 changes: 103 additions & 3 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# languageserver 0.3.20
# languageserver 0.3.21

- Preserve member completion for Polars conversions such as
`dt <- as_polars_df(iris)` and for `pl$` after calls using the converted frame.
Expand Down Expand Up @@ -125,6 +125,108 @@
Follow installed S7 constructor semantics, including 1.0 development class
references, forwarding constructors, named bindings, list property updates,
external class references, S4 parents and deprecation declarations.
- Resolve `$` and `@` member completion, signature help, and hover for static
namespace objects, lists, data frames, pairlists, stored S4/R6 values, and
lazy-loaded package datasets without invoking custom accessors or coercion
hooks (#818).
- Make `index_mode = "auto"` package-aware so standalone workspaces index open
files and their static `source()`/`sys.source()` dependencies lazily while
triggering shared background scans on demand for workspace symbols and global
references (#815).
- Keep hover, signature help, and completion responsive while workspace indexing,
parsing, S3 catalog building, and member-model preparation run in the
background, streaming indexing progress via `window/workDoneProgress` (#812, #811, #798).
- Support pluggable diagnostics and formatting backends through R adapters,
including built-in support for `jarl` diagnostics and `air` formatting
alongside the default `lintr` and `styler` backends (#809, #776). `lintr` and
`styler` have moved from `Imports` to `Suggests`, so the language server
itself does not require `lintr` or `styler` and
`install.packages("languageserver")` no longer installs them automatically;
users must install or upgrade `lintr` (>= 3.3.0-1) and install `styler` to
keep the default diagnostics and formatting, or select other backends.
- Skip `COMMENT` parse nodes and ignore computed callees when resolving function
calls and placing inlay hints (#807, #800, #799).
- Reduce worker scheduling, parse, and hot-path provider latency by precomputing
shared syntax/scope indexes in C, omitting eager XML serialization from worker
snapshots, and caching receiver analysis per document (#802, #795, #793).
- Preserve R6 class context for `self`, `super`, `private`, and aliases after
member assignments within methods (#797).
- Fix URI escaping for reserved filename characters, multiline `$`/`::`/`:::`
reference classification, overlapping raw-string scanning, and unsaved buffer
transfers across workspace folder changes (#796).
- Require R (>= 4.0.0) and modernize R 4.0 syntax; require `lintr` (>= 3.3.0-1)
when using the `lintr` diagnostics backend, dropping legacy `lintr`
workarounds (#813, #783).
- Honor `.lintr` settings from the working directory when linting pathless and
untitled documents (#782).
- Prevent function completions from inserting duplicate call parentheses when
an opening parenthesis already follows the token (#779, #778).
- Populate `NAMESPACE` imports and `DESCRIPTION` `Depends` packages in
diagnostics globals so `lintr::object_usage_linter()` recognizes imported
symbols and cross-file package functions (#775, #773, #652).
- Preserve missing terminal newline state during diagnostics and provide a preferred quick fix for `trailing_blank_lines_linter` (#774, #772).
- Respond to `shutdown` with a `null` result instead of `[]`, which clients that type the result strictly fail to decode (#789).

**Closed issues:**

- Hierarchical outline broken since 0.3.17 (#814)
- Intellisense / suggestion of arguments not depending on input (#805)
- No diagnostics, and documentColor/codeLens never answered, when the workspace folder is null (Windows, 0.3.20) (#794)
- enclose non syntactic names in backticks (#781)
- Existing brackets are ignored when function is autocompleted (#778)
- Support pluggable linting and formatting backends (#776)
- The object_usage_linter won't acknowledge bindings from @import or @importFrom (#773)
- Missing terminal newline lint is not reported in VSCode Problem Pane (#772)
- Autocompletion Slow (#687)
- Auto-completion for S7 classes? (#682)
- LSP diagnostics shows `lintr` error although `lintr::lint_package()` does not (#652)
- lsp sees things that lintr doesnt (#567)
- `languageserver` doesn't use `.lintr` file? (#557)

**Merged pull requests:**

- Cache package metadata across sessions and fix Polars member completion (#825)
- Prevent server crash when editing function headers (#824)
- Fix argument completion after member access (#820)
- Fix signature help in quoted arguments (#819)
- Fix member completion for static namespace objects (#818)
- Add capability-gated LSP 3.18 editor improvements (#817)
- Restore document outline hierarchy and recover outline requests after edits (#816)
- Use package-aware indexing and faster workspace discovery (#815)
- R 4.0.0 modernizations (#813)
- Keep basic providers responsive during parsing and metadata preparation (#812)
- Keep R providers responsive during large workspace indexing (#811)
- Balance CI coverage across eight shards (#810)
- Support pluggable diagnostics and formatting backends (#809)
- Add static S3 dispatch to completion, hover and signature help (#808)
- Ignore computed callees when resolving inlay hint function calls (#807)
- Bump ghcr.io/rocker-org/devcontainer-features/r-rig from 1.2.4 to 2.0.0 (#804)
- Handle pipe context across signature help, inlay hints, and providers (#803)
- Reduce worker scheduling and parse latency (#802)
- Fix diagnostics and pending replies in folderless sessions (#801)
- Skip parser comments correctly throughout the codebase (#800)
- Skip COMMENT nodes when placing inlay hints (#799)
- Keep large workspace loading responsive and report indexing progress (#798)
- Preserve R6 references after member assignments (#797)
- Fix URI handling, references, raw strings, and workspace buffer transfers (#796)
- Reduce hot-path latency with worker indexes and responsive scheduling (#795)
- Reduce member completion, hover and signature-help latency (#793)
- Preserve R6 member providers after inference timeouts (#792)
- Complete and describe R6 scope symbols and constructor signatures (#791)
- Keep Polars member providers available across metadata eviction (#790)
- Respond to shutdown with `null` result instead of `[]` (#789)
- Quote non-syntactic completion names and preserve snippet escapes (#788)
- Stabilize inlay hints during incomplete edits (#787)
- Extend member completion, signature help, and hover for R6 APIs (#786)
- Fix member completion for assigned and collected Polars queries (#785)
- Support static member and constructor intelligence for R6, S4 and S7 (#784)
- Require lintr (>= 3.3.0-1) and drop workarounds for older lintr (#783)
- fix: honor lintr settings for pathless documents (#782)
- fix: preserve existing call parentheses in completions (#779)
- Populate NAMESPACE and Depends imports in diagnostics globals for object_usage_linter() (#775)
- Enable detecting missing terminal newline (#774)

# languageserver 0.3.20

- Run full-document, range, and multiple-range formatting in a lazy, cancellable
background worker so other editor requests remain responsive during formatting (#764, #251).
Expand All @@ -140,8 +242,6 @@
avoiding zero-length allocations when definitions or occurrences are empty (#767).
- Speed up test-coverage workflow by utilizing 4 CPUs, path filtering, and 4-way
balanced test sharding (#769).
- Preserve missing terminal newline state during diagnostics and provide a preferred quick fix for `trailing_blank_lines_linter` (#774, #772).
- Respond to `shutdown` with a `null` result instead of `[]`, which clients that type the result strictly fail to decode.

**Closed issues:**

Expand Down
12 changes: 7 additions & 5 deletions R/index_worker.R
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
index_worker_state <- new.env(parent = emptyenv())

#' Discover files and restore cached summaries outside the server event loop
#' @noRd
index_worker_discover <- function(key, root, settings = NULL, cache_file = NULL,
batch_size = 20L, directory = NULL, summary_batch_size = min(batch_size, 20L),
known_uris = character(), file_limit = NULL) {
state_name <- ".languageserver_workspace_discoveries"
states <- get0(state_name, envir = globalenv(), inherits = FALSE)
states <- get0(state_name, envir = index_worker_state, inherits = FALSE)
if (!is.environment(states) || !identical(states$.__class__, "ordered_dict")) {
states <- collections::ordered_dict()
assign(state_name, states, envir = globalenv())
assign(state_name, states, envir = index_worker_state)
}
state <- states$pop(key, NULL)
if (!is.null(settings)) {
Expand Down Expand Up @@ -45,7 +47,7 @@ index_worker_discover <- function(key, root, settings = NULL, cache_file = NULL,
#' @noRd
index_worker_release <- function(key) {
for (name in c(".languageserver_workspace_discoveries", ".languageserver_workspace_indexes")) {
states <- get0(name, envir = globalenv(), inherits = FALSE)
states <- get0(name, envir = index_worker_state, inherits = FALSE)
if (is.environment(states) && identical(states$.__class__, "ordered_dict") && states$has(key)) {
states$remove(key)
}
Expand All @@ -59,10 +61,10 @@ index_worker_batch <- function(key, root, updates, requests, bootstrap = FALSE,
cache_file = NULL, revision = NULL, time_budget_ms = 25,
max_file_bytes = 2 * 1024^2) {
state_name <- ".languageserver_workspace_indexes"
states <- get0(state_name, envir = globalenv(), inherits = FALSE)
states <- get0(state_name, envir = index_worker_state, inherits = FALSE)
if (!is.environment(states) || !identical(states$.__class__, "ordered_dict")) {
states <- collections::ordered_dict()
assign(state_name, states, envir = globalenv())
assign(state_name, states, envir = index_worker_state)
}
state <- states$pop(key, NULL)
sync_needed <- is.null(state) && !isTRUE(bootstrap)
Expand Down
6 changes: 3 additions & 3 deletions tests/testthat/test-index-worker.R
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ test_that("worker persistence applies deltas and refuses missing or stale state"
summary$cacheable <- TRUE
key <- tempfile("index-test-")
withr::defer({
states <- get0(".languageserver_workspace_indexes", globalenv(), inherits = FALSE)
states <- get0(".languageserver_workspace_indexes", index_worker_state, inherits = FALSE)
if (is.environment(states) && is.function(states$has) && states$has(key)) states$remove(key)
})
cache_file <- file.path(root, "index.rds")
Expand All @@ -134,7 +134,7 @@ test_that("worker persistence applies deltas and refuses missing or stale state"
result <- index_worker_batch(missing, root, list(), list(), cache_file = cache_file)
expect_true(result$sync_needed)
expect_null(result$temp_file)
states <- get(".languageserver_workspace_indexes", globalenv())
states <- get(".languageserver_workspace_indexes", index_worker_state)
states$remove(missing)

old_cache <- lsp_settings$get("index_persistent_cache")
Expand Down Expand Up @@ -304,7 +304,7 @@ test_that("persistent worker catalogs retain active roots and release detached s
root <- withr::local_tempdir()
keys <- paste0(tempfile("bounded-index-test-"), seq_len(17L))
for (key in keys) index_worker_batch(key, root, list(), list(), bootstrap = TRUE)
states <- get(".languageserver_workspace_indexes", globalenv())
states <- get(".languageserver_workspace_indexes", index_worker_state)
expect_true(all(vapply(keys, states$has, logical(1L))))
index_worker_release(keys[[1L]])
expect_false(states$has(keys[[1L]]))
Expand Down
6 changes: 3 additions & 3 deletions tests/testthat/test-workspace-lifecycle.R
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ test_that("discovery completes across more roots than the worker snapshot cache"
keys <- vapply(workspaces, function(workspace) workspace$index$.__enclos_env__$private$worker_key, "")
withr::defer(for (key in keys) index_worker_release(key))
for (i in seq_along(workspaces)) server$process_events()
discoveries <- get(".languageserver_workspace_discoveries", globalenv())
discoveries <- get(".languageserver_workspace_discoveries", index_worker_state)
expect_true(all(vapply(keys, discoveries$has, logical(1L))))
for (i in seq_len(1600L)) {
server$process_events()
Expand Down Expand Up @@ -565,8 +565,8 @@ test_that("detaching releases worker cursors and catalogs without affecting reat
old_key <- manager$tasks$values()[[1L]]$.__enclos_env__$private$args$key
manager$finish_one()
index_worker_batch(old_key, root, list(), list(), bootstrap = TRUE)
discoveries <- get(".languageserver_workspace_discoveries", globalenv())
catalogs <- get(".languageserver_workspace_indexes", globalenv())
discoveries <- get(".languageserver_workspace_discoveries", index_worker_state)
catalogs <- get(".languageserver_workspace_indexes", index_worker_state)
expect_true(discoveries$has(old_key))
expect_true(catalogs$has(old_key))

Expand Down
2 changes: 1 addition & 1 deletion tests/testthat/test-workspace-loading.R
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,7 @@ test_that("lost discovery worker state restarts and superseded scans cannot inst
index$process_batch()
index$process_batch()
private <- tail(manager$tasks$values(), 1L)[[1L]]$.__enclos_env__$private
states <- get(".languageserver_workspace_discoveries", globalenv())
states <- get(".languageserver_workspace_discoveries", index_worker_state)
states$remove(private$args$key)
completed <- workspace_loading_result(manager)
expect_true(completed$result$restart)
Expand Down
Loading