Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement StatusBar #5188

Merged
merged 2 commits into from
Jul 2, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions crates/rust-analyzer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ pub struct ClientCapsConfig {
pub code_action_group: bool,
pub resolve_code_action: bool,
pub hover_actions: bool,
pub status_notification: bool,
}

impl Config {
Expand Down Expand Up @@ -365,6 +366,7 @@ impl Config {
self.client_caps.code_action_group = get_bool("codeActionGroup");
self.client_caps.resolve_code_action = get_bool("resolveCodeAction");
self.client_caps.hover_actions = get_bool("hoverActions");
self.client_caps.status_notification = get_bool("statusNotification");
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions crates/rust-analyzer/src/global_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ use crate::{
pub(crate) enum Status {
Loading,
Ready,
Invalid,
NeedsReload,
}

impl Default for Status {
Expand Down
18 changes: 17 additions & 1 deletion crates/rust-analyzer/src/lsp_ext.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
use std::{collections::HashMap, path::PathBuf};

use lsp_types::request::Request;
use lsp_types::{Position, Range, TextDocumentIdentifier};
use lsp_types::{notification::Notification, Position, Range, TextDocumentIdentifier};
use serde::{Deserialize, Serialize};

pub enum AnalyzerStatus {}
Expand Down Expand Up @@ -208,6 +208,22 @@ pub struct SsrParams {
pub parse_only: bool,
}

pub enum StatusNotification {}

#[serde(rename_all = "camelCase")]
#[derive(Serialize, Deserialize)]
pub enum Status {
Loading,
Ready,
NeedsReload,
Invalid,
}

impl Notification for StatusNotification {
type Params = Status;
const METHOD: &'static str = "rust-analyzer/status";
}

pub enum CodeActionRequest {}

impl Request for CodeActionRequest {
Expand Down
56 changes: 52 additions & 4 deletions crates/rust-analyzer/src/main_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,35 @@ impl GlobalState {
}

fn run(mut self, inbox: Receiver<lsp_server::Message>) -> Result<()> {
let registration_options = lsp_types::TextDocumentRegistrationOptions {
document_selector: Some(vec![
lsp_types::DocumentFilter {
language: None,
scheme: None,
pattern: Some("**/*.rs".into()),
},
lsp_types::DocumentFilter {
language: None,
scheme: None,
pattern: Some("**/Cargo.toml".into()),
},
lsp_types::DocumentFilter {
language: None,
scheme: None,
pattern: Some("**/Cargo.lock".into()),
},
]),
};
let registration = lsp_types::Registration {
id: "textDocument/didSave".to_string(),
method: "textDocument/didSave".to_string(),
register_options: Some(serde_json::to_value(registration_options).unwrap()),
};
self.send_request::<lsp_types::request::RegisterCapability>(
lsp_types::RegistrationParams { registrations: vec![registration] },
|_, _| (),
);

self.reload();

while let Some(event) = self.next_event(&inbox) {
Expand Down Expand Up @@ -169,16 +198,16 @@ impl GlobalState {
}
vfs::loader::Message::Progress { n_total, n_done } => {
if n_total == 0 {
self.status = Status::Ready;
self.transition(Status::Invalid);
} else {
let state = if n_done == 0 {
self.status = Status::Loading;
self.transition(Status::Loading);
Progress::Begin
} else if n_done < n_total {
Progress::Report
} else {
assert_eq!(n_done, n_total);
self.status = Status::Ready;
self.transition(Status::Ready);
Progress::End
};
self.report_progress(
Expand Down Expand Up @@ -274,6 +303,19 @@ impl GlobalState {
Ok(())
}

fn transition(&mut self, new_status: Status) {
self.status = Status::Ready;
if self.config.client_caps.status_notification {
let lsp_status = match new_status {
Status::Loading => lsp_ext::Status::Loading,
Status::Ready => lsp_ext::Status::Ready,
Status::Invalid => lsp_ext::Status::Invalid,
Status::NeedsReload => lsp_ext::Status::NeedsReload,
};
self.send_notification::<lsp_ext::StatusNotification>(lsp_status);
}
}

fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> {
self.register_request(&req, request_received);

Expand Down Expand Up @@ -383,10 +425,16 @@ impl GlobalState {
);
Ok(())
})?
.on::<lsp_types::notification::DidSaveTextDocument>(|this, _params| {
.on::<lsp_types::notification::DidSaveTextDocument>(|this, params| {
if let Some(flycheck) = &this.flycheck {
flycheck.handle.update();
}
let uri = params.text_document.uri.as_str();
if uri.ends_with("Cargo.toml") || uri.ends_with("Cargo.lock") {
if matches!(this.status, Status::Ready | Status::Invalid) {
this.transition(Status::NeedsReload);
}
}
Ok(())
})?
.on::<lsp_types::notification::DidChangeConfiguration>(|this, _params| {
Expand Down
2 changes: 1 addition & 1 deletion crates/rust-analyzer/src/reload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ impl GlobalState {
.collect(),
};
let registration = lsp_types::Registration {
id: "file-watcher".to_string(),
id: "workspace/didChangeWatchedFiles".to_string(),
method: "workspace/didChangeWatchedFiles".to_string(),
register_options: Some(serde_json::to_value(registration_options).unwrap()),
};
Expand Down
17 changes: 12 additions & 5 deletions crates/rust-analyzer/tests/heavy_tests/support.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,19 @@ impl Server {
while let Some(msg) = self.recv() {
match msg {
Message::Request(req) => {
if req.method != "window/workDoneProgress/create"
&& !(req.method == "client/registerCapability"
&& req.params.to_string().contains("workspace/didChangeWatchedFiles"))
{
panic!("unexpected request: {:?}", req)
if req.method == "window/workDoneProgress/create" {
continue;
}
if req.method == "client/registerCapability" {
let params = req.params.to_string();
if ["workspace/didChangeWatchedFiles", "textDocument/didSave"]
.iter()
.any(|&it| params.contains(it))
{
continue;
}
}
panic!("unexpected request: {:?}", req)
}
Message::Notification(_) => (),
Message::Response(res) => {
Expand Down
12 changes: 12 additions & 0 deletions docs/dev/lsp-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,18 @@ Returns internal status message, mostly for debugging purposes.

Reloads project information (that is, re-executes `cargo metadata`).

## Status Notification

**Client Capability:** `{ "statusNotification": boolean }`

**Method:** `rust-analyzer/status`

**Notification:** `"loading" | "ready" | "invalid" | "needsReload"`

This notification is sent from server to client.
The client can use it to display persistent status to the user (in modline).
For `needsReload` state, the client can provide a context-menu action to run `rust-analyzer/reloadWorkspace` request.

## Syntax Tree

**Method:** `rust-analyzer/syntaxTree`
Expand Down
1 change: 1 addition & 0 deletions editors/code/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ class ExperimentalFeatures implements lc.StaticFeature {
caps.codeActionGroup = true;
caps.resolveCodeAction = true;
caps.hoverActions = true;
caps.statusNotification = true;
capabilities.experimental = caps;
}
initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void {
Expand Down
43 changes: 42 additions & 1 deletion editors/code/src/ctx.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import * as vscode from 'vscode';
import * as lc from 'vscode-languageclient';
import * as ra from './lsp_ext';

import { Config } from './config';
import { createClient } from './client';
import { isRustEditor, RustEditor } from './util';
import { Status } from './lsp_ext';

export class Ctx {
private constructor(
readonly config: Config,
private readonly extCtx: vscode.ExtensionContext,
readonly client: lc.LanguageClient,
readonly serverPath: string,
readonly statusBar: vscode.StatusBarItem,
) {

}
Expand All @@ -22,9 +25,18 @@ export class Ctx {
cwd: string,
): Promise<Ctx> {
const client = createClient(serverPath, cwd);
const res = new Ctx(config, extCtx, client, serverPath);

const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
extCtx.subscriptions.push(statusBar);
statusBar.text = "rust-analyzer";
statusBar.tooltip = "ready";
statusBar.show();

const res = new Ctx(config, extCtx, client, serverPath, statusBar);

res.pushCleanup(client.start());
await client.onReady();
client.onNotification(ra.status, (status) => res.setStatus(status));
return res;
}

Expand Down Expand Up @@ -54,6 +66,35 @@ export class Ctx {
return this.extCtx.subscriptions;
}

setStatus(status: Status) {
switch (status) {
case "loading":
this.statusBar.text = "$(sync~spin) rust-analyzer";
this.statusBar.tooltip = "Loading the project";
this.statusBar.command = undefined;
this.statusBar.color = undefined;
break;
case "ready":
this.statusBar.text = "rust-analyzer";
this.statusBar.tooltip = "Ready";
this.statusBar.command = undefined;
this.statusBar.color = undefined;
break;
case "invalid":
this.statusBar.text = "$(error) rust-analyzer";
this.statusBar.tooltip = "Failed to load the project";
this.statusBar.command = undefined;
this.statusBar.color = new vscode.ThemeColor("notificationsErrorIcon.foreground");
break;
case "needsReload":
this.statusBar.text = "$(warning) rust-analyzer";
this.statusBar.tooltip = "Click to reload";
this.statusBar.command = "rust-analyzer.reloadWorkspace";
this.statusBar.color = new vscode.ThemeColor("notificationsWarningIcon.foreground");
break;
}
}

pushCleanup(d: Disposable) {
this.extCtx.subscriptions.push(d);
}
Expand Down
3 changes: 3 additions & 0 deletions editors/code/src/lsp_ext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import * as lc from "vscode-languageclient";

export const analyzerStatus = new lc.RequestType<null, string, void>("rust-analyzer/analyzerStatus");

export type Status = "loading" | "ready" | "invalid" | "needsReload";
export const status = new lc.NotificationType<Status>("rust-analyzer/status");

export const reloadWorkspace = new lc.RequestType<null, null, void>("rust-analyzer/reloadWorkspace");

export interface SyntaxTreeParams {
Expand Down