aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksey Kladov <[email protected]>2020-07-02 11:37:04 +0100
committerAleksey Kladov <[email protected]>2020-07-02 14:32:14 +0100
commit3ef76760761d17cef4ea4e8462d9ee2ca8395467 (patch)
treedbcb0bf2cc2f43533360a4a16f3994ddb88515df
parenta03cfa49268d3938b55ceff046d04a75de8972b9 (diff)
Implement StatusBar
-rw-r--r--crates/rust-analyzer/src/config.rs2
-rw-r--r--crates/rust-analyzer/src/global_state.rs1
-rw-r--r--crates/rust-analyzer/src/lsp_ext.rs18
-rw-r--r--crates/rust-analyzer/src/main_loop.rs18
-rw-r--r--docs/dev/lsp-extensions.md12
-rw-r--r--editors/code/src/client.ts1
-rw-r--r--editors/code/src/ctx.ts43
-rw-r--r--editors/code/src/lsp_ext.ts3
8 files changed, 93 insertions, 5 deletions
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 6c311648a..21acfe644 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -130,6 +130,7 @@ pub struct ClientCapsConfig {
130 pub code_action_group: bool, 130 pub code_action_group: bool,
131 pub resolve_code_action: bool, 131 pub resolve_code_action: bool,
132 pub hover_actions: bool, 132 pub hover_actions: bool,
133 pub status_notification: bool,
133} 134}
134 135
135impl Config { 136impl Config {
@@ -365,6 +366,7 @@ impl Config {
365 self.client_caps.code_action_group = get_bool("codeActionGroup"); 366 self.client_caps.code_action_group = get_bool("codeActionGroup");
366 self.client_caps.resolve_code_action = get_bool("resolveCodeAction"); 367 self.client_caps.resolve_code_action = get_bool("resolveCodeAction");
367 self.client_caps.hover_actions = get_bool("hoverActions"); 368 self.client_caps.hover_actions = get_bool("hoverActions");
369 self.client_caps.status_notification = get_bool("statusNotification");
368 } 370 }
369 } 371 }
370} 372}
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index b7b4edf66..5e9cae3f8 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -31,6 +31,7 @@ use crate::{
31pub(crate) enum Status { 31pub(crate) enum Status {
32 Loading, 32 Loading,
33 Ready, 33 Ready,
34 Invalid,
34} 35}
35 36
36impl Default for Status { 37impl Default for Status {
diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs
index 82207bbb8..d225ad5ff 100644
--- a/crates/rust-analyzer/src/lsp_ext.rs
+++ b/crates/rust-analyzer/src/lsp_ext.rs
@@ -3,7 +3,7 @@
3use std::{collections::HashMap, path::PathBuf}; 3use std::{collections::HashMap, path::PathBuf};
4 4
5use lsp_types::request::Request; 5use lsp_types::request::Request;
6use lsp_types::{Position, Range, TextDocumentIdentifier}; 6use lsp_types::{notification::Notification, Position, Range, TextDocumentIdentifier};
7use serde::{Deserialize, Serialize}; 7use serde::{Deserialize, Serialize};
8 8
9pub enum AnalyzerStatus {} 9pub enum AnalyzerStatus {}
@@ -208,6 +208,22 @@ pub struct SsrParams {
208 pub parse_only: bool, 208 pub parse_only: bool,
209} 209}
210 210
211pub enum StatusNotification {}
212
213#[serde(rename_all = "camelCase")]
214#[derive(Serialize, Deserialize)]
215pub enum Status {
216 Loading,
217 Ready,
218 NeedsReload,
219 Invalid,
220}
221
222impl Notification for StatusNotification {
223 type Params = Status;
224 const METHOD: &'static str = "rust-analyzer/status";
225}
226
211pub enum CodeActionRequest {} 227pub enum CodeActionRequest {}
212 228
213impl Request for CodeActionRequest { 229impl Request for CodeActionRequest {
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index e03038b25..a5a8c17a0 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -169,16 +169,16 @@ impl GlobalState {
169 } 169 }
170 vfs::loader::Message::Progress { n_total, n_done } => { 170 vfs::loader::Message::Progress { n_total, n_done } => {
171 if n_total == 0 { 171 if n_total == 0 {
172 self.status = Status::Ready; 172 self.transition(Status::Invalid);
173 } else { 173 } else {
174 let state = if n_done == 0 { 174 let state = if n_done == 0 {
175 self.status = Status::Loading; 175 self.transition(Status::Loading);
176 Progress::Begin 176 Progress::Begin
177 } else if n_done < n_total { 177 } else if n_done < n_total {
178 Progress::Report 178 Progress::Report
179 } else { 179 } else {
180 assert_eq!(n_done, n_total); 180 assert_eq!(n_done, n_total);
181 self.status = Status::Ready; 181 self.transition(Status::Ready);
182 Progress::End 182 Progress::End
183 }; 183 };
184 self.report_progress( 184 self.report_progress(
@@ -274,6 +274,18 @@ impl GlobalState {
274 Ok(()) 274 Ok(())
275 } 275 }
276 276
277 fn transition(&mut self, new_status: Status) {
278 self.status = Status::Ready;
279 if self.config.client_caps.status_notification {
280 let lsp_status = match new_status {
281 Status::Loading => lsp_ext::Status::Loading,
282 Status::Ready => lsp_ext::Status::Ready,
283 Status::Invalid => lsp_ext::Status::Invalid,
284 };
285 self.send_notification::<lsp_ext::StatusNotification>(lsp_status);
286 }
287 }
288
277 fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> { 289 fn on_request(&mut self, request_received: Instant, req: Request) -> Result<()> {
278 self.register_request(&req, request_received); 290 self.register_request(&req, request_received);
279 291
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index c0afb16d3..6d6bbac7c 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -399,6 +399,18 @@ Returns internal status message, mostly for debugging purposes.
399 399
400Reloads project information (that is, re-executes `cargo metadata`). 400Reloads project information (that is, re-executes `cargo metadata`).
401 401
402## Status Notification
403
404**Client Capability:** `{ "statusNotification": boolean }`
405
406**Method:** `rust-analyzer/status`
407
408**Notification:** `"loading" | "ready" | "invalid" | "needsReload"`
409
410This notification is sent from server to client.
411The client can use it to display persistent status to the user (in modline).
412For `needsReload` state, the client can provide a context-menu action to run `rust-analyzer/reloadWorkspace` request.
413
402## Syntax Tree 414## Syntax Tree
403 415
404**Method:** `rust-analyzer/syntaxTree` 416**Method:** `rust-analyzer/syntaxTree`
diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts
index 65ad573d8..3e5915c28 100644
--- a/editors/code/src/client.ts
+++ b/editors/code/src/client.ts
@@ -161,6 +161,7 @@ class ExperimentalFeatures implements lc.StaticFeature {
161 caps.codeActionGroup = true; 161 caps.codeActionGroup = true;
162 caps.resolveCodeAction = true; 162 caps.resolveCodeAction = true;
163 caps.hoverActions = true; 163 caps.hoverActions = true;
164 caps.statusNotification = true;
164 capabilities.experimental = caps; 165 capabilities.experimental = caps;
165 } 166 }
166 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void { 167 initialize(_capabilities: lc.ServerCapabilities<any>, _documentSelector: lc.DocumentSelector | undefined): void {
diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts
index 41df11991..6e767babf 100644
--- a/editors/code/src/ctx.ts
+++ b/editors/code/src/ctx.ts
@@ -1,9 +1,11 @@
1import * as vscode from 'vscode'; 1import * as vscode from 'vscode';
2import * as lc from 'vscode-languageclient'; 2import * as lc from 'vscode-languageclient';
3import * as ra from './lsp_ext';
3 4
4import { Config } from './config'; 5import { Config } from './config';
5import { createClient } from './client'; 6import { createClient } from './client';
6import { isRustEditor, RustEditor } from './util'; 7import { isRustEditor, RustEditor } from './util';
8import { Status } from './lsp_ext';
7 9
8export class Ctx { 10export class Ctx {
9 private constructor( 11 private constructor(
@@ -11,6 +13,7 @@ export class Ctx {
11 private readonly extCtx: vscode.ExtensionContext, 13 private readonly extCtx: vscode.ExtensionContext,
12 readonly client: lc.LanguageClient, 14 readonly client: lc.LanguageClient,
13 readonly serverPath: string, 15 readonly serverPath: string,
16 readonly statusBar: vscode.StatusBarItem,
14 ) { 17 ) {
15 18
16 } 19 }
@@ -22,9 +25,18 @@ export class Ctx {
22 cwd: string, 25 cwd: string,
23 ): Promise<Ctx> { 26 ): Promise<Ctx> {
24 const client = createClient(serverPath, cwd); 27 const client = createClient(serverPath, cwd);
25 const res = new Ctx(config, extCtx, client, serverPath); 28
29 const statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left);
30 extCtx.subscriptions.push(statusBar);
31 statusBar.text = "rust-analyzer";
32 statusBar.tooltip = "ready";
33 statusBar.show();
34
35 const res = new Ctx(config, extCtx, client, serverPath, statusBar);
36
26 res.pushCleanup(client.start()); 37 res.pushCleanup(client.start());
27 await client.onReady(); 38 await client.onReady();
39 client.onNotification(ra.status, (status) => res.setStatus(status));
28 return res; 40 return res;
29 } 41 }
30 42
@@ -54,6 +66,35 @@ export class Ctx {
54 return this.extCtx.subscriptions; 66 return this.extCtx.subscriptions;
55 } 67 }
56 68
69 setStatus(status: Status) {
70 switch (status) {
71 case "loading":
72 this.statusBar.text = "$(sync~spin) rust-analyzer";
73 this.statusBar.tooltip = "Loading the project";
74 this.statusBar.command = undefined;
75 this.statusBar.color = undefined;
76 break;
77 case "ready":
78 this.statusBar.text = "rust-analyzer";
79 this.statusBar.tooltip = "Ready";
80 this.statusBar.command = undefined;
81 this.statusBar.color = undefined;
82 break;
83 case "invalid":
84 this.statusBar.text = "$(error) rust-analyzer";
85 this.statusBar.tooltip = "Failed to load the project";
86 this.statusBar.command = undefined;
87 this.statusBar.color = new vscode.ThemeColor("notificationsErrorIcon.foreground");
88 break;
89 case "needsReload":
90 this.statusBar.text = "$(warning) rust-analyzer";
91 this.statusBar.tooltip = "Click to reload";
92 this.statusBar.command = "rust-analyzer.reloadWorkspace";
93 this.statusBar.color = new vscode.ThemeColor("notificationsWarningIcon.foreground");
94 break;
95 }
96 }
97
57 pushCleanup(d: Disposable) { 98 pushCleanup(d: Disposable) {
58 this.extCtx.subscriptions.push(d); 99 this.extCtx.subscriptions.push(d);
59 } 100 }
diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts
index 981b6f40e..bf4703239 100644
--- a/editors/code/src/lsp_ext.ts
+++ b/editors/code/src/lsp_ext.ts
@@ -6,6 +6,9 @@ import * as lc from "vscode-languageclient";
6 6
7export const analyzerStatus = new lc.RequestType<null, string, void>("rust-analyzer/analyzerStatus"); 7export const analyzerStatus = new lc.RequestType<null, string, void>("rust-analyzer/analyzerStatus");
8 8
9export type Status = "loading" | "ready" | "invalid" | "needsReload";
10export const status = new lc.NotificationType<Status>("rust-analyzer/status");
11
9export const reloadWorkspace = new lc.RequestType<null, null, void>("rust-analyzer/reloadWorkspace"); 12export const reloadWorkspace = new lc.RequestType<null, null, void>("rust-analyzer/reloadWorkspace");
10 13
11export interface SyntaxTreeParams { 14export interface SyntaxTreeParams {