Skip to content

Commit

Permalink
Auto merge of #13785 - Veykril:run-flycheck, r=Veykril
Browse files Browse the repository at this point in the history
Add command for manually running flychecks

Closes #13125
  • Loading branch information
bors committed Dec 16, 2022
2 parents 19c2ede + a04feb9 commit e0aa5af
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 80 deletions.
13 changes: 13 additions & 0 deletions crates/rust-analyzer/src/lsp_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,19 @@ impl Request for CancelFlycheck {
const METHOD: &'static str = "rust-analyzer/cancelFlycheck";
}

pub enum RunFlycheck {}

impl Notification for RunFlycheck {
type Params = RunFlycheckParams;
const METHOD: &'static str = "rust-analyzer/runFlycheck";
}

#[derive(Deserialize, Serialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RunFlycheckParams {
pub text_document: Option<TextDocumentIdentifier>,
}

pub enum MatchingBrace {}

impl Request for MatchingBrace {
Expand Down
173 changes: 97 additions & 76 deletions crates/rust-analyzer/src/main_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -703,6 +703,88 @@ impl GlobalState {

/// Handles an incoming notification.
fn on_notification(&mut self, not: Notification) -> Result<()> {
// FIXME: Move these implementations out into a module similar to on_request
fn run_flycheck(this: &mut GlobalState, vfs_path: VfsPath) -> bool {
let file_id = this.vfs.read().0.file_id(&vfs_path);
if let Some(file_id) = file_id {
let world = this.snapshot();
let mut updated = false;
let task = move || -> std::result::Result<(), ide::Cancelled> {
// Trigger flychecks for all workspaces that depend on the saved file
// Crates containing or depending on the saved file
let crate_ids: Vec<_> = world
.analysis
.crates_for(file_id)?
.into_iter()
.flat_map(|id| world.analysis.transitive_rev_deps(id))
.flatten()
.sorted()
.unique()
.collect();

let crate_root_paths: Vec<_> = crate_ids
.iter()
.filter_map(|&crate_id| {
world
.analysis
.crate_root(crate_id)
.map(|file_id| {
world
.file_id_to_file_path(file_id)
.as_path()
.map(ToOwned::to_owned)
})
.transpose()
})
.collect::<ide::Cancellable<_>>()?;
let crate_root_paths: Vec<_> =
crate_root_paths.iter().map(Deref::deref).collect();

// Find all workspaces that have at least one target containing the saved file
let workspace_ids =
world.workspaces.iter().enumerate().filter(|(_, ws)| match ws {
project_model::ProjectWorkspace::Cargo { cargo, .. } => {
cargo.packages().any(|pkg| {
cargo[pkg].targets.iter().any(|&it| {
crate_root_paths.contains(&cargo[it].root.as_path())
})
})
}
project_model::ProjectWorkspace::Json { project, .. } => project
.crates()
.any(|(c, _)| crate_ids.iter().any(|&crate_id| crate_id == c)),
project_model::ProjectWorkspace::DetachedFiles { .. } => false,
});

// Find and trigger corresponding flychecks
for flycheck in world.flycheck.iter() {
for (id, _) in workspace_ids.clone() {
if id == flycheck.id() {
updated = true;
flycheck.restart();
continue;
}
}
}
// No specific flycheck was triggered, so let's trigger all of them.
if !updated {
for flycheck in world.flycheck.iter() {
flycheck.restart();
}
}
Ok(())
};
this.task_pool.handle.spawn_with_sender(move |_| {
if let Err(e) = std::panic::catch_unwind(task) {
tracing::error!("flycheck task panicked: {e:?}")
}
});
true
} else {
false
}
}

NotificationDispatcher { not: Some(not), global_state: self }
.on::<lsp_types::notification::Cancel>(|this, params| {
let id: lsp_server::RequestId = match params.id {
Expand Down Expand Up @@ -782,6 +864,20 @@ impl GlobalState {
}
Ok(())
})?
.on::<lsp_ext::RunFlycheck>(|this, params| {
if let Some(text_document) = params.text_document {
if let Ok(vfs_path) = from_proto::vfs_path(&text_document.uri) {
if run_flycheck(this, vfs_path) {
return Ok(());
}
}
}
// No specific flycheck was triggered, so let's trigger all of them.
for flycheck in this.flycheck.iter() {
flycheck.restart();
}
Ok(())
})?
.on::<lsp_types::notification::DidSaveTextDocument>(|this, params| {
if let Ok(vfs_path) = from_proto::vfs_path(&params.text_document.uri) {
// Re-fetch workspaces if a workspace related file has changed
Expand All @@ -792,82 +888,7 @@ impl GlobalState {
}
}

let file_id = this.vfs.read().0.file_id(&vfs_path);
if let Some(file_id) = file_id {
let world = this.snapshot();
let mut updated = false;
let task = move || -> std::result::Result<(), ide::Cancelled> {
// Trigger flychecks for all workspaces that depend on the saved file
// Crates containing or depending on the saved file
let crate_ids: Vec<_> = world
.analysis
.crates_for(file_id)?
.into_iter()
.flat_map(|id| world.analysis.transitive_rev_deps(id))
.flatten()
.sorted()
.unique()
.collect();

let crate_root_paths: Vec<_> = crate_ids
.iter()
.filter_map(|&crate_id| {
world
.analysis
.crate_root(crate_id)
.map(|file_id| {
world
.file_id_to_file_path(file_id)
.as_path()
.map(ToOwned::to_owned)
})
.transpose()
})
.collect::<ide::Cancellable<_>>()?;
let crate_root_paths: Vec<_> =
crate_root_paths.iter().map(Deref::deref).collect();

// Find all workspaces that have at least one target containing the saved file
let workspace_ids =
world.workspaces.iter().enumerate().filter(|(_, ws)| match ws {
project_model::ProjectWorkspace::Cargo { cargo, .. } => {
cargo.packages().any(|pkg| {
cargo[pkg].targets.iter().any(|&it| {
crate_root_paths.contains(&cargo[it].root.as_path())
})
})
}
project_model::ProjectWorkspace::Json { project, .. } => {
project.crates().any(|(c, _)| {
crate_ids.iter().any(|&crate_id| crate_id == c)
})
}
project_model::ProjectWorkspace::DetachedFiles { .. } => false,
});

// Find and trigger corresponding flychecks
for flycheck in world.flycheck.iter() {
for (id, _) in workspace_ids.clone() {
if id == flycheck.id() {
updated = true;
flycheck.restart();
continue;
}
}
}
// No specific flycheck was triggered, so let's trigger all of them.
if !updated {
for flycheck in world.flycheck.iter() {
flycheck.restart();
}
}
Ok(())
};
this.task_pool.handle.spawn_with_sender(move |_| {
if let Err(e) = std::panic::catch_unwind(task) {
tracing::error!("DidSaveTextDocument flycheck task panicked: {e:?}")
}
});
if run_flycheck(this, vfs_path) {
return Ok(());
}
}
Expand Down
2 changes: 1 addition & 1 deletion docs/dev/lsp-extensions.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<!---
lsp_ext.rs hash: 61fe425627f9baaa
lsp_ext.rs hash: 1cb29d3afa36e743
If you need to change the above hash to make the test pass, please check if you
need to adjust this doc as well and ping this issue:
Expand Down
5 changes: 5 additions & 0 deletions editors/code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,11 @@
"command": "rust-analyzer.cancelFlycheck",
"title": "Cancel running flychecks",
"category": "rust-analyzer"
},
{
"command": "rust-analyzer.runFlycheck",
"title": "Run flycheck",
"category": "rust-analyzer"
}
],
"keybindings": [
Expand Down
15 changes: 12 additions & 3 deletions editors/code/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -788,21 +788,30 @@ export function openDocs(ctx: CtxInit): Cmd {

export function cancelFlycheck(ctx: CtxInit): Cmd {
return async () => {
await ctx.client.sendRequest(ra.cancelFlycheck);
};
}

export function runFlycheck(ctx: CtxInit): Cmd {
return async () => {
const editor = ctx.activeRustEditor;
const client = ctx.client;
await client.sendRequest(ra.cancelFlycheck);
const params = editor ? { uri: editor.document.uri.toString() } : null;

await client.sendNotification(ra.runFlycheck, { textDocument: params });
};
}

export function resolveCodeAction(ctx: CtxInit): Cmd {
return async (params: lc.CodeAction) => {
const client = ctx.client;
params.command = undefined;
const item = await client?.sendRequest(lc.CodeActionResolveRequest.type, params);
const item = await client.sendRequest(lc.CodeActionResolveRequest.type, params);
if (!item?.edit) {
return;
}
const itemEdit = item.edit;
const edit = await client?.protocol2CodeConverter.asWorkspaceEdit(itemEdit);
const edit = await client.protocol2CodeConverter.asWorkspaceEdit(itemEdit);
// filter out all text edits and recreate the WorkspaceEdit without them so we can apply
// snippet edits on our own
const lcFileSystemEdit = {
Expand Down
4 changes: 4 additions & 0 deletions editors/code/src/lsp_ext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,10 @@ export const relatedTests = new lc.RequestType<lc.TextDocumentPositionParams, Te

export const cancelFlycheck = new lc.RequestType0<void, void>("rust-analyzer/cancelFlycheck");

export const runFlycheck = new lc.NotificationType<{
textDocument: lc.TextDocumentIdentifier | null;
}>("rust-analyzer/runFlycheck");

// Experimental extensions

export interface SsrParams {
Expand Down
1 change: 1 addition & 0 deletions editors/code/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ function createCommands(): Record<string, CommandFactory> {
moveItemUp: { enabled: commands.moveItemUp },
moveItemDown: { enabled: commands.moveItemDown },
cancelFlycheck: { enabled: commands.cancelFlycheck },
runFlycheck: { enabled: commands.runFlycheck },
ssr: { enabled: commands.ssr },
serverVersion: { enabled: commands.serverVersion },
// Internal commands which are invoked by the server.
Expand Down

0 comments on commit e0aa5af

Please sign in to comment.