8955: feature: Support standalone Rust files r=matklad a=SomeoneToIgnore

![standalone](https://user-images.githubusercontent.com/2690773/119277037-0b579380-bc26-11eb-8d77-20d46ab4916a.gif)

Closes https://github.com/rust-analyzer/rust-analyzer/issues/6388

Caveats: 

* I've decided to support multiple detached files in the code (anticipating the scratch files), but I found no way to open multiple files in VSCode at once: running `code *.rs` makes the plugin to register in the `vscode.workspace.textDocuments` only the first file, while code actually displays all files later.
Apparently what happens is the same as when you have VSCode open at some workplace already and then run `code some_other_file.rs`: it gets opened in the same workspace of the same VSCode with no server to support it.
If there's a way to override it, I'd appreciate the pointer.

* No way to toggle inlay hints, since the setting is updated for the workspace (which does not exist for a single file opened)
> [2021-05-24 00:22:49.100] [exthost] [error] Error: Unable to write to Workspace Settings because no workspace is opened. Please open a workspace first and try again.

* No runners/lens to run or check the code are implemented for this mode. 
In theory, we can detect `rustc`, run it on a file and run the resulting binary, but not sure if worth doing it at this stage.

Otherwise imports, hints, completion and other features work.

Co-authored-by: Kirill Bulatov <mail4score@gmail.com>
This commit is contained in:
bors[bot] 2021-05-24 12:40:25 +00:00 committed by GitHub
commit 05fc97e31b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 210 additions and 61 deletions

View File

@ -50,7 +50,9 @@ pub fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIte
pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> { pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> {
log::debug!("Discovering sysroot for {}", cargo_toml.display()); log::debug!("Discovering sysroot for {}", cargo_toml.display());
let current_dir = cargo_toml.parent().unwrap(); let current_dir = cargo_toml.parent().ok_or_else(|| {
format_err!("Failed to find the parent directory for {}", cargo_toml.display())
})?;
let sysroot_dir = discover_sysroot_dir(current_dir)?; let sysroot_dir = discover_sysroot_dir(current_dir)?;
let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, current_dir)?; let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, current_dir)?;
let res = Sysroot::load(&sysroot_src_dir)?; let res = Sysroot::load(&sysroot_src_dir)?;

View File

@ -4,7 +4,7 @@
use std::{collections::VecDeque, fmt, fs, path::Path, process::Command}; use std::{collections::VecDeque, fmt, fs, path::Path, process::Command};
use anyhow::{Context, Result}; use anyhow::{format_err, Context, Result};
use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro}; use base_db::{CrateDisplayName, CrateGraph, CrateId, CrateName, Edition, Env, FileId, ProcMacro};
use cargo_workspace::DepKind; use cargo_workspace::DepKind;
use cfg::CfgOptions; use cfg::CfgOptions;
@ -49,6 +49,18 @@ pub enum ProjectWorkspace {
}, },
/// Project workspace was manually specified using a `rust-project.json` file. /// Project workspace was manually specified using a `rust-project.json` file.
Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> }, Json { project: ProjectJson, sysroot: Option<Sysroot>, rustc_cfg: Vec<CfgFlag> },
// FIXME: The primary limitation of this approach is that the set of detached files needs to be fixed at the beginning.
// That's not the end user experience we should strive for.
// Ideally, you should be able to just open a random detached file in existing cargo projects, and get the basic features working.
// That needs some changes on the salsa-level though.
// In particular, we should split the unified CrateGraph (which currently has maximal durability) into proper crate graph, and a set of ad hoc roots (with minimal durability).
// Then, we need to hide the graph behind the queries such that most queries look only at the proper crate graph, and fall back to ad hoc roots only if there's no results.
// After this, we should be able to tweak the logic in reload.rs to add newly opened files, which don't belong to any existing crates, to the set of the detached files.
// //
/// Project with a set of disjoint files, not belonging to any particular workspace.
/// Backed by basic sysroot crates for basic completion and highlighting.
DetachedFiles { files: Vec<AbsPathBuf>, sysroot: Sysroot, rustc_cfg: Vec<CfgFlag> },
} }
impl fmt::Debug for ProjectWorkspace { impl fmt::Debug for ProjectWorkspace {
@ -75,6 +87,12 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
debug_struct.field("n_rustc_cfg", &rustc_cfg.len()); debug_struct.field("n_rustc_cfg", &rustc_cfg.len());
debug_struct.finish() debug_struct.finish()
} }
ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => f
.debug_struct("DetachedFiles")
.field("n_files", &files.len())
.field("n_sysroot_crates", &sysroot.crates().len())
.field("n_rustc_cfg", &rustc_cfg.len())
.finish(),
} }
} }
} }
@ -165,6 +183,14 @@ pub fn load_inline(
Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg }) Ok(ProjectWorkspace::Json { project: project_json, sysroot, rustc_cfg })
} }
pub fn load_detached_files(detached_files: Vec<AbsPathBuf>) -> Result<ProjectWorkspace> {
let sysroot = Sysroot::discover(
&detached_files.first().ok_or_else(|| format_err!("No detached files to load"))?,
)?;
let rustc_cfg = rustc_cfg::get(None, None);
Ok(ProjectWorkspace::DetachedFiles { files: detached_files, sysroot, rustc_cfg })
}
/// Returns the roots for the current `ProjectWorkspace` /// Returns the roots for the current `ProjectWorkspace`
/// The return type contains the path and whether or not /// The return type contains the path and whether or not
/// the root is a member of the current workspace /// the root is a member of the current workspace
@ -224,6 +250,19 @@ pub fn to_roots(&self, build_data: Option<&BuildDataResult>) -> Vec<PackageRoot>
}) })
})) }))
.collect(), .collect(),
ProjectWorkspace::DetachedFiles { files, sysroot, .. } => files
.into_iter()
.map(|detached_file| PackageRoot {
is_member: true,
include: vec![detached_file.clone()],
exclude: Vec::new(),
})
.chain(sysroot.crates().map(|krate| PackageRoot {
is_member: false,
include: vec![sysroot[krate].root_dir().to_path_buf()],
exclude: Vec::new(),
}))
.collect(),
} }
} }
@ -234,6 +273,9 @@ pub fn n_packages(&self) -> usize {
let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len()); let rustc_package_len = rustc.as_ref().map_or(0, |rc| rc.packages().len());
cargo.packages().len() + sysroot.crates().len() + rustc_package_len cargo.packages().len() + sysroot.crates().len() + rustc_package_len
} }
ProjectWorkspace::DetachedFiles { sysroot, files, .. } => {
sysroot.crates().len() + files.len()
}
} }
} }
@ -267,6 +309,9 @@ pub fn to_crate_graph(
rustc, rustc,
rustc.as_ref().zip(build_data).and_then(|(it, map)| map.get(it.workspace_root())), rustc.as_ref().zip(build_data).and_then(|(it, map)| map.get(it.workspace_root())),
), ),
ProjectWorkspace::DetachedFiles { files, sysroot, rustc_cfg } => {
detached_files_to_crate_graph(rustc_cfg.clone(), load, files, sysroot)
}
}; };
if crate_graph.patch_cfg_if() { if crate_graph.patch_cfg_if() {
log::debug!("Patched std to depend on cfg-if") log::debug!("Patched std to depend on cfg-if")
@ -474,6 +519,48 @@ fn cargo_to_crate_graph(
crate_graph crate_graph
} }
fn detached_files_to_crate_graph(
rustc_cfg: Vec<CfgFlag>,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,
detached_files: &[AbsPathBuf],
sysroot: &Sysroot,
) -> CrateGraph {
let _p = profile::span("detached_files_to_crate_graph");
let mut crate_graph = CrateGraph::default();
let (public_deps, _libproc_macro) =
sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
let mut cfg_options = CfgOptions::default();
cfg_options.extend(rustc_cfg);
for detached_file in detached_files {
let file_id = match load(&detached_file) {
Some(file_id) => file_id,
None => {
log::error!("Failed to load detached file {:?}", detached_file);
continue;
}
};
let display_name = detached_file
.file_stem()
.and_then(|os_str| os_str.to_str())
.map(|file_stem| CrateDisplayName::from_canonical_name(file_stem.to_string()));
let detached_file_crate = crate_graph.add_crate_root(
file_id,
Edition::Edition2018,
display_name,
cfg_options.clone(),
Env::default(),
Vec::new(),
);
for (name, krate) in public_deps.iter() {
add_dep(&mut crate_graph, detached_file_crate, name.clone(), *krate);
}
}
crate_graph
}
fn handle_rustc_crates( fn handle_rustc_crates(
rustc_workspace: &CargoWorkspace, rustc_workspace: &CargoWorkspace,
load: &mut dyn FnMut(&AbsPath) -> Option<FileId>, load: &mut dyn FnMut(&AbsPath) -> Option<FileId>,

View File

@ -199,7 +199,7 @@ fn run_server() -> Result<()> {
config.update(json); config.update(json);
} }
if config.linked_projects().is_empty() { if config.linked_projects().is_empty() && config.detached_files().is_empty() {
let workspace_roots = initialize_params let workspace_roots = initialize_params
.workspace_folders .workspace_folders
.map(|workspaces| { .map(|workspaces| {
@ -217,7 +217,6 @@ fn run_server() -> Result<()> {
if discovered.is_empty() { if discovered.is_empty() {
log::error!("failed to find any projects in {:?}", workspace_roots); log::error!("failed to find any projects in {:?}", workspace_roots);
} }
config.discovered_projects = Some(discovered); config.discovered_projects = Some(discovered);
} }

View File

@ -236,6 +236,7 @@ fn default() -> Self {
pub struct Config { pub struct Config {
caps: lsp_types::ClientCapabilities, caps: lsp_types::ClientCapabilities,
data: ConfigData, data: ConfigData,
detached_files: Vec<AbsPathBuf>,
pub discovered_projects: Option<Vec<ProjectManifest>>, pub discovered_projects: Option<Vec<ProjectManifest>>,
pub root_path: AbsPathBuf, pub root_path: AbsPathBuf,
} }
@ -328,13 +329,23 @@ pub struct WorkspaceSymbolConfig {
impl Config { impl Config {
pub fn new(root_path: AbsPathBuf, caps: ClientCapabilities) -> Self { pub fn new(root_path: AbsPathBuf, caps: ClientCapabilities) -> Self {
Config { caps, data: ConfigData::default(), discovered_projects: None, root_path } Config {
caps,
data: ConfigData::default(),
detached_files: Vec::new(),
discovered_projects: None,
root_path,
}
} }
pub fn update(&mut self, json: serde_json::Value) { pub fn update(&mut self, mut json: serde_json::Value) {
log::info!("updating config from JSON: {:#}", json); log::info!("updating config from JSON: {:#}", json);
if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) { if json.is_null() || json.as_object().map_or(false, |it| it.is_empty()) {
return; return;
} }
self.detached_files = get_field::<Vec<PathBuf>>(&mut json, "detachedFiles", None, "[]")
.into_iter()
.map(AbsPathBuf::assert)
.collect();
self.data = ConfigData::from_json(json); self.data = ConfigData::from_json(json);
} }
@ -387,6 +398,10 @@ pub fn linked_projects(&self) -> Vec<LinkedProject> {
} }
} }
pub fn detached_files(&self) -> &[AbsPathBuf] {
&self.detached_files
}
pub fn did_save_text_document_dynamic_registration(&self) -> bool { pub fn did_save_text_document_dynamic_registration(&self) -> bool {
let caps = let caps =
try_or!(self.caps.text_document.as_ref()?.synchronization.clone()?, Default::default()); try_or!(self.caps.text_document.as_ref()?.synchronization.clone()?, Default::default());

View File

@ -312,6 +312,7 @@ pub(crate) fn cargo_target_for_crate_root(
cargo.target_by_root(&path).map(|it| (cargo, it)) cargo.target_by_root(&path).map(|it| (cargo, it))
} }
ProjectWorkspace::Json { .. } => None, ProjectWorkspace::Json { .. } => None,
ProjectWorkspace::DetachedFiles { .. } => None,
}) })
} }
} }

View File

@ -661,19 +661,28 @@ pub(crate) fn handle_runnables(
} }
} }
None => { None => {
res.push(lsp_ext::Runnable { if !snap.config.linked_projects().is_empty()
label: "cargo check --workspace".to_string(), || !snap
location: None, .config
kind: lsp_ext::RunnableKind::Cargo, .discovered_projects
args: lsp_ext::CargoRunnable { .as_ref()
workspace_root: None, .map(|projects| projects.is_empty())
override_cargo: config.override_cargo, .unwrap_or(true)
cargo_args: vec!["check".to_string(), "--workspace".to_string()], {
cargo_extra_args: config.cargo_extra_args, res.push(lsp_ext::Runnable {
executable_args: Vec::new(), label: "cargo check --workspace".to_string(),
expect_test: None, location: None,
}, kind: lsp_ext::RunnableKind::Cargo,
}); args: lsp_ext::CargoRunnable {
workspace_root: None,
override_cargo: config.override_cargo,
cargo_args: vec!["check".to_string(), "--workspace".to_string()],
cargo_extra_args: config.cargo_extra_args,
executable_args: Vec::new(),
expect_test: None,
},
});
}
} }
} }
Ok(res) Ok(res)

View File

@ -103,6 +103,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
impl GlobalState { impl GlobalState {
fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> { fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> {
if self.config.linked_projects().is_empty() if self.config.linked_projects().is_empty()
&& self.config.detached_files().is_empty()
&& self.config.notifications().cargo_toml_not_found && self.config.notifications().cargo_toml_not_found
{ {
self.show_message( self.show_message(

View File

@ -147,6 +147,7 @@ pub(crate) fn fetch_workspaces_if_needed(&mut self) {
self.task_pool.handle.spawn_with_sender({ self.task_pool.handle.spawn_with_sender({
let linked_projects = self.config.linked_projects(); let linked_projects = self.config.linked_projects();
let detached_files = self.config.detached_files().to_vec();
let cargo_config = self.config.cargo(); let cargo_config = self.config.cargo();
move |sender| { move |sender| {
@ -161,7 +162,7 @@ pub(crate) fn fetch_workspaces_if_needed(&mut self) {
sender.send(Task::FetchWorkspace(ProjectWorkspaceProgress::Begin)).unwrap(); sender.send(Task::FetchWorkspace(ProjectWorkspaceProgress::Begin)).unwrap();
let workspaces = linked_projects let mut workspaces = linked_projects
.iter() .iter()
.map(|project| match project { .map(|project| match project {
LinkedProject::ProjectManifest(manifest) => { LinkedProject::ProjectManifest(manifest) => {
@ -180,6 +181,11 @@ pub(crate) fn fetch_workspaces_if_needed(&mut self) {
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
if !detached_files.is_empty() {
workspaces
.push(project_model::ProjectWorkspace::load_detached_files(detached_files));
}
log::info!("did fetch workspaces {:?}", workspaces); log::info!("did fetch workspaces {:?}", workspaces);
sender sender
.send(Task::FetchWorkspace(ProjectWorkspaceProgress::End(workspaces))) .send(Task::FetchWorkspace(ProjectWorkspaceProgress::End(workspaces)))
@ -407,6 +413,7 @@ fn reload_flycheck(&mut self) {
_ => None, _ => None,
} }
} }
ProjectWorkspace::DetachedFiles { .. } => None,
}) })
.map(|(id, root)| { .map(|(id, root)| {
let sender = sender.clone(); let sender = sender.clone();

View File

@ -4,6 +4,7 @@ import * as ra from '../src/lsp_ext';
import * as Is from 'vscode-languageclient/lib/common/utils/is'; import * as Is from 'vscode-languageclient/lib/common/utils/is';
import { assert } from './util'; import { assert } from './util';
import { WorkspaceEdit } from 'vscode'; import { WorkspaceEdit } from 'vscode';
import { Workspace } from './ctx';
export interface Env { export interface Env {
[name: string]: string; [name: string]: string;
@ -23,7 +24,7 @@ function renderHoverActions(actions: ra.CommandLinkGroup[]): vscode.MarkdownStri
return result; return result;
} }
export function createClient(serverPath: string, cwd: string, extraEnv: Env): lc.LanguageClient { export function createClient(serverPath: string, workspace: Workspace, extraEnv: Env): lc.LanguageClient {
// '.' Is the fallback if no folder is open // '.' Is the fallback if no folder is open
// TODO?: Workspace folders support Uri's (eg: file://test.txt). // TODO?: Workspace folders support Uri's (eg: file://test.txt).
// It might be a good idea to test if the uri points to a file. // It might be a good idea to test if the uri points to a file.
@ -31,6 +32,11 @@ export function createClient(serverPath: string, cwd: string, extraEnv: Env): lc
const newEnv = Object.assign({}, process.env); const newEnv = Object.assign({}, process.env);
Object.assign(newEnv, extraEnv); Object.assign(newEnv, extraEnv);
let cwd = undefined;
if (workspace.kind === "Workspace Folder") {
cwd = workspace.folder.fsPath;
};
const run: lc.Executable = { const run: lc.Executable = {
command: serverPath, command: serverPath,
options: { cwd, env: newEnv }, options: { cwd, env: newEnv },
@ -43,9 +49,14 @@ export function createClient(serverPath: string, cwd: string, extraEnv: Env): lc
'Rust Analyzer Language Server Trace', 'Rust Analyzer Language Server Trace',
); );
let initializationOptions = vscode.workspace.getConfiguration("rust-analyzer");
if (workspace.kind === "Detached Files") {
initializationOptions = { "detachedFiles": workspace.files.map(file => file.uri.fsPath), ...initializationOptions };
}
const clientOptions: lc.LanguageClientOptions = { const clientOptions: lc.LanguageClientOptions = {
documentSelector: [{ scheme: 'file', language: 'rust' }], documentSelector: [{ scheme: 'file', language: 'rust' }],
initializationOptions: vscode.workspace.getConfiguration("rust-analyzer"), initializationOptions,
diagnosticCollectionName: "rustc", diagnosticCollectionName: "rustc",
traceOutputChannel, traceOutputChannel,
middleware: { middleware: {

View File

@ -7,6 +7,16 @@ import { createClient } from './client';
import { isRustEditor, RustEditor } from './util'; import { isRustEditor, RustEditor } from './util';
import { ServerStatusParams } from './lsp_ext'; import { ServerStatusParams } from './lsp_ext';
export type Workspace =
{
kind: 'Workspace Folder';
folder: vscode.Uri;
}
| {
kind: 'Detached Files';
files: vscode.TextDocument[];
};
export class Ctx { export class Ctx {
private constructor( private constructor(
readonly config: Config, readonly config: Config,
@ -22,9 +32,9 @@ export class Ctx {
config: Config, config: Config,
extCtx: vscode.ExtensionContext, extCtx: vscode.ExtensionContext,
serverPath: string, serverPath: string,
cwd: string, workspace: Workspace,
): Promise<Ctx> { ): Promise<Ctx> {
const client = createClient(serverPath, cwd, config.serverExtraEnv); const client = createClient(serverPath, workspace, config.serverExtraEnv);
const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
extCtx.subscriptions.push(statusBar); extCtx.subscriptions.push(statusBar);

View File

@ -7,7 +7,7 @@ import * as commands from './commands';
import { activateInlayHints } from './inlay_hints'; import { activateInlayHints } from './inlay_hints';
import { Ctx } from './ctx'; import { Ctx } from './ctx';
import { Config } from './config'; import { Config } from './config';
import { log, assert, isValidExecutable } from './util'; import { log, assert, isValidExecutable, isRustDocument } from './util';
import { PersistentState } from './persistent_state'; import { PersistentState } from './persistent_state';
import { fetchRelease, download } from './net'; import { fetchRelease, download } from './net';
import { activateTaskProvider } from './tasks'; import { activateTaskProvider } from './tasks';
@ -28,26 +28,6 @@ export async function activate(context: vscode.ExtensionContext) {
} }
async function tryActivate(context: vscode.ExtensionContext) { async function tryActivate(context: vscode.ExtensionContext) {
// Register a "dumb" onEnter command for the case where server fails to
// start.
//
// FIXME: refactor command registration code such that commands are
// **always** registered, even if the server does not start. Use API like
// this perhaps?
//
// ```TypeScript
// registerCommand(
// factory: (Ctx) => ((Ctx) => any),
// fallback: () => any = () => vscode.window.showErrorMessage(
// "rust-analyzer is not available"
// ),
// )
const defaultOnEnter = vscode.commands.registerCommand(
'rust-analyzer.onEnter',
() => vscode.commands.executeCommand('default:type', { text: '\n' }),
);
context.subscriptions.push(defaultOnEnter);
const config = new Config(context); const config = new Config(context);
const state = new PersistentState(context.globalState); const state = new PersistentState(context.globalState);
const serverPath = await bootstrap(config, state).catch(err => { const serverPath = await bootstrap(config, state).catch(err => {
@ -67,14 +47,52 @@ async function tryActivate(context: vscode.ExtensionContext) {
const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
if (workspaceFolder === undefined) { if (workspaceFolder === undefined) {
throw new Error("no folder is opened"); const rustDocuments = vscode.workspace.textDocuments.filter(document => isRustDocument(document));
if (rustDocuments.length > 0) {
ctx = await Ctx.create(config, context, serverPath, { kind: 'Detached Files', files: rustDocuments });
} else {
throw new Error("no rust files are opened");
}
} else {
// Note: we try to start the server before we activate type hints so that it
// registers its `onDidChangeDocument` handler before us.
//
// This a horribly, horribly wrong way to deal with this problem.
ctx = await Ctx.create(config, context, serverPath, { kind: "Workspace Folder", folder: workspaceFolder.uri });
ctx.pushCleanup(activateTaskProvider(workspaceFolder, ctx.config));
} }
await initCommonContext(context, ctx);
// Note: we try to start the server before we activate type hints so that it activateInlayHints(ctx);
// registers its `onDidChangeDocument` handler before us. warnAboutExtensionConflicts();
vscode.workspace.onDidChangeConfiguration(
_ => ctx?.client?.sendNotification('workspace/didChangeConfiguration', { settings: "" }),
null,
ctx.subscriptions,
);
}
async function initCommonContext(context: vscode.ExtensionContext, ctx: Ctx) {
// Register a "dumb" onEnter command for the case where server fails to
// start.
// //
// This a horribly, horribly wrong way to deal with this problem. // FIXME: refactor command registration code such that commands are
ctx = await Ctx.create(config, context, serverPath, workspaceFolder.uri.fsPath); // **always** registered, even if the server does not start. Use API like
// this perhaps?
//
// ```TypeScript
// registerCommand(
// factory: (Ctx) => ((Ctx) => any),
// fallback: () => any = () => vscode.window.showErrorMessage(
// "rust-analyzer is not available"
// ),
// )
const defaultOnEnter = vscode.commands.registerCommand(
'rust-analyzer.onEnter',
() => vscode.commands.executeCommand('default:type', { text: '\n' }),
);
context.subscriptions.push(defaultOnEnter);
await setContextValue(RUST_PROJECT_CONTEXT_NAME, true); await setContextValue(RUST_PROJECT_CONTEXT_NAME, true);
@ -134,17 +152,6 @@ async function tryActivate(context: vscode.ExtensionContext) {
ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction); ctx.registerCommand('resolveCodeAction', commands.resolveCodeAction);
ctx.registerCommand('applyActionGroup', commands.applyActionGroup); ctx.registerCommand('applyActionGroup', commands.applyActionGroup);
ctx.registerCommand('gotoLocation', commands.gotoLocation); ctx.registerCommand('gotoLocation', commands.gotoLocation);
ctx.pushCleanup(activateTaskProvider(workspaceFolder, ctx.config));
activateInlayHints(ctx);
warnAboutExtensionConflicts();
vscode.workspace.onDidChangeConfiguration(
_ => ctx?.client?.sendNotification('workspace/didChangeConfiguration', { settings: "" }),
null,
ctx.subscriptions,
);
} }
export async function deactivate() { export async function deactivate() {