diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 5591d284718d..bc9b02e8ca81 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -585,9 +585,6 @@ config_data! { /// Whether to show `can't find Cargo.toml` error message. notifications_cargoTomlNotFound: bool = true, - /// Whether to send an UnindexedProject notification to the client. - notifications_unindexedProject: bool = false, - /// How many worker threads in the main loop. The default `null` means to pick automatically. numThreads: Option = None, @@ -1118,7 +1115,6 @@ pub enum FilesWatcher { #[derive(Debug, Clone)] pub struct NotificationsConfig { pub cargo_toml_not_found: bool, - pub unindexed_project: bool, } #[derive(Debug, Clone)] @@ -1828,7 +1824,6 @@ impl Config { pub fn notifications(&self) -> NotificationsConfig { NotificationsConfig { cargo_toml_not_found: self.notifications_cargoTomlNotFound().to_owned(), - unindexed_project: self.notifications_unindexedProject().to_owned(), } } diff --git a/crates/rust-analyzer/src/handlers/notification.rs b/crates/rust-analyzer/src/handlers/notification.rs index 0315a0bd5654..22b05ff871bb 100644 --- a/crates/rust-analyzer/src/handlers/notification.rs +++ b/crates/rust-analyzer/src/handlers/notification.rs @@ -73,9 +73,7 @@ pub(crate) fn handle_did_open_text_document( tracing::info!("New file content set {:?}", params.text_document.text); state.vfs.write().0.set_file_contents(path, Some(params.text_document.text.into_bytes())); - if state.config.discover_command().is_some() - || state.config.notifications().unindexed_project - { + if state.config.discover_command().is_some() { tracing::debug!("queuing task"); let _ = state .deferred_task_queue diff --git a/crates/rust-analyzer/src/lsp/ext.rs b/crates/rust-analyzer/src/lsp/ext.rs index 8d0f654d8b0c..06b9dd75d033 100644 --- a/crates/rust-analyzer/src/lsp/ext.rs +++ b/crates/rust-analyzer/src/lsp/ext.rs @@ -856,16 +856,3 @@ pub struct CompletionImport { pub struct ClientCommandOptions { pub commands: Vec, } - -pub enum UnindexedProject {} - -impl Notification for UnindexedProject { - type Params = UnindexedProjectParams; - const METHOD: &'static str = "rust-analyzer/unindexedProject"; -} - -#[derive(Deserialize, Serialize, Debug)] -#[serde(rename_all = "camelCase")] -pub struct UnindexedProjectParams { - pub text_documents: Vec, -} diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index ff0087bc44b8..eedce8b325fe 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -91,7 +91,6 @@ pub(crate) enum QueuedTask { pub(crate) enum Task { Response(lsp_server::Response), DiscoverLinkedProjects(DiscoverProjectParam), - ClientNotification(lsp_ext::UnindexedProjectParams), Retry(lsp_server::Request), Diagnostics(DiagnosticsGeneration, Vec<(FileId, Vec)>), DiscoverTest(lsp_ext::DiscoverTestResults), @@ -634,9 +633,6 @@ impl GlobalState { fn handle_task(&mut self, prime_caches_progress: &mut Vec, task: Task) { match task { Task::Response(response) => self.respond(response), - Task::ClientNotification(params) => { - self.send_notification::(params) - } // Only retry requests that haven't been cancelled. Otherwise we do unnecessary work. Task::Retry(req) if !self.is_completed(&req) => self.on_request(req), Task::Retry(_) => (), @@ -813,12 +809,7 @@ impl GlobalState { if snap.config.discover_command().is_some() { let arg = DiscoverProjectParam::Path(uri); sender.send(Task::DiscoverLinkedProjects(arg)).unwrap(); - } else if snap.config.notifications().unindexed_project { - let params = lsp_ext::UnindexedProjectParams { - text_documents: vec![lsp_types::TextDocumentIdentifier { uri }], - }; - sender.send(Task::ClientNotification(params)).unwrap(); - }; + } } else { tracing::debug!(?uri, "is indexed"); } diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index caf6859f18b6..4753e71c22dc 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs @@ -32,8 +32,6 @@ use lsp_types::{ RenameFilesParams, TextDocumentItem, TextDocumentPositionParams, WorkDoneProgressParams, }; use rust_analyzer::lsp::ext::{OnEnter, Runnables, RunnablesParams}; - -use rust_analyzer::lsp::ext::{OnEnter, Runnables, RunnablesParams, UnindexedProject}; use serde_json::json; use stdx::format_to_acc; @@ -824,66 +822,6 @@ fn main() {{}} ); } -#[test] -fn test_opening_a_file_outside_of_indexed_workspace() { - if skip_slow_tests() { - return; - } - - let tmp_dir = TestDir::new(); - let path = tmp_dir.path(); - - let project = json!({ - "roots": [path], - "crates": [ { - "root_module": path.join("src/crate_one/lib.rs"), - "deps": [], - "edition": "2015", - "cfg": [ "cfg_atom_1", "feature=\"cfg_1\""], - } ] - }); - - let code = format!( - r#" -//- /rust-project.json -{project} - -//- /src/crate_one/lib.rs -mod bar; - -fn main() {{}} -"#, - ); - - let server = Project::with_fixture(&code) - .tmp_dir(tmp_dir) - .with_config(serde_json::json!({ - "notifications": { - "unindexedProject": true - }, - })) - .server() - .wait_until_workspace_is_loaded(); - - let uri = server.doc_id("src/crate_two/lib.rs").uri; - server.notification::(DidOpenTextDocumentParams { - text_document: TextDocumentItem { - uri: uri.clone(), - language_id: "rust".to_owned(), - version: 0, - text: "/// Docs\nfn foo() {}".to_owned(), - }, - }); - let expected = json!({ - "textDocuments": [ - { - "uri": uri - } - ] - }); - server.expect_notification::(expected); -} - #[test] fn diagnostics_dont_block_typing() { if skip_slow_tests() { diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs index c43832553230..ebae47b4810a 100644 --- a/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/crates/rust-analyzer/tests/slow-tests/support.rs @@ -256,40 +256,6 @@ impl Server { self.send_notification(r) } - pub(crate) fn expect_notification(&self, expected: Value) - where - N: lsp_types::notification::Notification, - N::Params: Serialize, - { - while let Some(Message::Notification(actual)) = - recv_timeout(&self.client.receiver).unwrap_or_else(|_| panic!("timed out")) - { - if actual.method == N::METHOD { - let actual = actual - .clone() - .extract::(N::METHOD) - .expect("was not able to extract notification"); - - tracing::debug!(?actual, "got notification"); - if let Some((expected_part, actual_part)) = find_mismatch(&expected, &actual) { - panic!( - "JSON mismatch\nExpected:\n{}\nWas:\n{}\nExpected part:\n{}\nActual part:\n{}\n", - to_string_pretty(&expected).unwrap(), - to_string_pretty(&actual).unwrap(), - to_string_pretty(expected_part).unwrap(), - to_string_pretty(actual_part).unwrap(), - ); - } else { - tracing::debug!("successfully matched notification"); - return; - } - } else { - continue; - } - } - panic!("never got expected notification"); - } - #[track_caller] pub(crate) fn request(&self, params: R::Params, expected_resp: Value) where diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index a6c858ce5a88..69f2154dc297 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -592,25 +592,6 @@ Reloads project information (that is, re-executes `cargo metadata`). Rebuilds build scripts and proc-macros, and runs the build scripts to reseed the build data. -## Unindexed Project - -**Experimental Client Capability:** `{ "unindexedProject": boolean }` - -**Method:** `rust-analyzer/unindexedProject` - -**Notification:** - -```typescript -interface UnindexedProjectParams { - /// A list of documents that rust-analyzer has determined are not indexed. - textDocuments: lc.TextDocumentIdentifier[] -} -``` - -This notification is sent from the server to the client. The client is expected -to determine the appropriate owners of `textDocuments` and update `linkedProjects` -if an owner can be determined successfully. - ## Server Status **Experimental Client Capability:** `{ "serverStatusNotification": boolean }` diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index eed8d8ba6e5e..9a5572d38ee2 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -811,11 +811,6 @@ Sets the LRU capacity of the specified queries. -- Whether to show `can't find Cargo.toml` error message. -- -[[rust-analyzer.notifications.unindexedProject]]rust-analyzer.notifications.unindexedProject (default: `false`):: -+ --- -Whether to send an UnindexedProject notification to the client. --- [[rust-analyzer.numThreads]]rust-analyzer.numThreads (default: `null`):: + -- diff --git a/editors/code/package.json b/editors/code/package.json index 4dd60261e92a..4443f4ec1085 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -2224,16 +2224,6 @@ } } }, - { - "title": "notifications", - "properties": { - "rust-analyzer.notifications.unindexedProject": { - "markdownDescription": "Whether to send an UnindexedProject notification to the client.", - "default": false, - "type": "boolean" - } - } - }, { "title": "general", "properties": { @@ -2538,6 +2528,22 @@ } } }, + { + "title": "workspace", + "properties": { + "rust-analyzer.workspace.discoverCommand": { + "markdownDescription": "Enables automatic discovery of projects using the discoverCommand.\n\nSetting this command will result in rust-analyzer starting indexing\nonly once a Rust file has been opened.", + "default": null, + "type": [ + "null", + "array" + ], + "items": { + "type": "string" + } + } + } + }, { "title": "workspace", "properties": { diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index f679c883983b..6fbea72f580e 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -102,16 +102,7 @@ export async function createClient( const resp = await next(params, token); if (resp && Array.isArray(resp)) { return resp.map((val) => { - return prepareVSCodeConfig(val, (key, cfg) => { - // we only want to set discovered workspaces on the right key - // and if a workspace has been discovered. - if ( - key === "linkedProjects" && - config.discoveredWorkspaces.length > 0 - ) { - cfg[key] = config.discoveredWorkspaces; - } - }); + return prepareVSCodeConfig(val); }); } else { return resp; diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index e676bc0826c7..254f88f7671a 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -5,7 +5,6 @@ import * as vscode from "vscode"; import type { Env } from "./client"; import { log } from "./util"; import { expectNotUndefined, unwrapUndefinable } from "./undefinable"; -import type { JsonProject } from "./rust_project"; export type RunnableEnvCfgItem = { mask?: string; @@ -41,7 +40,6 @@ export class Config { constructor(ctx: vscode.ExtensionContext) { this.globalStorageUri = ctx.globalStorageUri; - this.discoveredWorkspaces = []; vscode.workspace.onDidChangeConfiguration( this.onDidChangeConfiguration, this, @@ -63,8 +61,6 @@ export class Config { log.info("Using configuration", Object.fromEntries(cfg)); } - public discoveredWorkspaces: JsonProject[]; - private async onDidChangeConfiguration(event: vscode.ConfigurationChangeEvent) { this.refreshLogging(); @@ -357,18 +353,7 @@ export class Config { } } -// the optional `cb?` parameter is meant to be used to add additional -// key/value pairs to the VS Code configuration. This needed for, e.g., -// including a `rust-project.json` into the `linkedProjects` key as part -// of the configuration/InitializationParams _without_ causing VS Code -// configuration to be written out to workspace-level settings. This is -// undesirable behavior because rust-project.json files can be tens of -// thousands of lines of JSON, most of which is not meant for humans -// to interact with. -export function prepareVSCodeConfig( - resp: T, - cb?: (key: Extract, res: { [key: string]: any }) => void, -): T { +export function prepareVSCodeConfig(resp: T): T { if (Is.string(resp)) { return substituteVSCodeVariableInString(resp) as T; } else if (resp && Is.array(resp)) { @@ -380,9 +365,6 @@ export function prepareVSCodeConfig( for (const key in resp) { const val = resp[key]; res[key] = prepareVSCodeConfig(val); - if (cb) { - cb(key, res); - } } return res as T; } diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index bf0b84ec358e..c55636fb16de 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -1,5 +1,5 @@ import * as vscode from "vscode"; -import * as lc from "vscode-languageclient/node"; +import type * as lc from "vscode-languageclient/node"; import * as ra from "./lsp_ext"; import { Config, prepareVSCodeConfig } from "./config"; @@ -22,8 +22,6 @@ import { import { execRevealDependency } from "./commands"; import { PersistentState } from "./persistent_state"; import { bootstrap } from "./bootstrap"; -import type { RustAnalyzerExtensionApi } from "./main"; -import type { JsonProject } from "./rust_project"; import { prepareTestExplorer } from "./test_explorer"; import { spawn } from "node:child_process"; import { text } from "node:stream/consumers"; @@ -69,9 +67,9 @@ export type CtxInit = Ctx & { readonly client: lc.LanguageClient; }; -export class Ctx implements RustAnalyzerExtensionApi { +export class Ctx { readonly statusBar: vscode.StatusBarItem; - config: Config; + readonly config: Config; readonly workspace: Workspace; readonly version: string; @@ -224,15 +222,6 @@ export class Ctx implements RustAnalyzerExtensionApi { }; let rawInitializationOptions = vscode.workspace.getConfiguration("rust-analyzer"); - if (this.config.discoverProjectRunner) { - const command = `${this.config.discoverProjectRunner}.discoverWorkspaceCommand`; - log.info(`running command: ${command}`); - const uris = vscode.workspace.textDocuments - .filter(isRustDocument) - .map((document) => document.uri); - const projects: JsonProject[] = await vscode.commands.executeCommand(command, uris); - this.setWorkspaces(projects); - } if (this.workspace.kind === "Detached Files") { rawInitializationOptions = { @@ -241,16 +230,7 @@ export class Ctx implements RustAnalyzerExtensionApi { }; } - const initializationOptions = prepareVSCodeConfig( - rawInitializationOptions, - (key, obj) => { - // we only want to set discovered workspaces on the right key - // and if a workspace has been discovered. - if (key === "linkedProjects" && this.config.discoveredWorkspaces.length > 0) { - obj["linkedProjects"] = this.config.discoveredWorkspaces; - } - }, - ); + const initializationOptions = prepareVSCodeConfig(rawInitializationOptions); this._client = await createClient( this.traceOutputChannel, @@ -270,23 +250,6 @@ export class Ctx implements RustAnalyzerExtensionApi { this.outputChannel!.show(); }), ); - this.pushClientCleanup( - this._client.onNotification(ra.unindexedProject, async (params) => { - if (this.config.discoverProjectRunner) { - const command = `${this.config.discoverProjectRunner}.discoverWorkspaceCommand`; - log.info(`running command: ${command}`); - const uris = params.textDocuments.map((doc) => - vscode.Uri.parse(doc.uri, true), - ); - const projects: JsonProject[] = await vscode.commands.executeCommand( - command, - uris, - ); - this.setWorkspaces(projects); - await this.notifyRustAnalyzer(); - } - }), - ); } return this._client; } @@ -399,19 +362,6 @@ export class Ctx implements RustAnalyzerExtensionApi { return this.extCtx.subscriptions; } - setWorkspaces(workspaces: JsonProject[]) { - this.config.discoveredWorkspaces = workspaces; - } - - async notifyRustAnalyzer(): Promise { - // this is a workaround to avoid needing writing the `rust-project.json` into - // a workspace-level VS Code-specific settings folder. We'd like to keep the - // `rust-project.json` entirely in-memory. - await this.client?.sendNotification(lc.DidChangeConfigurationNotification.type, { - settings: "", - }); - } - private updateCommands(forceDisable?: "disable") { this.commandDisposables.forEach((disposable) => disposable.dispose()); this.commandDisposables = []; diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 2462f06f18b7..a68d7c487adc 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -274,9 +274,3 @@ export type RecursiveMemoryLayoutNode = { export type RecursiveMemoryLayout = { nodes: RecursiveMemoryLayoutNode[]; }; - -export const unindexedProject = new lc.NotificationType( - "rust-analyzer/unindexedProject", -); - -export type UnindexedProjectParams = { textDocuments: lc.TextDocumentIdentifier[] }; diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 0af58fd7812a..48bc84b7da4c 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -6,25 +6,14 @@ import { type CommandFactory, Ctx, fetchWorkspace } from "./ctx"; import * as diagnostics from "./diagnostics"; import { activateTaskProvider } from "./tasks"; import { setContextValue } from "./util"; -import type { JsonProject } from "./rust_project"; const RUST_PROJECT_CONTEXT_NAME = "inRustProject"; -// This API is not stable and may break in between minor releases. -export interface RustAnalyzerExtensionApi { - readonly client?: lc.LanguageClient; - - setWorkspaces(workspaces: JsonProject[]): void; - notifyRustAnalyzer(): Promise; -} - export async function deactivate() { await setContextValue(RUST_PROJECT_CONTEXT_NAME, undefined); } -export async function activate( - context: vscode.ExtensionContext, -): Promise { +export async function activate(context: vscode.ExtensionContext): Promise { checkConflictingExtensions(); const ctx = new Ctx(context, createCommands(), fetchWorkspace()); @@ -40,7 +29,7 @@ export async function activate( return api; } -async function activateServer(ctx: Ctx): Promise { +async function activateServer(ctx: Ctx): Promise { if (ctx.workspace.kind === "Workspace Folder") { ctx.pushExtCleanup(activateTaskProvider(ctx.config)); } diff --git a/editors/code/src/rust_project.ts b/editors/code/src/rust_project.ts deleted file mode 100644 index c983874fc009..000000000000 --- a/editors/code/src/rust_project.ts +++ /dev/null @@ -1,110 +0,0 @@ -export interface JsonProject { - /// Path to the sysroot directory. - /// - /// The sysroot is where rustc looks for the - /// crates that are built-in to rust, such as - /// std. - /// - /// https://doc.rust-lang.org/rustc/command-line-arguments.html#--sysroot-override-the-system-root - /// - /// To see the current value of sysroot, you - /// can query rustc: - /// - /// ``` - /// $ rustc --print sysroot - /// /Users/yourname/.rustup/toolchains/stable-x86_64-apple-darwin - /// ``` - sysroot?: string; - /// Path to the directory with *source code* of - /// sysroot crates. - /// - /// By default, this is `lib/rustlib/src/rust/library` - /// relative to the sysroot. - /// - /// It should point to the directory where std, - /// core, and friends can be found: - /// - /// https://github.com/rust-lang/rust/tree/master/library. - /// - /// If provided, rust-analyzer automatically adds - /// dependencies on sysroot crates. Conversely, - /// if you omit this path, you can specify sysroot - /// dependencies yourself and, for example, have - /// several different "sysroots" in one graph of - /// crates. - sysroot_src?: string; - /// The set of crates comprising the current - /// project. Must include all transitive - /// dependencies as well as sysroot crate (libstd, - /// libcore and such). - crates: Crate[]; -} - -export interface Crate { - /// Optional crate name used for display purposes, - /// without affecting semantics. See the `deps` - /// key for semantically-significant crate names. - display_name?: string; - /// Path to the root module of the crate. - root_module: string; - /// Edition of the crate. - edition: "2015" | "2018" | "2021"; - /// Dependencies - deps: Dep[]; - /// Should this crate be treated as a member of - /// current "workspace". - /// - /// By default, inferred from the `root_module` - /// (members are the crates which reside inside - /// the directory opened in the editor). - /// - /// Set this to `false` for things like standard - /// library and 3rd party crates to enable - /// performance optimizations (rust-analyzer - /// assumes that non-member crates don't change). - is_workspace_member?: boolean; - /// Optionally specify the (super)set of `.rs` - /// files comprising this crate. - /// - /// By default, rust-analyzer assumes that only - /// files under `root_module.parent` can belong - /// to a crate. `include_dirs` are included - /// recursively, unless a subdirectory is in - /// `exclude_dirs`. - /// - /// Different crates can share the same `source`. - /// - /// If two crates share an `.rs` file in common, - /// they *must* have the same `source`. - /// rust-analyzer assumes that files from one - /// source can't refer to files in another source. - source?: { - include_dirs: string[]; - exclude_dirs: string[]; - }; - /// The set of cfgs activated for a given crate, like - /// `["unix", "feature=\"foo\"", "feature=\"bar\""]`. - cfg: string[]; - /// Target triple for this Crate. - /// - /// Used when running `rustc --print cfg` - /// to get target-specific cfgs. - target?: string; - /// Environment variables, used for - /// the `env!` macro - env: { [key: string]: string }; - - /// Whether the crate is a proc-macro crate. - is_proc_macro: boolean; - /// For proc-macro crates, path to compiled - /// proc-macro (.so file). - proc_macro_dylib_path?: string; -} - -export interface Dep { - /// Index of a crate in the `crates` array. - crate: number; - /// Name as should appear in the (implicit) - /// `extern crate name` declaration. - name: string; -}