From 7d6b126a63d76f85b49909b52838eef413eb2ba0 Mon Sep 17 00:00:00 2001
From: Peter Wen <[email protected]>
Date: Mon, 11 Mar 2024 15:09:25 -0400
Subject: [PATCH] Make it work for chromium's infra configs
- Add the arg --lucicfg_stdlib_path to specify the directory that
lucicfg's stdlib resides. If this is missing then "go to definition"
for globals like `time.hour` will not work.
- Handle `//` (absolute) and `./` (relative) load path prefixes. The
global prefix in a file under //src/infra/config assumes `//` refers
to //src/infra/config, and in a file under
//src/internal/infra/config then `//` refers to that directory
instead. The relative path assumes it's rooted in the current file's
directory.
- Handle `@stdlib` load path prefix when --lucicfg_stdlib_path is
specified.
- Handle global symbol resolution when it's not a starlark builtin by
resolving it in a file with the same name in the stdlib or in the root
builtins.star for the `luci` symbol. This also requires the
--lucicfg_stdlib_path arg.
---
src/bazel.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++-
src/main.rs | 6 +++++
2 files changed, 69 insertions(+), 1 deletion(-)
diff --git a/src/bazel.rs b/src/bazel.rs
index 5455c33..ba0edf9 100644
--- a/src/bazel.rs
+++ b/src/bazel.rs
@@ -139,6 +139,7 @@ struct FilesystemCompletionOptions {
pub(crate) struct BazelContext<Client> {
workspaces: RefCell<HashMap<PathBuf, Rc<BazelWorkspace>>>,
query_output_base: Option<PathBuf>,
+ lucicfg_stdlib_path: Option<PathBuf>,
pub(crate) mode: ContextMode,
pub(crate) print_non_none: bool,
pub(crate) prelude: Vec<FrozenModule>,
@@ -156,6 +157,7 @@ impl<Client: BazelClient> BazelContext<Client> {
prelude: &[PathBuf],
module: bool,
query_output_base: Option<PathBuf>,
+ lucicfg_stdlib_path: Option<PathBuf>,
) -> anyhow::Result<Self> {
let globals = globals();
let prelude: Vec<_> = prelude
@@ -193,6 +195,7 @@ impl<Client: BazelClient> BazelContext<Client> {
Ok(Self {
workspaces: RefCell::new(HashMap::new()),
query_output_base,
+ lucicfg_stdlib_path,
mode,
print_non_none,
prelude,
@@ -642,6 +645,41 @@ impl<Client: BazelClient> LspContext for BazelContext<Client> {
current_file: &LspUrl,
workspace_root: Option<&Path>,
) -> anyhow::Result<LspUrl> {
+ let mut paths_to_test = Vec::new();
+ // Handle '//' and '@stdlib//' prefixes.
+ if path.starts_with("//") {
+ // Assuming workspace_root is //src.
+ let src_path = match workspace_root {
+ Some(root_path) => root_path.to_path_buf(),
+ None => PathBuf::from("/"),
+ };
+ let relative_path = path[2..].to_owned();
+ let current_file_path = current_file.path();
+ let path_string = current_file_path.to_string_lossy();
+ if path_string.contains("/internal/infra/config/") {
+ paths_to_test.push(src_path.join("internal/infra/config").join(&relative_path));
+ } else if path_string.contains("/infra/config/") {
+ paths_to_test.push(src_path.join("infra/config").join(&relative_path));
+ }
+ } else if path.starts_with("@stdlib//") {
+ if let Some(lucicfg_stdlib_path) = self.lucicfg_stdlib_path.as_deref() {
+ let relative_path = path[9..].to_owned();
+ paths_to_test.push(lucicfg_stdlib_path.join(&relative_path));
+ }
+ } else if path.starts_with("./") {
+ let cur_path = match current_file.path().parent() {
+ Some(path) => path.to_path_buf(),
+ None => PathBuf::from("/"),
+ };
+ let relative_path = path.to_owned();
+ paths_to_test.push(cur_path.join(&relative_path));
+ }
+ for path in paths_to_test {
+ if path.exists() {
+ return Ok(Url::from_file_path(path).unwrap().try_into()?);
+ }
+ }
+
let label = Label::parse(path)?;
let workspace = self.workspace(workspace_root, current_file)?;
@@ -775,12 +813,36 @@ impl<Client: BazelClient> LspContext for BazelContext<Client> {
self.try_get_environment(uri).unwrap_or_default()
}
+
fn get_url_for_global_symbol(
&self,
_current_file: &LspUrl,
symbol: &str,
) -> anyhow::Result<Option<LspUrl>> {
- Ok(self.builtin_symbols.get(symbol).cloned())
+ if self.builtin_symbols.contains_key(symbol) {
+ return Ok(self.builtin_symbols.get(symbol).cloned());
+ }
+
+ let mut mapping = HashMap::new();
+
+ if let Some(lucicfg_stdlib_path) = self.lucicfg_stdlib_path.as_deref() {
+ if symbol == "luci" {
+ return Ok(Some(LspUrl::File(lucicfg_stdlib_path.join("builtins.star"))));
+ }
+
+ for entry in fs::read_dir(lucicfg_stdlib_path.join("internal"))? {
+ let entry = entry?;
+ let path = entry.path();
+
+ if path.is_file() {
+ if let Some(file_stem) = path.file_stem() {
+ mapping.insert(file_stem.to_string_lossy().to_string(), LspUrl::File(path));
+ }
+ }
+ }
+ }
+
+ Ok(mapping.get(symbol).cloned())
}
fn get_string_completion_options(
diff --git a/src/main.rs b/src/main.rs
index d3e7400..cc8fab9 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -35,6 +35,11 @@ struct Args {
/// temp directory.
#[arg(long)]
query_output_base: Option<PathBuf>,
+
+ /// The absolute path to .../lucicfg/starlark/stdlib (requires
+ /// passing the full path that includes the stdlib directory).
+ #[arg(long)]
+ lucicfg_stdlib_path: Option<PathBuf>,
}
fn main() -> anyhow::Result<()> {
@@ -56,6 +61,7 @@ fn main() -> anyhow::Result<()> {
&[],
true,
query_output_base,
+ args.lucicfg_stdlib_path,
)?;
starlark_lsp::server::stdio_server(ctx)?;
--
2.44.0.683.g7961c838ac-goog