Skip to content

Add support for source() directive in symbol resolution#1157

Open
lionel- wants to merge 10 commits intooak/namespacefrom
oak/source
Open

Add support for source() directive in symbol resolution#1157
lionel- wants to merge 10 commits intooak/namespacefrom
oak/source

Conversation

@lionel-
Copy link
Copy Markdown
Contributor

@lionel- lionel- commented Apr 16, 2026

Branched from #1154

Closes #1148
Progress towards posit-dev/positron#11112

Adds support for source() directives in scripts, allowing for cross-file symbol resolution. Like in RStudio, sourced files are resolved from the workspace directory. Effects on the LSP:

  • Top-level definitions in the sourced files are made available below the source() call.
  • Side effects from top-level library() and require() calls are also made available after the source() call.
  • Sourced files can transitively source other files.

For now this only affects goto-definition, but should later also affect diagnostics such as unused symbols.

Both source() and library() are allowed in lazy nested scopes such as function bodies. In that case the side effects are available inside that scope (and nested children), but not outside, even though the effects are global. That's because outside that scope, code can't make assumptions about the availability of the symbols.

With a helpers.R file containing:

foo <- function() {}
library(dplyr)

We have at top-level:

# Both undefined
print(foo)
print(mutate)

source("helpers.R")

# Now defined
print(foo)
print(mutate)

In nested scopes:

# Both undefined
print(foo)
print(mutate)

function() {
  source("helpers.R")
  # Now defined
  print(foo)
  print(mutate)
}

# Still undefined
print(foo)
print(mutate)

At top-level, it doesn't matter whether local is true or false, the sourced definitions overwrite any local ones. We have a new DefinitionKind::Sourced variant for these definitions:

foo <- "local"
source("helpers.R")
print(foo) # Resolves to helper's definition

In nested scopes, if local = FALSE, the sourced definitions are made available via cross-file directives instead, that are only used for unbound symbols. This allows proper resolution of local symbols:

function() {
  foo <- 1
  source("helpers.R")
  print(foo) # Still refers to the local definition
}

If local is true, sourced definitions are instead added to the local scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant