From 46d6357994eb8ced835a69390dfbf01abed40ee1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 3 Aug 2022 18:22:45 +0200 Subject: Add a setting to disable comment continuation in VSCode --- editors/code/src/config.ts | 14 +++++++++++--- editors/code/src/main.ts | 4 +++- 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index b04f18890b9..1c58040d58c 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -16,9 +16,13 @@ export class Config { readonly extensionId = "rust-lang.rust-analyzer"; readonly rootSection = "rust-analyzer"; - private readonly requiresWorkspaceReloadOpts = ["serverPath", "server"].map( - (opt) => `${this.rootSection}.${opt}` - ); + private readonly requiresWorkspaceReloadOpts = [ + "serverPath", + "server", + // FIXME: This shouldn't be here, changing this setting should reload + // `continueCommentsOnNewline` behavior without restart + "typing", + ].map((opt) => `${this.rootSection}.${opt}`); private readonly requiresReloadOpts = [ "cargo", "procMacro", @@ -140,6 +144,10 @@ export class Config { return this.get("restartServerOnConfigChange"); } + get typingContinueCommentsOnNewline() { + return this.get("typing.continueCommentsOnNewline"); + } + get debug() { let sourceFileMap = this.get | "auto">("debug.sourceFileMap"); if (sourceFileMap !== "auto") { diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 9ae20ddc4ac..d78b711a47a 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -84,7 +84,9 @@ async function tryActivate(context: vscode.ExtensionContext): Promise -- cgit 1.4.1-3-g733a5 From 4b648d8f6cfb9f58490a70e4539aa36c89b9d35e Mon Sep 17 00:00:00 2001 From: Jake Heinz Date: Mon, 8 Aug 2022 00:20:17 +0000 Subject: [code] make toggleInlayHints understand {off,on}UntilPressed --- editors/code/src/commands.ts | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index bf55329ca1f..1b793bb0b15 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -326,8 +326,22 @@ export function toggleInlayHints(_ctx: Ctx): Cmd { const config = vscode.workspace.getConfiguration("editor.inlayHints", { languageId: "rust", }); - const value = !config.get("enabled"); - await config.update("enabled", value, vscode.ConfigurationTarget.Global); + + const value = config.get("enabled"); + let stringValue; + if (typeof value === "string") { + stringValue = value; + } else { + stringValue = value ? "on" : "off"; + } + const nextValues: Record = { + on: "off", + off: "on", + onUnlessPressed: "offUnlessPressed", + offUnlessPressed: "onUnlessPressed", + }; + const nextValue = nextValues[stringValue] ?? "on"; + await config.update("enabled", nextValue, vscode.ConfigurationTarget.Global); }; } -- cgit 1.4.1-3-g733a5 From ec8256dd8027bbe5493698e4b9125f1f0cf8c646 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 13 Aug 2022 20:18:21 +0200 Subject: Move VSCode diagnostics workaroudn into client code --- crates/rust-analyzer/src/handlers.rs | 3 +-- crates/rust-analyzer/src/main_loop.rs | 22 ++++------------------ editors/code/src/client.ts | 9 +++++++++ 3 files changed, 14 insertions(+), 20 deletions(-) (limited to 'editors/code/src') diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 47daa732d5d..943d043bc19 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -1320,8 +1320,7 @@ pub(crate) fn publish_diagnostics( .unwrap(), }), source: Some("rust-analyzer".to_string()), - // https://github.com/rust-lang/rust-analyzer/issues/11404 - message: if !d.message.is_empty() { d.message } else { " ".to_string() }, + message: d.message, related_information: None, tags: if d.unused { Some(vec![DiagnosticTag::UNNECESSARY]) } else { None }, data: None, diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 92b6be22e76..77419998249 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -327,29 +327,15 @@ impl GlobalState { continue; } - let url = file_id_to_url(&self.vfs.read().0, file_id); - let mut diagnostics = + let uri = file_id_to_url(&self.vfs.read().0, file_id); + let diagnostics = self.diagnostics.diagnostics_for(file_id).cloned().collect::>(); - for d in &mut diagnostics { - // https://github.com/rust-lang/rust-analyzer/issues/11404 - // FIXME: We should move this workaround into the client code - if d.message.is_empty() { - d.message = " ".to_string(); - } - if let Some(rds) = d.related_information.as_mut() { - for rd in rds { - if rd.message.is_empty() { - rd.message = " ".to_string(); - } - } - } - } - let version = from_proto::vfs_path(&url) + let version = from_proto::vfs_path(&uri) .map(|path| self.mem_docs.get(&path).map(|it| it.version)) .unwrap_or_default(); self.send_notification::( - lsp_types::PublishDiagnosticsParams { uri: url, diagnostics, version }, + lsp_types::PublishDiagnosticsParams { uri, diagnostics, version }, ); } } diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 8a2dea6b35b..40ba17844be 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -105,6 +105,15 @@ export async function createClient( traceOutputChannel: traceOutputChannel(), outputChannel: outputChannel(), middleware: { + async handleDiagnostics(uri, diagnostics, next) { + // Workaround for https://github.com/microsoft/vscode/issues/155531 + for (const diagnostic of diagnostics) { + if (!diagnostic.message) { + diagnostic.message = " "; + } + } + next(uri, diagnostics); + }, async provideHover( document: vscode.TextDocument, position: vscode.Position, -- cgit 1.4.1-3-g733a5 From 614969baa770360b711881a3d9776d60937c98db Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 13 Aug 2022 20:49:00 +0200 Subject: Pad empty diagnostic messages in relatedInformation as well --- editors/code/src/client.ts | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'editors/code/src') diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 40ba17844be..27ab31db8db 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -111,6 +111,13 @@ export async function createClient( if (!diagnostic.message) { diagnostic.message = " "; } + if (diagnostic.relatedInformation) { + for (const relatedInformation of diagnostic.relatedInformation) { + if (!relatedInformation.message) { + relatedInformation.message = " "; + } + } + } } next(uri, diagnostics); }, -- cgit 1.4.1-3-g733a5 From dcbe892d7ceec113a148a89bc6d1c339c55586d1 Mon Sep 17 00:00:00 2001 From: Jonas Schievink Date: Mon, 15 Aug 2022 13:51:45 +0200 Subject: Add an HIR pretty-printer --- crates/hir-def/src/body.rs | 5 + crates/hir-def/src/body/pretty.rs | 621 +++++++++++++++++++++++++++++++++ crates/hir-def/src/builtin_type.rs | 35 ++ crates/hir-def/src/expr.rs | 6 +- crates/hir-def/src/item_tree/pretty.rs | 179 +--------- crates/hir-def/src/item_tree/tests.rs | 12 +- crates/hir-def/src/lib.rs | 1 + crates/hir-def/src/pretty.rs | 209 +++++++++++ crates/hir-def/src/type_ref.rs | 4 + crates/hir/src/lib.rs | 29 +- crates/ide/src/view_hir.rs | 14 +- editors/code/src/commands.ts | 2 +- 12 files changed, 914 insertions(+), 203 deletions(-) create mode 100644 crates/hir-def/src/body/pretty.rs create mode 100644 crates/hir-def/src/pretty.rs (limited to 'editors/code/src') diff --git a/crates/hir-def/src/body.rs b/crates/hir-def/src/body.rs index 080a307b1f8..1d818d96267 100644 --- a/crates/hir-def/src/body.rs +++ b/crates/hir-def/src/body.rs @@ -4,6 +4,7 @@ mod lower; #[cfg(test)] mod tests; pub mod scope; +mod pretty; use std::{ops::Index, sync::Arc}; @@ -352,6 +353,10 @@ impl Body { } } + pub fn pretty_print(&self, db: &dyn DefDatabase, owner: DefWithBodyId) -> String { + pretty::print_body_hir(db, self, owner) + } + fn new( db: &dyn DefDatabase, expander: Expander, diff --git a/crates/hir-def/src/body/pretty.rs b/crates/hir-def/src/body/pretty.rs new file mode 100644 index 00000000000..ddd476efe5c --- /dev/null +++ b/crates/hir-def/src/body/pretty.rs @@ -0,0 +1,621 @@ +//! A pretty-printer for HIR. + +use std::fmt::{self, Write}; + +use crate::{ + expr::{Array, BindingAnnotation, Literal, Statement}, + pretty::{print_generic_args, print_path, print_type_ref}, + type_ref::TypeRef, +}; + +use super::*; + +pub(super) fn print_body_hir(db: &dyn DefDatabase, body: &Body, owner: DefWithBodyId) -> String { + let needs_semi; + let header = match owner { + DefWithBodyId::FunctionId(it) => { + needs_semi = false; + let item_tree_id = it.lookup(db).id; + format!("fn {}(…) ", item_tree_id.item_tree(db)[item_tree_id.value].name) + } + DefWithBodyId::StaticId(it) => { + needs_semi = true; + let item_tree_id = it.lookup(db).id; + format!("static {} = ", item_tree_id.item_tree(db)[item_tree_id.value].name) + } + DefWithBodyId::ConstId(it) => { + needs_semi = true; + let item_tree_id = it.lookup(db).id; + let name = match &item_tree_id.item_tree(db)[item_tree_id.value].name { + Some(name) => name.to_string(), + None => "_".to_string(), + }; + format!("const {} = ", name) + } + }; + + let mut p = Printer { body, buf: header, indent_level: 0, needs_indent: false }; + p.print_expr(body.body_expr); + if needs_semi { + p.buf.push(';'); + } + p.buf +} + +macro_rules! w { + ($dst:expr, $($arg:tt)*) => { + { let _ = write!($dst, $($arg)*); } + }; +} + +macro_rules! wln { + ($dst:expr) => { + { let _ = writeln!($dst); } + }; + ($dst:expr, $($arg:tt)*) => { + { let _ = writeln!($dst, $($arg)*); } + }; +} + +struct Printer<'a> { + body: &'a Body, + buf: String, + indent_level: usize, + needs_indent: bool, +} + +impl<'a> Write for Printer<'a> { + fn write_str(&mut self, s: &str) -> fmt::Result { + for line in s.split_inclusive('\n') { + if self.needs_indent { + match self.buf.chars().rev().skip_while(|ch| *ch == ' ').next() { + Some('\n') | None => {} + _ => self.buf.push('\n'), + } + self.buf.push_str(&" ".repeat(self.indent_level)); + self.needs_indent = false; + } + + self.buf.push_str(line); + self.needs_indent = line.ends_with('\n'); + } + + Ok(()) + } +} + +impl<'a> Printer<'a> { + fn indented(&mut self, f: impl FnOnce(&mut Self)) { + self.indent_level += 1; + wln!(self); + f(self); + self.indent_level -= 1; + self.buf = self.buf.trim_end_matches('\n').to_string(); + } + + fn whitespace(&mut self) { + match self.buf.chars().next_back() { + None | Some('\n' | ' ') => {} + _ => self.buf.push(' '), + } + } + + fn newline(&mut self) { + match self.buf.chars().rev().skip_while(|ch| *ch == ' ').next() { + Some('\n') | None => {} + _ => writeln!(self).unwrap(), + } + } + + fn print_expr(&mut self, expr: ExprId) { + let expr = &self.body[expr]; + + match expr { + Expr::Missing => w!(self, "�"), + Expr::Underscore => w!(self, "_"), + Expr::Path(path) => self.print_path(path), + Expr::If { condition, then_branch, else_branch } => { + w!(self, "if "); + self.print_expr(*condition); + w!(self, " "); + self.print_expr(*then_branch); + if let Some(els) = *else_branch { + w!(self, " else "); + self.print_expr(els); + } + } + Expr::Let { pat, expr } => { + w!(self, "let "); + self.print_pat(*pat); + w!(self, " = "); + self.print_expr(*expr); + } + Expr::Loop { body, label } => { + if let Some(lbl) = label { + w!(self, "{}: ", self.body[*lbl].name); + } + w!(self, "loop "); + self.print_expr(*body); + } + Expr::While { condition, body, label } => { + if let Some(lbl) = label { + w!(self, "{}: ", self.body[*lbl].name); + } + w!(self, "while "); + self.print_expr(*condition); + self.print_expr(*body); + } + Expr::For { iterable, pat, body, label } => { + if let Some(lbl) = label { + w!(self, "{}: ", self.body[*lbl].name); + } + w!(self, "for "); + self.print_pat(*pat); + w!(self, " in "); + self.print_expr(*iterable); + self.print_expr(*body); + } + Expr::Call { callee, args, is_assignee_expr: _ } => { + self.print_expr(*callee); + w!(self, "("); + if !args.is_empty() { + self.indented(|p| { + for arg in &**args { + p.print_expr(*arg); + wln!(p, ","); + } + }); + } + w!(self, ")"); + } + Expr::MethodCall { receiver, method_name, args, generic_args } => { + self.print_expr(*receiver); + w!(self, ".{}", method_name); + if let Some(args) = generic_args { + w!(self, "::<"); + print_generic_args(args, self).unwrap(); + w!(self, ">"); + } + w!(self, "("); + if !args.is_empty() { + self.indented(|p| { + for arg in &**args { + p.print_expr(*arg); + wln!(p, ","); + } + }); + } + w!(self, ")"); + } + Expr::Match { expr, arms } => { + w!(self, "match "); + self.print_expr(*expr); + w!(self, " {{"); + self.indented(|p| { + for arm in &**arms { + p.print_pat(arm.pat); + if let Some(guard) = arm.guard { + w!(p, " if "); + p.print_expr(guard); + } + w!(p, " => "); + p.print_expr(arm.expr); + wln!(p, ","); + } + }); + wln!(self, "}}"); + } + Expr::Continue { label } => { + w!(self, "continue"); + if let Some(label) = label { + w!(self, " {}", label); + } + } + Expr::Break { expr, label } => { + w!(self, "break"); + if let Some(label) = label { + w!(self, " {}", label); + } + if let Some(expr) = expr { + self.whitespace(); + self.print_expr(*expr); + } + } + Expr::Return { expr } => { + w!(self, "return"); + if let Some(expr) = expr { + self.whitespace(); + self.print_expr(*expr); + } + } + Expr::Yield { expr } => { + w!(self, "yield"); + if let Some(expr) = expr { + self.whitespace(); + self.print_expr(*expr); + } + } + Expr::RecordLit { path, fields, spread, ellipsis, is_assignee_expr: _ } => { + match path { + Some(path) => self.print_path(path), + None => w!(self, "�"), + } + + w!(self, "{{"); + self.indented(|p| { + for field in &**fields { + w!(p, "{}: ", field.name); + p.print_expr(field.expr); + wln!(p, ","); + } + if let Some(spread) = spread { + w!(p, ".."); + p.print_expr(*spread); + wln!(p); + } + if *ellipsis { + wln!(p, ".."); + } + }); + w!(self, "}}"); + } + Expr::Field { expr, name } => { + self.print_expr(*expr); + w!(self, ".{}", name); + } + Expr::Await { expr } => { + self.print_expr(*expr); + w!(self, ".await"); + } + Expr::Try { expr } => { + self.print_expr(*expr); + w!(self, "?"); + } + Expr::TryBlock { body } => { + w!(self, "try "); + self.print_expr(*body); + } + Expr::Async { body } => { + w!(self, "async "); + self.print_expr(*body); + } + Expr::Const { body } => { + w!(self, "const "); + self.print_expr(*body); + } + Expr::Cast { expr, type_ref } => { + self.print_expr(*expr); + w!(self, " as "); + self.print_type_ref(type_ref); + } + Expr::Ref { expr, rawness, mutability } => { + w!(self, "&"); + if rawness.is_raw() { + w!(self, "raw "); + } + if mutability.is_mut() { + w!(self, "mut "); + } + self.print_expr(*expr); + } + Expr::Box { expr } => { + w!(self, "box "); + self.print_expr(*expr); + } + Expr::UnaryOp { expr, op } => { + let op = match op { + ast::UnaryOp::Deref => "*", + ast::UnaryOp::Not => "!", + ast::UnaryOp::Neg => "-", + }; + w!(self, "{}", op); + self.print_expr(*expr); + } + Expr::BinaryOp { lhs, rhs, op } => { + let (bra, ket) = match op { + None | Some(ast::BinaryOp::Assignment { .. }) => ("", ""), + _ => ("(", ")"), + }; + w!(self, "{}", bra); + self.print_expr(*lhs); + w!(self, "{} ", ket); + match op { + Some(op) => w!(self, "{}", op), + None => w!(self, "�"), // :) + } + w!(self, " {}", bra); + self.print_expr(*rhs); + w!(self, "{}", ket); + } + Expr::Range { lhs, rhs, range_type } => { + if let Some(lhs) = lhs { + w!(self, "("); + self.print_expr(*lhs); + w!(self, ") "); + } + let range = match range_type { + ast::RangeOp::Exclusive => "..", + ast::RangeOp::Inclusive => "..=", + }; + w!(self, "{}", range); + if let Some(rhs) = rhs { + w!(self, "("); + self.print_expr(*rhs); + w!(self, ") "); + } + } + Expr::Index { base, index } => { + self.print_expr(*base); + w!(self, "["); + self.print_expr(*index); + w!(self, "]"); + } + Expr::Closure { args, arg_types, ret_type, body } => { + w!(self, "|"); + for (i, (pat, ty)) in args.iter().zip(arg_types.iter()).enumerate() { + if i != 0 { + w!(self, ", "); + } + self.print_pat(*pat); + if let Some(ty) = ty { + w!(self, ": "); + self.print_type_ref(ty); + } + } + w!(self, "|"); + if let Some(ret_ty) = ret_type { + w!(self, " -> "); + self.print_type_ref(ret_ty); + } + self.whitespace(); + self.print_expr(*body); + } + Expr::Tuple { exprs, is_assignee_expr: _ } => { + w!(self, "("); + for expr in exprs.iter() { + self.print_expr(*expr); + w!(self, ", "); + } + w!(self, ")"); + } + Expr::Unsafe { body } => { + w!(self, "unsafe "); + self.print_expr(*body); + } + Expr::Array(arr) => { + w!(self, "["); + if !matches!(arr, Array::ElementList { elements, .. } if elements.is_empty()) { + self.indented(|p| match arr { + Array::ElementList { elements, is_assignee_expr: _ } => { + for elem in elements.iter() { + p.print_expr(*elem); + w!(p, ", "); + } + } + Array::Repeat { initializer, repeat } => { + p.print_expr(*initializer); + w!(p, "; "); + p.print_expr(*repeat); + } + }); + self.newline(); + } + w!(self, "]"); + } + Expr::Literal(lit) => self.print_literal(lit), + Expr::Block { id: _, statements, tail, label } => { + self.whitespace(); + if let Some(lbl) = label { + w!(self, "{}: ", self.body[*lbl].name); + } + w!(self, "{{"); + if !statements.is_empty() || tail.is_some() { + self.indented(|p| { + for stmt in &**statements { + p.print_stmt(stmt); + } + if let Some(tail) = tail { + p.print_expr(*tail); + } + p.newline(); + }); + } + w!(self, "}}"); + } + Expr::MacroStmts { statements, tail } => { + w!(self, "{{ // macro statements"); + self.indented(|p| { + for stmt in statements.iter() { + p.print_stmt(stmt); + } + if let Some(tail) = tail { + p.print_expr(*tail); + } + }); + self.newline(); + w!(self, "}}"); + } + } + } + + fn print_pat(&mut self, pat: PatId) { + let pat = &self.body[pat]; + + match pat { + Pat::Missing => w!(self, "�"), + Pat::Wild => w!(self, "_"), + Pat::Tuple { args, ellipsis } => { + w!(self, "("); + for (i, pat) in args.iter().enumerate() { + if i != 0 { + w!(self, ", "); + } + if *ellipsis == Some(i) { + w!(self, ".., "); + } + self.print_pat(*pat); + } + w!(self, ")"); + } + Pat::Or(pats) => { + for (i, pat) in pats.iter().enumerate() { + if i != 0 { + w!(self, " | "); + } + self.print_pat(*pat); + } + } + Pat::Record { path, args, ellipsis } => { + match path { + Some(path) => self.print_path(path), + None => w!(self, "�"), + } + + w!(self, " {{"); + self.indented(|p| { + for arg in args.iter() { + w!(p, "{}: ", arg.name); + p.print_pat(arg.pat); + wln!(p, ","); + } + if *ellipsis { + wln!(p, ".."); + } + }); + w!(self, "}}"); + } + Pat::Range { start, end } => { + self.print_expr(*start); + w!(self, "..."); + self.print_expr(*end); + } + Pat::Slice { prefix, slice, suffix } => { + w!(self, "["); + for pat in prefix.iter() { + self.print_pat(*pat); + w!(self, ", "); + } + if let Some(pat) = slice { + self.print_pat(*pat); + w!(self, ", "); + } + for pat in suffix.iter() { + self.print_pat(*pat); + w!(self, ", "); + } + w!(self, "]"); + } + Pat::Path(path) => self.print_path(path), + Pat::Lit(expr) => self.print_expr(*expr), + Pat::Bind { mode, name, subpat } => { + let mode = match mode { + BindingAnnotation::Unannotated => "", + BindingAnnotation::Mutable => "mut ", + BindingAnnotation::Ref => "ref ", + BindingAnnotation::RefMut => "ref mut ", + }; + w!(self, "{}{}", mode, name); + if let Some(pat) = subpat { + self.whitespace(); + self.print_pat(*pat); + } + } + Pat::TupleStruct { path, args, ellipsis } => { + match path { + Some(path) => self.print_path(path), + None => w!(self, "�"), + } + w!(self, "("); + for (i, arg) in args.iter().enumerate() { + if i != 0 { + w!(self, ", "); + } + if *ellipsis == Some(i) { + w!(self, ", .."); + } + self.print_pat(*arg); + } + w!(self, ")"); + } + Pat::Ref { pat, mutability } => { + w!(self, "&"); + if mutability.is_mut() { + w!(self, "mut "); + } + self.print_pat(*pat); + } + Pat::Box { inner } => { + w!(self, "box "); + self.print_pat(*inner); + } + Pat::ConstBlock(c) => { + w!(self, "const "); + self.print_expr(*c); + } + } + } + + fn print_stmt(&mut self, stmt: &Statement) { + match stmt { + Statement::Let { pat, type_ref, initializer, else_branch } => { + w!(self, "let "); + self.print_pat(*pat); + if let Some(ty) = type_ref { + w!(self, ": "); + self.print_type_ref(ty); + } + if let Some(init) = initializer { + w!(self, " = "); + self.print_expr(*init); + } + if let Some(els) = else_branch { + w!(self, " else "); + self.print_expr(*els); + } + wln!(self, ";"); + } + Statement::Expr { expr, has_semi } => { + self.print_expr(*expr); + if *has_semi { + w!(self, ";"); + } + wln!(self); + } + } + } + + fn print_literal(&mut self, literal: &Literal) { + match literal { + Literal::String(it) => w!(self, "{:?}", it), + Literal::ByteString(it) => w!(self, "\"{}\"", it.escape_ascii()), + Literal::Char(it) => w!(self, "'{}'", it.escape_debug()), + Literal::Bool(it) => w!(self, "{}", it), + Literal::Int(i, suffix) => { + w!(self, "{}", i); + if let Some(suffix) = suffix { + w!(self, "{}", suffix); + } + } + Literal::Uint(i, suffix) => { + w!(self, "{}", i); + if let Some(suffix) = suffix { + w!(self, "{}", suffix); + } + } + Literal::Float(f, suffix) => { + w!(self, "{}", f); + if let Some(suffix) = suffix { + w!(self, "{}", suffix); + } + } + } + } + + fn print_type_ref(&mut self, ty: &TypeRef) { + print_type_ref(ty, self).unwrap(); + } + + fn print_path(&mut self, path: &Path) { + print_path(path, self).unwrap(); + } +} diff --git a/crates/hir-def/src/builtin_type.rs b/crates/hir-def/src/builtin_type.rs index 25a408036ff..dd69c3ab473 100644 --- a/crates/hir-def/src/builtin_type.rs +++ b/crates/hir-def/src/builtin_type.rs @@ -156,3 +156,38 @@ impl BuiltinFloat { Some(res) } } + +impl fmt::Display for BuiltinInt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + BuiltinInt::Isize => "isize", + BuiltinInt::I8 => "i8", + BuiltinInt::I16 => "i16", + BuiltinInt::I32 => "i32", + BuiltinInt::I64 => "i64", + BuiltinInt::I128 => "i128", + }) + } +} + +impl fmt::Display for BuiltinUint { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + BuiltinUint::Usize => "usize", + BuiltinUint::U8 => "u8", + BuiltinUint::U16 => "u16", + BuiltinUint::U32 => "u32", + BuiltinUint::U64 => "u64", + BuiltinUint::U128 => "u128", + }) + } +} + +impl fmt::Display for BuiltinFloat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(match self { + BuiltinFloat::F32 => "f32", + BuiltinFloat::F64 => "f64", + }) + } +} diff --git a/crates/hir-def/src/expr.rs b/crates/hir-def/src/expr.rs index c1b3788acb7..4381b43c258 100644 --- a/crates/hir-def/src/expr.rs +++ b/crates/hir-def/src/expr.rs @@ -12,6 +12,8 @@ //! //! See also a neighboring `body` module. +use std::fmt; + use hir_expand::name::Name; use la_arena::{Idx, RawIdx}; @@ -52,8 +54,8 @@ impl FloatTypeWrapper { } } -impl std::fmt::Display for FloatTypeWrapper { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl fmt::Display for FloatTypeWrapper { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{:?}", f64::from_bits(self.0)) } } diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs index f12d9a1273c..34dd817fd13 100644 --- a/crates/hir-def/src/item_tree/pretty.rs +++ b/crates/hir-def/src/item_tree/pretty.rs @@ -2,13 +2,10 @@ use std::fmt::{self, Write}; -use itertools::Itertools; - use crate::{ attr::RawAttrs, generics::{TypeOrConstParamData, WherePredicate, WherePredicateTypeTarget}, - path::GenericArg, - type_ref::TraitBoundModifier, + pretty::{print_path, print_type_bounds, print_type_ref}, visibility::RawVisibility, }; @@ -464,183 +461,15 @@ impl<'a> Printer<'a> { } fn print_type_ref(&mut self, type_ref: &TypeRef) { - // FIXME: deduplicate with `HirDisplay` impl - match type_ref { - TypeRef::Never => w!(self, "!"), - TypeRef::Placeholder => w!(self, "_"), - TypeRef::Tuple(fields) => { - w!(self, "("); - for (i, field) in fields.iter().enumerate() { - if i != 0 { - w!(self, ", "); - } - self.print_type_ref(field); - } - w!(self, ")"); - } - TypeRef::Path(path) => self.print_path(path), - TypeRef::RawPtr(pointee, mtbl) => { - let mtbl = match mtbl { - Mutability::Shared => "*const", - Mutability::Mut => "*mut", - }; - w!(self, "{} ", mtbl); - self.print_type_ref(pointee); - } - TypeRef::Reference(pointee, lt, mtbl) => { - let mtbl = match mtbl { - Mutability::Shared => "", - Mutability::Mut => "mut ", - }; - w!(self, "&"); - if let Some(lt) = lt { - w!(self, "{} ", lt.name); - } - w!(self, "{}", mtbl); - self.print_type_ref(pointee); - } - TypeRef::Array(elem, len) => { - w!(self, "["); - self.print_type_ref(elem); - w!(self, "; {}]", len); - } - TypeRef::Slice(elem) => { - w!(self, "["); - self.print_type_ref(elem); - w!(self, "]"); - } - TypeRef::Fn(args_and_ret, varargs) => { - let ((_, return_type), args) = - args_and_ret.split_last().expect("TypeRef::Fn is missing return type"); - w!(self, "fn("); - for (i, (_, typeref)) in args.iter().enumerate() { - if i != 0 { - w!(self, ", "); - } - self.print_type_ref(typeref); - } - if *varargs { - if !args.is_empty() { - w!(self, ", "); - } - w!(self, "..."); - } - w!(self, ") -> "); - self.print_type_ref(return_type); - } - TypeRef::Macro(_ast_id) => { - w!(self, ""); - } - TypeRef::Error => w!(self, "{{unknown}}"), - TypeRef::ImplTrait(bounds) => { - w!(self, "impl "); - self.print_type_bounds(bounds); - } - TypeRef::DynTrait(bounds) => { - w!(self, "dyn "); - self.print_type_bounds(bounds); - } - } + print_type_ref(type_ref, self).unwrap(); } fn print_type_bounds(&mut self, bounds: &[Interned]) { - for (i, bound) in bounds.iter().enumerate() { - if i != 0 { - w!(self, " + "); - } - - match bound.as_ref() { - TypeBound::Path(path, modifier) => { - match modifier { - TraitBoundModifier::None => (), - TraitBoundModifier::Maybe => w!(self, "?"), - } - self.print_path(path) - } - TypeBound::ForLifetime(lifetimes, path) => { - w!(self, "for<{}> ", lifetimes.iter().format(", ")); - self.print_path(path); - } - TypeBound::Lifetime(lt) => w!(self, "{}", lt.name), - TypeBound::Error => w!(self, "{{unknown}}"), - } - } + print_type_bounds(bounds, self).unwrap(); } fn print_path(&mut self, path: &Path) { - match path.type_anchor() { - Some(anchor) => { - w!(self, "<"); - self.print_type_ref(anchor); - w!(self, ">::"); - } - None => match path.kind() { - PathKind::Plain => {} - PathKind::Super(0) => w!(self, "self::"), - PathKind::Super(n) => { - for _ in 0..*n { - w!(self, "super::"); - } - } - PathKind::Crate => w!(self, "crate::"), - PathKind::Abs => w!(self, "::"), - PathKind::DollarCrate(_) => w!(self, "$crate::"), - }, - } - - for (i, segment) in path.segments().iter().enumerate() { - if i != 0 { - w!(self, "::"); - } - - w!(self, "{}", segment.name); - if let Some(generics) = segment.args_and_bindings { - // NB: these are all in type position, so `::<` turbofish syntax is not necessary - w!(self, "<"); - let mut first = true; - let args = if generics.has_self_type { - let (self_ty, args) = generics.args.split_first().unwrap(); - w!(self, "Self="); - self.print_generic_arg(self_ty); - first = false; - args - } else { - &generics.args - }; - for arg in args { - if !first { - w!(self, ", "); - } - first = false; - self.print_generic_arg(arg); - } - for binding in &generics.bindings { - if !first { - w!(self, ", "); - } - first = false; - w!(self, "{}", binding.name); - if !binding.bounds.is_empty() { - w!(self, ": "); - self.print_type_bounds(&binding.bounds); - } - if let Some(ty) = &binding.type_ref { - w!(self, " = "); - self.print_type_ref(ty); - } - } - - w!(self, ">"); - } - } - } - - fn print_generic_arg(&mut self, arg: &GenericArg) { - match arg { - GenericArg::Type(ty) => self.print_type_ref(ty), - GenericArg::Const(c) => w!(self, "{}", c), - GenericArg::Lifetime(lt) => w!(self, "{}", lt.name), - } + print_path(path, self).unwrap(); } fn print_generic_params(&mut self, params: &GenericParams) { diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs index 5cdf36cc61b..e30d9652bb5 100644 --- a/crates/hir-def/src/item_tree/tests.rs +++ b/crates/hir-def/src/item_tree/tests.rs @@ -283,10 +283,10 @@ struct S { "#, expect![[r#" pub(self) struct S { - pub(self) a: Mixed<'a, T, Item = (), OtherItem = u8>, - pub(self) b: Qualified::Syntax, - pub(self) c: ::Path<'a>, - pub(self) d: dyn for<'a> Trait<'a>, + pub(self) a: Mixed::<'a, T, Item = (), OtherItem = u8>, + pub(self) b: Qualified::::Syntax, + pub(self) c: ::Path::<'a>, + pub(self) d: dyn for<'a> Trait::<'a>, } "#]], ) @@ -329,7 +329,7 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {} T: Copy, U: ?Sized; - impl<'a, 'b, T, const K: u8> S<'a, 'b, T, K> + impl<'a, 'b, T, const K: u8> S::<'a, 'b, T, K> where T: Copy, T: 'a, @@ -352,7 +352,7 @@ trait Tr<'a, T: 'a>: Super where Self: for<'a> Tr<'a, T> {} where Self: Super, T: 'a, - Self: for<'a> Tr<'a, T> + Self: for<'a> Tr::<'a, T> { } "#]], diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs index 56603f4b154..32ebfda4fd9 100644 --- a/crates/hir-def/src/lib.rs +++ b/crates/hir-def/src/lib.rs @@ -53,6 +53,7 @@ pub mod import_map; mod test_db; #[cfg(test)] mod macro_expansion_tests; +mod pretty; use std::{ hash::{Hash, Hasher}, diff --git a/crates/hir-def/src/pretty.rs b/crates/hir-def/src/pretty.rs new file mode 100644 index 00000000000..6636c8a23ca --- /dev/null +++ b/crates/hir-def/src/pretty.rs @@ -0,0 +1,209 @@ +//! Display and pretty printing routines. + +use std::fmt::{self, Write}; + +use hir_expand::mod_path::PathKind; +use itertools::Itertools; + +use crate::{ + intern::Interned, + path::{GenericArg, GenericArgs, Path}, + type_ref::{Mutability, TraitBoundModifier, TypeBound, TypeRef}, +}; + +pub(crate) fn print_path(path: &Path, buf: &mut dyn Write) -> fmt::Result { + match path.type_anchor() { + Some(anchor) => { + write!(buf, "<")?; + print_type_ref(anchor, buf)?; + write!(buf, ">::")?; + } + None => match path.kind() { + PathKind::Plain => {} + PathKind::Super(0) => write!(buf, "self")?, + PathKind::Super(n) => { + for i in 0..*n { + if i == 0 { + buf.write_str("super")?; + } else { + buf.write_str("::super")?; + } + } + } + PathKind::Crate => write!(buf, "crate")?, + PathKind::Abs => {} + PathKind::DollarCrate(_) => write!(buf, "$crate")?, + }, + } + + for (i, segment) in path.segments().iter().enumerate() { + if i != 0 || !matches!(path.kind(), PathKind::Plain) { + write!(buf, "::")?; + } + + write!(buf, "{}", segment.name)?; + if let Some(generics) = segment.args_and_bindings { + write!(buf, "::<")?; + print_generic_args(generics, buf)?; + + write!(buf, ">")?; + } + } + + Ok(()) +} + +pub(crate) fn print_generic_args(generics: &GenericArgs, buf: &mut dyn Write) -> fmt::Result { + let mut first = true; + let args = if generics.has_self_type { + let (self_ty, args) = generics.args.split_first().unwrap(); + write!(buf, "Self=")?; + print_generic_arg(self_ty, buf)?; + first = false; + args + } else { + &generics.args + }; + for arg in args { + if !first { + write!(buf, ", ")?; + } + first = false; + print_generic_arg(arg, buf)?; + } + for binding in &generics.bindings { + if !first { + write!(buf, ", ")?; + } + first = false; + write!(buf, "{}", binding.name)?; + if !binding.bounds.is_empty() { + write!(buf, ": ")?; + print_type_bounds(&binding.bounds, buf)?; + } + if let Some(ty) = &binding.type_ref { + write!(buf, " = ")?; + print_type_ref(ty, buf)?; + } + } + Ok(()) +} + +pub(crate) fn print_generic_arg(arg: &GenericArg, buf: &mut dyn Write) -> fmt::Result { + match arg { + GenericArg::Type(ty) => print_type_ref(ty, buf), + GenericArg::Const(c) => write!(buf, "{}", c), + GenericArg::Lifetime(lt) => write!(buf, "{}", lt.name), + } +} + +pub(crate) fn print_type_ref(type_ref: &TypeRef, buf: &mut dyn Write) -> fmt::Result { + // FIXME: deduplicate with `HirDisplay` impl + match type_ref { + TypeRef::Never => write!(buf, "!")?, + TypeRef::Placeholder => write!(buf, "_")?, + TypeRef::Tuple(fields) => { + write!(buf, "(")?; + for (i, field) in fields.iter().enumerate() { + if i != 0 { + write!(buf, ", ")?; + } + print_type_ref(field, buf)?; + } + write!(buf, ")")?; + } + TypeRef::Path(path) => print_path(path, buf)?, + TypeRef::RawPtr(pointee, mtbl) => { + let mtbl = match mtbl { + Mutability::Shared => "*const", + Mutability::Mut => "*mut", + }; + write!(buf, "{} ", mtbl)?; + print_type_ref(pointee, buf)?; + } + TypeRef::Reference(pointee, lt, mtbl) => { + let mtbl = match mtbl { + Mutability::Shared => "", + Mutability::Mut => "mut ", + }; + write!(buf, "&")?; + if let Some(lt) = lt { + write!(buf, "{} ", lt.name)?; + } + write!(buf, "{}", mtbl)?; + print_type_ref(pointee, buf)?; + } + TypeRef::Array(elem, len) => { + write!(buf, "[")?; + print_type_ref(elem, buf)?; + write!(buf, "; {}]", len)?; + } + TypeRef::Slice(elem) => { + write!(buf, "[")?; + print_type_ref(elem, buf)?; + write!(buf, "]")?; + } + TypeRef::Fn(args_and_ret, varargs) => { + let ((_, return_type), args) = + args_and_ret.split_last().expect("TypeRef::Fn is missing return type"); + write!(buf, "fn(")?; + for (i, (_, typeref)) in args.iter().enumerate() { + if i != 0 { + write!(buf, ", ")?; + } + print_type_ref(typeref, buf)?; + } + if *varargs { + if !args.is_empty() { + write!(buf, ", ")?; + } + write!(buf, "...")?; + } + write!(buf, ") -> ")?; + print_type_ref(return_type, buf)?; + } + TypeRef::Macro(_ast_id) => { + write!(buf, "")?; + } + TypeRef::Error => write!(buf, "{{unknown}}")?, + TypeRef::ImplTrait(bounds) => { + write!(buf, "impl ")?; + print_type_bounds(bounds, buf)?; + } + TypeRef::DynTrait(bounds) => { + write!(buf, "dyn ")?; + print_type_bounds(bounds, buf)?; + } + } + + Ok(()) +} + +pub(crate) fn print_type_bounds( + bounds: &[Interned], + buf: &mut dyn Write, +) -> fmt::Result { + for (i, bound) in bounds.iter().enumerate() { + if i != 0 { + write!(buf, " + ")?; + } + + match bound.as_ref() { + TypeBound::Path(path, modifier) => { + match modifier { + TraitBoundModifier::None => (), + TraitBoundModifier::Maybe => write!(buf, "?")?, + } + print_path(path, buf)?; + } + TypeBound::ForLifetime(lifetimes, path) => { + write!(buf, "for<{}> ", lifetimes.iter().format(", "))?; + print_path(path, buf)?; + } + TypeBound::Lifetime(lt) => write!(buf, "{}", lt.name)?, + TypeBound::Error => write!(buf, "{{unknown}}")?, + } + } + + Ok(()) +} diff --git a/crates/hir-def/src/type_ref.rs b/crates/hir-def/src/type_ref.rs index 9248059627d..5b4c71be7fb 100644 --- a/crates/hir-def/src/type_ref.rs +++ b/crates/hir-def/src/type_ref.rs @@ -77,6 +77,10 @@ impl Rawness { Rawness::Ref } } + + pub fn is_raw(&self) -> bool { + matches!(self, Self::RawPtr) + } } #[derive(Clone, PartialEq, Eq, Hash, Debug)] diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 8f984210e11..3561bdeba7f 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -72,7 +72,7 @@ use itertools::Itertools; use nameres::diagnostics::DefDiagnosticKind; use once_cell::unsync::Lazy; use rustc_hash::FxHashSet; -use stdx::{format_to, impl_from, never}; +use stdx::{impl_from, never}; use syntax::{ ast::{self, HasAttrs as _, HasDocComments, HasName}, AstNode, AstPtr, SmolStr, SyntaxNodePtr, TextRange, T, @@ -1136,6 +1136,20 @@ impl DefWithBody { } } + fn id(&self) -> DefWithBodyId { + match self { + DefWithBody::Function(it) => it.id.into(), + DefWithBody::Static(it) => it.id.into(), + DefWithBody::Const(it) => it.id.into(), + } + } + + /// A textual representation of the HIR of this def's body for debugging purposes. + pub fn debug_hir(self, db: &dyn HirDatabase) -> String { + let body = db.body(self.id()); + body.pretty_print(db.upcast(), self.id()) + } + pub fn diagnostics(self, db: &dyn HirDatabase, acc: &mut Vec) { let krate = self.module(db).id.krate(); @@ -1470,19 +1484,6 @@ impl Function { let def_map = db.crate_def_map(loc.krate(db).into()); def_map.fn_as_proc_macro(self.id).map(|id| Macro { id: id.into() }) } - - /// A textual representation of the HIR of this function for debugging purposes. - pub fn debug_hir(self, db: &dyn HirDatabase) -> String { - let body = db.body(self.id.into()); - - let mut result = String::new(); - format_to!(result, "HIR expressions in the body of `{}`:\n", self.name(db)); - for (id, expr) in body.exprs.iter() { - format_to!(result, "{:?}: {:?}\n", id, expr); - } - - result - } } // Note: logically, this belongs to `hir_ty`, but we are not using it there yet. diff --git a/crates/ide/src/view_hir.rs b/crates/ide/src/view_hir.rs index bf0835ed7e0..d2bbbf6d26a 100644 --- a/crates/ide/src/view_hir.rs +++ b/crates/ide/src/view_hir.rs @@ -1,4 +1,4 @@ -use hir::{Function, Semantics}; +use hir::{DefWithBody, Semantics}; use ide_db::base_db::FilePosition; use ide_db::RootDatabase; use syntax::{algo::find_node_at_offset, ast, AstNode}; @@ -19,8 +19,12 @@ fn body_hir(db: &RootDatabase, position: FilePosition) -> Option { let sema = Semantics::new(db); let source_file = sema.parse(position.file_id); - let function = find_node_at_offset::(source_file.syntax(), position.offset)?; - - let function: Function = sema.to_def(&function)?; - Some(function.debug_hir(db)) + let item = find_node_at_offset::(source_file.syntax(), position.offset)?; + let def: DefWithBody = match item { + ast::Item::Fn(it) => sema.to_def(&it)?.into(), + ast::Item::Const(it) => sema.to_def(&it)?.into(), + ast::Item::Static(it) => sema.to_def(&it)?.into(), + _ => return None, + }; + Some(def.debug_hir(db)) } diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 1b793bb0b15..f58de9da1bf 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -433,7 +433,7 @@ export function syntaxTree(ctx: Ctx): Cmd { // The contents of the file come from the `TextDocumentContentProvider` export function viewHir(ctx: Ctx): Cmd { const tdcp = new (class implements vscode.TextDocumentContentProvider { - readonly uri = vscode.Uri.parse("rust-analyzer-hir://viewHir/hir.txt"); + readonly uri = vscode.Uri.parse("rust-analyzer-hir://viewHir/hir.rs"); readonly eventEmitter = new vscode.EventEmitter(); constructor() { vscode.workspace.onDidChangeTextDocument( -- cgit 1.4.1-3-g733a5 From a0b257c9d91d00c054376facc6681f563f1a5e1b Mon Sep 17 00:00:00 2001 From: David Barsky Date: Tue, 16 Aug 2022 13:38:50 -0400 Subject: chore: remove unused `currentExtensionIsNightly()` in config.ts --- editors/code/src/config.ts | 6 ------ 1 file changed, 6 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 1c58040d58c..b83582a344a 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -5,8 +5,6 @@ import { log } from "./util"; export type UpdatesChannel = "stable" | "nightly"; -const NIGHTLY_TAG = "nightly"; - export type RunnableEnvCfg = | undefined | Record @@ -175,10 +173,6 @@ export class Config { gotoTypeDef: this.get("hover.actions.gotoTypeDef.enable"), }; } - - get currentExtensionIsNightly() { - return this.package.releaseTag === NIGHTLY_TAG; - } } export async function updateConfig(config: vscode.WorkspaceConfiguration) { -- cgit 1.4.1-3-g733a5 From 23747419ca52414d3ecf6f69f1e530e47ab1e937 Mon Sep 17 00:00:00 2001 From: Dezhi Wu Date: Wed, 17 Aug 2022 21:44:58 +0800 Subject: fix: a bunch of typos This PR will fix some typos detected by [typos]. There are also some other typos in the function names, variable names, and file names, which I leave as they are. I'm more certain that typos in comments should be fixed. [typos]: https://github.com/crate-ci/typos --- crates/flycheck/src/lib.rs | 2 +- crates/hir-def/src/nameres/proc_macro.rs | 2 +- crates/hir-expand/src/lib.rs | 2 +- crates/ide-assists/src/handlers/extract_module.rs | 6 +++--- .../src/handlers/replace_turbofish_with_explicit_type.rs | 2 +- crates/ide-assists/src/utils/suggest_name.rs | 4 ++-- crates/ide-completion/src/completions/postfix/format_like.rs | 4 ++-- crates/ide-completion/src/tests/flyimport.rs | 2 +- crates/ide-db/src/rename.rs | 2 +- crates/ide-diagnostics/src/handlers/missing_match_arms.rs | 2 +- crates/mbe/src/expander/matcher.rs | 2 +- crates/project-model/src/lib.rs | 2 +- crates/project-model/src/workspace.rs | 2 +- crates/rust-analyzer/src/bin/logger.rs | 2 +- crates/syntax/src/hacks.rs | 2 +- crates/vfs/src/lib.rs | 2 +- docs/dev/architecture.md | 2 +- editors/code/src/commands.ts | 2 +- editors/code/src/toolchain.ts | 2 +- 19 files changed, 23 insertions(+), 23 deletions(-) (limited to 'editors/code/src') diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 3347940ec6d..2bebea2fbfe 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -345,7 +345,7 @@ impl CargoActor { // // Because cargo only outputs one JSON object per line, we can // simply skip a line if it doesn't parse, which just ignores any - // erroneus output. + // erroneous output. let mut error = String::new(); let mut read_at_least_one_message = false; diff --git a/crates/hir-def/src/nameres/proc_macro.rs b/crates/hir-def/src/nameres/proc_macro.rs index 5089ef2d817..52b79cd0fdd 100644 --- a/crates/hir-def/src/nameres/proc_macro.rs +++ b/crates/hir-def/src/nameres/proc_macro.rs @@ -45,7 +45,7 @@ impl Attrs { kind: ProcMacroKind::CustomDerive { helpers: Box::new([]) }, }), - // `#[proc_macro_derive(Trait, attibutes(helper1, helper2, ...))]` + // `#[proc_macro_derive(Trait, attributes(helper1, helper2, ...))]` [ TokenTree::Leaf(Leaf::Ident(trait_name)), TokenTree::Leaf(Leaf::Punct(comma)), diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs index f6b97fd5405..d753d88470c 100644 --- a/crates/hir-expand/src/lib.rs +++ b/crates/hir-expand/src/lib.rs @@ -616,7 +616,7 @@ impl ExpansionInfo { let token_id = match token_id_in_attr_input { Some(token_id) => token_id, - // the token is not inside an attribute's input so do the lookup in the macro_arg as ususal + // the token is not inside an attribute's input so do the lookup in the macro_arg as usual None => { let relative_range = token.value.text_range().checked_sub(self.arg.value.text_range().start())?; diff --git a/crates/ide-assists/src/handlers/extract_module.rs b/crates/ide-assists/src/handlers/extract_module.rs index b3c4d306ac1..897980c6650 100644 --- a/crates/ide-assists/src/handlers/extract_module.rs +++ b/crates/ide-assists/src/handlers/extract_module.rs @@ -29,7 +29,7 @@ use super::remove_unused_param::range_to_remove; // Assist: extract_module // -// Extracts a selected region as seperate module. All the references, visibility and imports are +// Extracts a selected region as separate module. All the references, visibility and imports are // resolved. // // ``` @@ -105,7 +105,7 @@ pub(crate) fn extract_module(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti // //- Thirdly, resolving all the imports this includes removing paths from imports // outside the module, shifting/cloning them inside new module, or shifting the imports, or making - // new import statemnts + // new import statements //We are getting item usages and record_fields together, record_fields //for change_visibility and usages for first point mentioned above in the process @@ -661,7 +661,7 @@ fn check_intersection_and_push( import_path: TextRange, ) { if import_paths_to_be_removed.len() > 0 { - // Text ranges recieved here for imports are extended to the + // Text ranges received here for imports are extended to the // next/previous comma which can cause intersections among them // and later deletion of these can cause panics similar // to reported in #11766. So to mitigate it, we diff --git a/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs b/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs index 6112e09455a..5242f3b5100 100644 --- a/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs +++ b/crates/ide-assists/src/handlers/replace_turbofish_with_explicit_type.rs @@ -88,7 +88,7 @@ pub(crate) fn replace_turbofish_with_explicit_type( }, ); } else if let Some(InferType(t)) = let_stmt.ty() { - // If there's a type inferrence underscore, we can offer to replace it with the type in + // If there's a type inference underscore, we can offer to replace it with the type in // the turbofish. // let x: _ = fn::<...>(); let underscore_range = t.syntax().text_range(); diff --git a/crates/ide-assists/src/utils/suggest_name.rs b/crates/ide-assists/src/utils/suggest_name.rs index 779cdbc93c5..662ddd063f3 100644 --- a/crates/ide-assists/src/utils/suggest_name.rs +++ b/crates/ide-assists/src/utils/suggest_name.rs @@ -75,7 +75,7 @@ pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr { /// In current implementation, the function tries to get the name from /// the following sources: /// -/// * if expr is an argument to function/method, use paramter name +/// * if expr is an argument to function/method, use parameter name /// * if expr is a function/method call, use function name /// * expression type name if it exists (E.g. `()`, `fn() -> ()` or `!` do not have names) /// * fallback: `var_name` @@ -85,7 +85,7 @@ pub(crate) fn for_generic_parameter(ty: &ast::ImplTraitType) -> SmolStr { /// Currently it sticks to the first name found. // FIXME: Microoptimize and return a `SmolStr` here. pub(crate) fn for_variable(expr: &ast::Expr, sema: &Semantics<'_, RootDatabase>) -> String { - // `from_param` does not benifit from stripping + // `from_param` does not benefit from stripping // it need the largest context possible // so we check firstmost if let Some(name) = from_param(expr, sema) { diff --git a/crates/ide-completion/src/completions/postfix/format_like.rs b/crates/ide-completion/src/completions/postfix/format_like.rs index 6b94347e0ad..b273a4cb53b 100644 --- a/crates/ide-completion/src/completions/postfix/format_like.rs +++ b/crates/ide-completion/src/completions/postfix/format_like.rs @@ -173,7 +173,7 @@ impl FormatStrParser { } } (State::Expr, ':') if chars.peek().copied() == Some(':') => { - // path seperator + // path separator current_expr.push_str("::"); chars.next(); } @@ -185,7 +185,7 @@ impl FormatStrParser { current_expr = String::new(); self.state = State::FormatOpts; } else { - // We're inside of braced expression, assume that it's a struct field name/value delimeter. + // We're inside of braced expression, assume that it's a struct field name/value delimiter. current_expr.push(chr); } } diff --git a/crates/ide-completion/src/tests/flyimport.rs b/crates/ide-completion/src/tests/flyimport.rs index 0bba7f2459c..a63ef006875 100644 --- a/crates/ide-completion/src/tests/flyimport.rs +++ b/crates/ide-completion/src/tests/flyimport.rs @@ -159,7 +159,7 @@ pub mod some_module { pub struct ThiiiiiirdStruct; // contains all letters from the query, but not in the beginning, displayed second pub struct AfterThirdStruct; - // contains all letters from the query in the begginning, displayed first + // contains all letters from the query in the beginning, displayed first pub struct ThirdStruct; } diff --git a/crates/ide-db/src/rename.rs b/crates/ide-db/src/rename.rs index 517fe3f246d..49b81265ea5 100644 --- a/crates/ide-db/src/rename.rs +++ b/crates/ide-db/src/rename.rs @@ -82,7 +82,7 @@ impl Definition { } /// Textual range of the identifier which will change when renaming this - /// `Definition`. Note that some definitions, like buitin types, can't be + /// `Definition`. Note that some definitions, like builtin types, can't be /// renamed. pub fn range_for_rename(self, sema: &Semantics<'_, RootDatabase>) -> Option { let res = match self { diff --git a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs index 9e66fbfb752..5fcaf405b14 100644 --- a/crates/ide-diagnostics/src/handlers/missing_match_arms.rs +++ b/crates/ide-diagnostics/src/handlers/missing_match_arms.rs @@ -750,7 +750,7 @@ fn main() { enum Foo { A } fn main() { // FIXME: this should not bail out but current behavior is such as the old algorithm. - // ExprValidator::validate_match(..) checks types of top level patterns incorrecly. + // ExprValidator::validate_match(..) checks types of top level patterns incorrectly. match Foo::A { ref _x => {} Foo::A => {} diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs index 5020e9abaf3..c1aa14d6b7e 100644 --- a/crates/mbe/src/expander/matcher.rs +++ b/crates/mbe/src/expander/matcher.rs @@ -321,7 +321,7 @@ struct MatchState<'t> { /// The KleeneOp of this sequence if we are in a repetition. sep_kind: Option, - /// Number of tokens of seperator parsed + /// Number of tokens of separator parsed sep_parsed: Option, /// Matched meta variables bindings diff --git a/crates/project-model/src/lib.rs b/crates/project-model/src/lib.rs index e3f83084ac8..b81b7432f65 100644 --- a/crates/project-model/src/lib.rs +++ b/crates/project-model/src/lib.rs @@ -3,7 +3,7 @@ //! //! Pure model is represented by the [`base_db::CrateGraph`] from another crate. //! -//! In this crate, we are conserned with "real world" project models. +//! In this crate, we are concerned with "real world" project models. //! //! Specifically, here we have a representation for a Cargo project //! ([`CargoWorkspace`]) and for manually specified layout ([`ProjectJson`]). diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index daabb299f76..8d6f50f5587 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -770,7 +770,7 @@ fn handle_rustc_crates( queue.push_back(root_pkg); while let Some(pkg) = queue.pop_front() { // Don't duplicate packages if they are dependended on a diamond pattern - // N.B. if this line is ommitted, we try to analyse over 4_800_000 crates + // N.B. if this line is omitted, we try to analyse over 4_800_000 crates // which is not ideal if rustc_pkg_crates.contains_key(&pkg) { continue; diff --git a/crates/rust-analyzer/src/bin/logger.rs b/crates/rust-analyzer/src/bin/logger.rs index 0b69f75bc0d..298814af5a4 100644 --- a/crates/rust-analyzer/src/bin/logger.rs +++ b/crates/rust-analyzer/src/bin/logger.rs @@ -52,7 +52,7 @@ impl Logger { // merge chalk filter to our main filter (from RA_LOG env). // // The acceptable syntax of CHALK_DEBUG is `target[span{field=value}]=level`. - // As the value should only affect chalk crates, we'd better mannually + // As the value should only affect chalk crates, we'd better manually // specify the target. And for simplicity, CHALK_DEBUG only accept the value // that specify level. let chalk_level_dir = std::env::var("CHALK_DEBUG") diff --git a/crates/syntax/src/hacks.rs b/crates/syntax/src/hacks.rs index a047f61fa03..ec3d3d444c3 100644 --- a/crates/syntax/src/hacks.rs +++ b/crates/syntax/src/hacks.rs @@ -1,4 +1,4 @@ -//! Things which exist to solve practial issues, but which shouldn't exist. +//! Things which exist to solve practical issues, but which shouldn't exist. //! //! Please avoid adding new usages of the functions in this module diff --git a/crates/vfs/src/lib.rs b/crates/vfs/src/lib.rs index 10fae41d081..7badb1c363b 100644 --- a/crates/vfs/src/lib.rs +++ b/crates/vfs/src/lib.rs @@ -64,7 +64,7 @@ pub struct FileId(pub u32); /// Storage for all files read by rust-analyzer. /// -/// For more informations see the [crate-level](crate) documentation. +/// For more information see the [crate-level](crate) documentation. #[derive(Default)] pub struct Vfs { interner: PathInterner, diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md index 51e26c58a91..c173a239fea 100644 --- a/docs/dev/architecture.md +++ b/docs/dev/architecture.md @@ -485,7 +485,7 @@ Mind the code--architecture gap: at the moment, we are using fewer feature flags ### Serialization In Rust, it is easy (often too easy) to add serialization to any type by adding `#[derive(Serialize)]`. -This easiness is misleading -- serializable types impose significant backwards compatability constraints. +This easiness is misleading -- serializable types impose significant backwards compatibility constraints. If a type is serializable, then it is a part of some IPC boundary. You often don't control the other side of this boundary, so changing serializable types is hard. diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index f58de9da1bf..12f666401fd 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -655,7 +655,7 @@ function crateGraph(ctx: Ctx, full: boolean): Cmd { html, body { margin:0; padding:0; overflow:hidden } svg { position:fixed; top:0; left:0; height:100%; width:100% } - /* Disable the graphviz backgroud and fill the polygons */ + /* Disable the graphviz background and fill the polygons */ .graph > polygon { display:none; } :is(.node,.edge) polygon { fill: white; } diff --git a/editors/code/src/toolchain.ts b/editors/code/src/toolchain.ts index bac163da9f3..e1ca4954280 100644 --- a/editors/code/src/toolchain.ts +++ b/editors/code/src/toolchain.ts @@ -158,7 +158,7 @@ export const getPathForExecutable = memoizeAsync( try { // hmm, `os.homedir()` seems to be infallible - // it is not mentioned in docs and cannot be infered by the type signature... + // it is not mentioned in docs and cannot be inferred by the type signature... const standardPath = vscode.Uri.joinPath( vscode.Uri.file(os.homedir()), ".cargo", -- cgit 1.4.1-3-g733a5 From 45b7b6a60a7404d093f4afe81a0da10824cef7b8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 19 Aug 2022 08:52:31 +0200 Subject: Implement lsp extension for cancelling running flychecks --- crates/flycheck/src/lib.rs | 26 +++++++++++++++++++++----- crates/rust-analyzer/src/handlers.rs | 6 ++++++ crates/rust-analyzer/src/lsp_ext.rs | 8 ++++++++ crates/rust-analyzer/src/main_loop.rs | 7 ++++--- docs/dev/lsp-extensions.md | 2 +- editors/code/package.json | 5 +++++ editors/code/src/commands.ts | 6 ++++++ editors/code/src/lsp_ext.ts | 30 +++++++++++++++++------------- editors/code/src/main.ts | 1 + 9 files changed, 69 insertions(+), 22 deletions(-) (limited to 'editors/code/src') diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 2bebea2fbfe..c22945c81fc 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -77,8 +77,13 @@ impl FlycheckHandle { } /// Schedule a re-start of the cargo check worker. - pub fn update(&self) { - self.sender.send(Restart).unwrap(); + pub fn restart(&self) { + self.sender.send(Restart::Yes).unwrap(); + } + + /// Stop this cargo check worker. + pub fn cancel(&self) { + self.sender.send(Restart::No).unwrap(); } pub fn id(&self) -> usize { @@ -122,7 +127,10 @@ pub enum Progress { DidCancel, } -struct Restart; +enum Restart { + Yes, + No, +} struct FlycheckActor { id: usize, @@ -149,6 +157,7 @@ impl FlycheckActor { config: FlycheckConfig, workspace_root: AbsPathBuf, ) -> FlycheckActor { + tracing::info!(%id, ?workspace_root, "Spawning flycheck"); FlycheckActor { id, sender, config, workspace_root, cargo_handle: None } } fn progress(&self, progress: Progress) { @@ -164,10 +173,13 @@ impl FlycheckActor { fn run(mut self, inbox: Receiver) { while let Some(event) = self.next_event(&inbox) { match event { - Event::Restart(Restart) => { + Event::Restart(Restart::No) => { + self.cancel_check_process(); + } + Event::Restart(Restart::Yes) => { // Cancel the previously spawned process self.cancel_check_process(); - while let Ok(Restart) = inbox.recv_timeout(Duration::from_millis(50)) {} + while let Ok(_) = inbox.recv_timeout(Duration::from_millis(50)) {} let command = self.check_command(); tracing::debug!(?command, "will restart flycheck"); @@ -223,6 +235,10 @@ impl FlycheckActor { fn cancel_check_process(&mut self) { if let Some(cargo_handle) = self.cargo_handle.take() { + tracing::debug!( + command = ?self.check_command(), + "did cancel flycheck" + ); cargo_handle.cancel(); self.progress(Progress::DidCancel); } diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 6337d49c240..fdb3205b0a9 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -51,6 +51,12 @@ pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result< Ok(()) } +pub(crate) fn handle_cancel_flycheck(state: &mut GlobalState, _: ()) -> Result<()> { + let _p = profile::span("handle_stop_flycheck"); + state.flycheck.iter().for_each(|flycheck| flycheck.cancel()); + Ok(()) +} + pub(crate) fn handle_analyzer_status( snap: GlobalStateSnapshot, params: lsp_ext::AnalyzerStatusParams, diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 5f0e108624b..e61c8b643d2 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -129,6 +129,14 @@ pub struct ExpandedMacro { pub expansion: String, } +pub enum CancelFlycheck {} + +impl Request for CancelFlycheck { + type Params = (); + type Result = (); + const METHOD: &'static str = "rust-analyzer/cancelFlycheck"; +} + pub enum MatchingBrace {} impl Request for MatchingBrace { diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 77419998249..f187547019a 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -288,7 +288,7 @@ impl GlobalState { if became_quiescent { // Project has loaded properly, kick off initial flycheck - self.flycheck.iter().for_each(FlycheckHandle::update); + self.flycheck.iter().for_each(FlycheckHandle::restart); if self.config.prefill_caches() { self.prime_caches_queue.request_op("became quiescent".to_string()); } @@ -590,6 +590,7 @@ impl GlobalState { .on_sync_mut::(handlers::handle_workspace_reload) .on_sync_mut::(handlers::handle_memory_usage) .on_sync_mut::(handlers::handle_shuffle_crate_graph) + .on_sync_mut::(handlers::handle_cancel_flycheck) .on_sync::(handlers::handle_join_lines) .on_sync::(handlers::handle_on_enter) .on_sync::(handlers::handle_selection_range) @@ -779,7 +780,7 @@ impl GlobalState { for (id, _) in workspace_ids.clone() { if id == flycheck.id() { updated = true; - flycheck.update(); + flycheck.restart(); continue; } } @@ -798,7 +799,7 @@ impl GlobalState { // No specific flycheck was triggered, so let's trigger all of them. if !updated { for flycheck in &this.flycheck { - flycheck.update(); + flycheck.restart(); } } Ok(()) diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 5040643d34a..6d2c7d7b063 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ [^\n]+\n/m, ""); + } + diag.code = { + target: vscode.Uri.from({ + scheme: "rust-analyzer-diagnostics-view", + path: "/diagnostic message", + fragment: uri.toString(), + query: idx.toString(), + }), + value: "Click for full compiler diagnostic", + }; + } + }); + return next(uri, diagnostics); + }, async provideHover( document: vscode.TextDocument, position: vscode.Position, diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 632a7d86faa..d8dbd1df16d 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -238,6 +238,9 @@ export class Config { gotoTypeDef: this.get("hover.actions.gotoTypeDef.enable"), }; } + get previewRustcOutput() { + return this.get("diagnostics.previewRustcOutput"); + } } const VarRegex = new RegExp(/\$\{(.+?)\}/g); diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 3e366525ee2..d6cee5c8fc6 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -179,7 +179,8 @@ export class Ctx { this.traceOutputChannel, this.outputChannel, initializationOptions, - serverOptions + serverOptions, + this.config ); this.pushClientCleanup( this._client.onNotification(ra.serverStatus, (params) => diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index e76b657c1bf..25f1e83d109 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -48,6 +48,30 @@ async function activateServer(ctx: Ctx): Promise { ctx.pushExtCleanup(activateTaskProvider(ctx.config)); } + ctx.pushExtCleanup( + vscode.workspace.registerTextDocumentContentProvider( + "rust-analyzer-diagnostics-view", + new (class implements vscode.TextDocumentContentProvider { + async provideTextDocumentContent(uri: vscode.Uri): Promise { + const diags = ctx.client?.diagnostics?.get( + vscode.Uri.parse(uri.fragment, true) + ); + if (!diags) { + return "Unable to find original rustc diagnostic"; + } + + const diag = diags[parseInt(uri.query)]; + if (!diag) { + return "Unable to find original rustc diagnostic"; + } + const rendered = (diag as unknown as { data?: { rendered?: string } }).data + ?.rendered; + return rendered ?? "Unable to find original rustc diagnostic"; + } + })() + ) + ); + vscode.workspace.onDidChangeWorkspaceFolders( async (_) => ctx.onWorkspaceFolderChanges(), null, -- cgit 1.4.1-3-g733a5 From 8661740626ae7f608051e1309d802c408b1e20e2 Mon Sep 17 00:00:00 2001 From: Maria José Solano Date: Sun, 27 Nov 2022 09:46:37 -0800 Subject: Use typed notification method --- editors/code/src/main.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'editors/code/src') diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 25f1e83d109..01a1a2db7fd 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -79,7 +79,7 @@ async function activateServer(ctx: Ctx): Promise { ); vscode.workspace.onDidChangeConfiguration( async (_) => { - await ctx.client?.sendNotification("workspace/didChangeConfiguration", { + await ctx.client?.sendNotification(lc.DidChangeConfigurationNotification.type, { settings: "", }); }, -- cgit 1.4.1-3-g733a5 From a04feb915a0e5ef348075a3537a95875cb1b1ffe Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 16 Dec 2022 22:43:14 +0100 Subject: Add command for manually running flychecks --- crates/rust-analyzer/src/lsp_ext.rs | 13 +++ crates/rust-analyzer/src/main_loop.rs | 173 +++++++++++++++++++--------------- docs/dev/lsp-extensions.md | 2 +- editors/code/package.json | 5 + editors/code/src/commands.ts | 15 ++- editors/code/src/lsp_ext.ts | 4 + editors/code/src/main.ts | 1 + 7 files changed, 133 insertions(+), 80 deletions(-) (limited to 'editors/code/src') diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index a1df0b6d7dd..a2d539cf6ca 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -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, +} + pub enum MatchingBrace {} impl Request for MatchingBrace { diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 274588ce0e0..d979317b218 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -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::>()?; + 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::(|this, params| { let id: lsp_server::RequestId = match params.id { @@ -782,6 +864,20 @@ impl GlobalState { } Ok(()) })? + .on::(|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::(|this, params| { if let Ok(vfs_path) = from_proto::vfs_path(¶ms.text_document.uri) { // Re-fetch workspaces if a workspace related file has changed @@ -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::>()?; - 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(()); } } diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 5aced035d52..308a92bebe0 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ [^\n]+\n/m, ""); } + let value; + if (errorCode) { + if (typeof diag.code === "string" || typeof diag.code === "number") { + value = diag.code; + } else { + value = diag.code?.value; + } + } diag.code = { target: vscode.Uri.from({ scheme: "rust-analyzer-diagnostics-view", @@ -131,7 +141,7 @@ export async function createClient( fragment: uri.toString(), query: idx.toString(), }), - value: "Click for full compiler diagnostic", + value: value ?? "Click for full compiler diagnostic", }; } }); diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index d8dbd1df16d..eb4f965291f 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -241,6 +241,10 @@ export class Config { get previewRustcOutput() { return this.get("diagnostics.previewRustcOutput"); } + + get useRustcErrorCode() { + return this.get("diagnostics.useRustcErrorCode"); + } } const VarRegex = new RegExp(/\$\{(.+?)\}/g); -- cgit 1.4.1-3-g733a5 From c4d8cf1dad9e2ad5710c56533c42979e91520c78 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 3 Jan 2023 12:32:38 +0100 Subject: Use ZWNJ to prevent VSCode from forming ligatures between hints and code --- editors/code/src/client.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) (limited to 'editors/code/src') diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 1470c1f581d..e6595340aae 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -100,6 +100,24 @@ export async function createClient( } }, }, + async provideInlayHints(document, viewPort, token, next) { + const inlays = await next(document, viewPort, token); + if (!inlays) { + return inlays; + } + // U+200C is a zero-width non-joiner to prevent the editor from forming a ligature + // between code and hints + for (const inlay of inlays) { + if (typeof inlay.label === "string") { + inlay.label = `\u{200c}${inlay.label}\u{200c}`; + } else if (Array.isArray(inlay.label)) { + for (const it of inlay.label) { + it.value = `\u{200c}${it.value}\u{200c}`; + } + } + } + return inlays; + }, async handleDiagnostics( uri: vscode.Uri, diagnostics: vscode.Diagnostic[], -- cgit 1.4.1-3-g733a5 From 1b8141b54cb5f89bd3dee7bfbab41109d1b48cb6 Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Tue, 3 Jan 2023 10:16:16 -0500 Subject: Parse + decorate rendered ANSI cargo output Use ANSI control characters to display text decorations matching the VScode terminal theme, and strip them out when providing text content for rustc diagnostics. This adds the small `anser` library to parse the control codes, and it also supports HTML output so it should be fairly easy to switch to a rendered HTML/webview implementation if desired. --- editors/code/package-lock.json | 11 +++ editors/code/package.json | 1 + editors/code/src/client.ts | 11 ++- editors/code/src/diagnostics.ts | 212 ++++++++++++++++++++++++++++++++++++++++ editors/code/src/main.ts | 61 ++++++++---- 5 files changed, 272 insertions(+), 24 deletions(-) create mode 100644 editors/code/src/diagnostics.ts (limited to 'editors/code/src') diff --git a/editors/code/package-lock.json b/editors/code/package-lock.json index ee69d224760..4844837a06f 100644 --- a/editors/code/package-lock.json +++ b/editors/code/package-lock.json @@ -9,6 +9,7 @@ "version": "0.5.0-dev", "license": "MIT OR Apache-2.0", "dependencies": { + "anser": "^2.1.1", "d3": "^7.6.1", "d3-graphviz": "^5.0.2", "vscode-languageclient": "^8.0.2" @@ -394,6 +395,11 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/anser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/anser/-/anser-2.1.1.tgz", + "integrity": "sha512-nqLm4HxOTpeLOxcmB3QWmV5TcDFhW9y/fyQ+hivtDFcK4OQ+pQ5fzPnXHM1Mfcm0VkLtvVi1TCPr++Qy0Q/3EQ==" + }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -4096,6 +4102,11 @@ "uri-js": "^4.2.2" } }, + "anser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/anser/-/anser-2.1.1.tgz", + "integrity": "sha512-nqLm4HxOTpeLOxcmB3QWmV5TcDFhW9y/fyQ+hivtDFcK4OQ+pQ5fzPnXHM1Mfcm0VkLtvVi1TCPr++Qy0Q/3EQ==" + }, "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", diff --git a/editors/code/package.json b/editors/code/package.json index 468368668fc..3fe189e2b3b 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -35,6 +35,7 @@ "test": "cross-env TEST_VARIABLE=test node ./out/tests/runTests.js" }, "dependencies": { + "anser": "^2.1.1", "d3": "^7.6.1", "d3-graphviz": "^5.0.2", "vscode-languageclient": "^8.0.2" diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index e6595340aae..74cf44f42f7 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -3,6 +3,7 @@ import * as vscode from "vscode"; import * as ra from "../src/lsp_ext"; import * as Is from "vscode-languageclient/lib/common/utils/is"; import { assert } from "./util"; +import * as diagnostics from "./diagnostics"; import { WorkspaceEdit } from "vscode"; import { Config, substituteVSCodeVariables } from "./config"; import { randomUUID } from "crypto"; @@ -120,12 +121,12 @@ export async function createClient( }, async handleDiagnostics( uri: vscode.Uri, - diagnostics: vscode.Diagnostic[], + diagnosticList: vscode.Diagnostic[], next: lc.HandleDiagnosticsSignature ) { const preview = config.previewRustcOutput; const errorCode = config.useRustcErrorCode; - diagnostics.forEach((diag, idx) => { + diagnosticList.forEach((diag, idx) => { // Abuse the fact that VSCode leaks the LSP diagnostics data field through the // Diagnostic class, if they ever break this we are out of luck and have to go // back to the worst diagnostics experience ever:) @@ -154,8 +155,8 @@ export async function createClient( } diag.code = { target: vscode.Uri.from({ - scheme: "rust-analyzer-diagnostics-view", - path: "/diagnostic message", + scheme: diagnostics.URI_SCHEME, + path: `/diagnostic message [${idx.toString()}]`, fragment: uri.toString(), query: idx.toString(), }), @@ -163,7 +164,7 @@ export async function createClient( }; } }); - return next(uri, diagnostics); + return next(uri, diagnosticList); }, async provideHover( document: vscode.TextDocument, diff --git a/editors/code/src/diagnostics.ts b/editors/code/src/diagnostics.ts new file mode 100644 index 00000000000..9695d8bf26d --- /dev/null +++ b/editors/code/src/diagnostics.ts @@ -0,0 +1,212 @@ +import * as anser from "anser"; +import * as vscode from "vscode"; +import { ProviderResult, Range, TextEditorDecorationType, ThemeColor, window } from "vscode"; +import { Ctx } from "./ctx"; + +export const URI_SCHEME = "rust-analyzer-diagnostics-view"; + +export class TextDocumentProvider implements vscode.TextDocumentContentProvider { + private _onDidChange = new vscode.EventEmitter(); + + public constructor(private readonly ctx: Ctx) {} + + get onDidChange(): vscode.Event { + return this._onDidChange.event; + } + + triggerUpdate(uri: vscode.Uri) { + if (uri.scheme === URI_SCHEME) { + this._onDidChange.fire(uri); + } + } + + dispose() { + this._onDidChange.dispose(); + } + + async provideTextDocumentContent(uri: vscode.Uri): Promise { + const contents = getRenderedDiagnostic(this.ctx, uri); + return anser.ansiToText(contents); + } +} + +function getRenderedDiagnostic(ctx: Ctx, uri: vscode.Uri): string { + const diags = ctx.client?.diagnostics?.get(vscode.Uri.parse(uri.fragment, true)); + if (!diags) { + return "Unable to find original rustc diagnostic"; + } + + const diag = diags[parseInt(uri.query)]; + if (!diag) { + return "Unable to find original rustc diagnostic"; + } + const rendered = (diag as unknown as { data?: { rendered?: string } }).data?.rendered; + + if (!rendered) { + return "Unable to find original rustc diagnostic"; + } + + return rendered; +} + +interface AnserStyle { + fg: string; + bg: string; + fg_truecolor: string; + bg_truecolor: string; + decorations: Array; +} + +export class AnsiDecorationProvider implements vscode.Disposable { + private _decorationTypes = new Map(); + + public constructor(private readonly ctx: Ctx) {} + + dispose(): void { + for (const decorationType of this._decorationTypes.values()) { + decorationType.dispose(); + } + + this._decorationTypes.clear(); + } + + async provideDecorations(editor: vscode.TextEditor) { + if (editor.document.uri.scheme !== URI_SCHEME) { + return; + } + + const decorations = (await this._getDecorations(editor.document.uri)) || []; + for (const [decorationType, ranges] of decorations) { + editor.setDecorations(decorationType, ranges); + } + } + + private _getDecorations( + uri: vscode.Uri + ): ProviderResult<[TextEditorDecorationType, Range[]][]> { + const stringContents = getRenderedDiagnostic(this.ctx, uri); + const lines = stringContents.split("\n"); + + const result = new Map(); + // Populate all known decoration types in the result. This forces any + // lingering decorations to be cleared if the text content changes to + // something without ANSI codes for a given decoration type. + for (const decorationType of this._decorationTypes.values()) { + result.set(decorationType, []); + } + + for (const [lineNumber, line] of lines.entries()) { + const totalEscapeLength = 0; + + // eslint-disable-next-line camelcase + const parsed = anser.ansiToJson(line, { use_classes: true }); + + let offset = 0; + + for (const span of parsed) { + const { content, ...style } = span; + + const range = new Range( + lineNumber, + offset - totalEscapeLength, + lineNumber, + offset + content.length - totalEscapeLength + ); + + offset += content.length; + + const decorationType = this._getDecorationType(style); + + if (!result.has(decorationType)) { + result.set(decorationType, []); + } + + result.get(decorationType)!.push(range); + } + } + + return [...result]; + } + + private _getDecorationType(style: AnserStyle): TextEditorDecorationType { + let decorationType = this._decorationTypes.get(style); + + if (decorationType) { + return decorationType; + } + + const fontWeight = style.decorations.find((s) => s === "bold"); + const fontStyle = style.decorations.find((s) => s === "italic"); + const textDecoration = style.decorations.find((s) => s === "underline"); + + decorationType = window.createTextEditorDecorationType({ + backgroundColor: AnsiDecorationProvider._convertColor(style.bg, style.bg_truecolor), + color: AnsiDecorationProvider._convertColor(style.fg, style.fg_truecolor), + fontWeight, + fontStyle, + textDecoration, + }); + + this._decorationTypes.set(style, decorationType); + + return decorationType; + } + + // NOTE: This could just be a kebab-case to camelCase conversion, but I think it's + // a short enough list to just write these by hand + static readonly _anserToThemeColor: Record = { + "ansi-black": "ansiBlack", + "ansi-white": "ansiWhite", + "ansi-red": "ansiRed", + "ansi-green": "ansiGreen", + "ansi-yellow": "ansiYellow", + "ansi-blue": "ansiBlue", + "ansi-magenta": "ansiMagenta", + "ansi-cyan": "ansiCyan", + + "ansi-bright-black": "ansiBrightBlack", + "ansi-bright-white": "ansiBrightWhite", + "ansi-bright-red": "ansiBrightRed", + "ansi-bright-green": "ansiBrightGreen", + "ansi-bright-yellow": "ansiBrightYellow", + "ansi-bright-blue": "ansiBrightBlue", + "ansi-bright-magenta": "ansiBrightMagenta", + "ansi-bright-cyan": "ansiBrightCyan", + }; + + private static _convertColor( + color?: string, + truecolor?: string + ): ThemeColor | string | undefined { + if (!color) { + return undefined; + } + + if (color === "ansi-truecolor") { + if (!truecolor) { + return undefined; + } + return `rgb(${truecolor})`; + } + + const paletteMatch = color.match(/ansi-palette-(.+)/); + if (paletteMatch) { + const paletteColor = paletteMatch[1]; + // anser won't return both the RGB and the color name at the same time, + // so just fake a single foreground control char with the palette number: + const spans = anser.ansiToJson(`\x1b[38;5;${paletteColor}m`); + const rgb = spans[1].fg; + + if (rgb) { + return `rgb(${rgb})`; + } + } + + const themeColor = AnsiDecorationProvider._anserToThemeColor[color]; + if (themeColor) { + return new ThemeColor("terminal." + themeColor); + } + + return undefined; + } +} diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index 9a9667b2cd2..dd439317c70 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -3,6 +3,7 @@ import * as lc from "vscode-languageclient/node"; import * as commands from "./commands"; import { CommandFactory, Ctx, fetchWorkspace } from "./ctx"; +import * as diagnostics from "./diagnostics"; import { activateTaskProvider } from "./tasks"; import { setContextValue } from "./util"; @@ -48,30 +49,52 @@ async function activateServer(ctx: Ctx): Promise { ctx.pushExtCleanup(activateTaskProvider(ctx.config)); } + const diagnosticProvider = new diagnostics.TextDocumentProvider(ctx); ctx.pushExtCleanup( vscode.workspace.registerTextDocumentContentProvider( - "rust-analyzer-diagnostics-view", - new (class implements vscode.TextDocumentContentProvider { - async provideTextDocumentContent(uri: vscode.Uri): Promise { - const diags = ctx.client?.diagnostics?.get( - vscode.Uri.parse(uri.fragment, true) - ); - if (!diags) { - return "Unable to find original rustc diagnostic"; - } - - const diag = diags[parseInt(uri.query)]; - if (!diag) { - return "Unable to find original rustc diagnostic"; - } - const rendered = (diag as unknown as { data?: { rendered?: string } }).data - ?.rendered; - return rendered ?? "Unable to find original rustc diagnostic"; - } - })() + diagnostics.URI_SCHEME, + diagnosticProvider ) ); + const decorationProvider = new diagnostics.AnsiDecorationProvider(ctx); + ctx.pushExtCleanup(decorationProvider); + + async function decorateVisibleEditors(document: vscode.TextDocument) { + for (const editor of vscode.window.visibleTextEditors) { + if (document === editor.document) { + await decorationProvider.provideDecorations(editor); + } + } + } + + vscode.workspace.onDidChangeTextDocument( + async (event) => await decorateVisibleEditors(event.document), + null, + ctx.subscriptions + ); + vscode.workspace.onDidOpenTextDocument(decorateVisibleEditors, null, ctx.subscriptions); + vscode.window.onDidChangeActiveTextEditor( + async (editor) => { + if (editor) { + diagnosticProvider.triggerUpdate(editor.document.uri); + await decorateVisibleEditors(editor.document); + } + }, + null, + ctx.subscriptions + ); + vscode.window.onDidChangeVisibleTextEditors( + async (visibleEditors) => { + for (const editor of visibleEditors) { + diagnosticProvider.triggerUpdate(editor.document.uri); + await decorationProvider.provideDecorations(editor); + } + }, + null, + ctx.subscriptions + ); + vscode.workspace.onDidChangeWorkspaceFolders( async (_) => ctx.onWorkspaceFolderChanges(), null, -- cgit 1.4.1-3-g733a5 From 738ce83d858aa498c3ff28be3417101ab5f9ce6f Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Tue, 3 Jan 2023 10:49:47 -0500 Subject: Strip colors before matching preview diagnostics --- editors/code/src/client.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 74cf44f42f7..c6d64ebc1ed 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -1,3 +1,4 @@ +import * as anser from "anser"; import * as lc from "vscode-languageclient/node"; import * as vscode from "vscode"; import * as ra from "../src/lsp_ext"; @@ -139,9 +140,10 @@ export async function createClient( ?.rendered; if (rendered) { if (preview) { + const decolorized = anser.ansiToText(rendered); const index = - rendered.match(/^(note|help):/m)?.index || rendered.length; - diag.message = rendered + decolorized.match(/^(note|help):/m)?.index || rendered.length; + diag.message = decolorized .substring(0, index) .replace(/^ -->[^\n]+\n/m, ""); } -- cgit 1.4.1-3-g733a5 From 65cf7abbe2ee75cce74592fa0af75356181566be Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Wed, 4 Jan 2023 12:04:45 -0500 Subject: Use experimental capability to enable color codes --- crates/flycheck/src/lib.rs | 18 ++++++++++++------ crates/rust-analyzer/src/config.rs | 10 ++++++++-- docs/user/generated_config.adoc | 4 ++-- editors/code/package.json | 2 +- editors/code/src/client.ts | 1 + 5 files changed, 24 insertions(+), 11 deletions(-) (limited to 'editors/code/src') diff --git a/crates/flycheck/src/lib.rs b/crates/flycheck/src/lib.rs index 2911c2589a3..11f7b068ecb 100644 --- a/crates/flycheck/src/lib.rs +++ b/crates/flycheck/src/lib.rs @@ -47,6 +47,7 @@ pub enum FlycheckConfig { features: Vec, extra_args: Vec, extra_env: FxHashMap, + ansi_color_output: bool, }, CustomCommand { command: String, @@ -293,16 +294,21 @@ impl FlycheckActor { extra_args, features, extra_env, + ansi_color_output, } => { let mut cmd = Command::new(toolchain::cargo()); cmd.arg(command); cmd.current_dir(&self.root); - cmd.args([ - "--workspace", - "--message-format=json-diagnostic-rendered-ansi", - "--manifest-path", - ]) - .arg(self.root.join("Cargo.toml").as_os_str()); + cmd.arg("--workspace"); + + cmd.arg(if *ansi_color_output { + "--message-format=json-diagnostic-rendered-ansi" + } else { + "--message-format=json" + }); + + cmd.arg("--manifest-path"); + cmd.arg(self.root.join("Cargo.toml").as_os_str()); for target in target_triples { cmd.args(["--target", target.as_str()]); diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 006256544f3..b0afbdc9a42 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -161,8 +161,8 @@ config_data! { /// Override the command rust-analyzer uses instead of `cargo check` for /// diagnostics on save. The command is required to output json and /// should therefore include `--message-format=json` or a similar option - /// (for colored diagnostics, use - /// `--message-format=json-diagnostic-rendered-ansi`). + /// (if your client supports the `colorDiagnosticOutput` experimental + /// capability, you can use `--message-format=json-diagnostic-rendered-ansi`). /// /// If you're changing this because you're using some tool wrapping /// Cargo, you might also want to change @@ -1008,6 +1008,11 @@ impl Config { self.experimental("serverStatusNotification") } + /// Whether the client supports colored output for full diagnostics from `checkOnSave`. + pub fn color_diagnostic_output(&self) -> bool { + self.experimental("colorDiagnosticOutput") + } + pub fn publish_diagnostics(&self) -> bool { self.data.diagnostics_enable } @@ -1206,6 +1211,7 @@ impl Config { }, extra_args: self.data.check_extraArgs.clone(), extra_env: self.check_on_save_extra_env(), + ansi_color_output: self.color_diagnostic_output(), }, } } diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc index 5a3019831ae..b33a2e79525 100644 --- a/docs/user/generated_config.adoc +++ b/docs/user/generated_config.adoc @@ -174,8 +174,8 @@ Whether to pass `--no-default-features` to Cargo. Defaults to Override the command rust-analyzer uses instead of `cargo check` for diagnostics on save. The command is required to output json and should therefore include `--message-format=json` or a similar option -(for colored diagnostics, use -`--message-format=json-diagnostic-rendered-ansi`). +(if your client supports the `colorDiagnosticOutput` experimental +capability, you can use `--message-format=json-diagnostic-rendered-ansi`). If you're changing this because you're using some tool wrapping Cargo, you might also want to change diff --git a/editors/code/package.json b/editors/code/package.json index 77da5e54538..930564bd7ca 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -644,7 +644,7 @@ ] }, "rust-analyzer.check.overrideCommand": { - "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(for colored diagnostics, use\n`--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects, this command is invoked for\neach of them, with the working directory being the project root\n(i.e., the folder containing the `Cargo.toml`).\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.", + "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects, this command is invoked for\neach of them, with the working directory being the project root\n(i.e., the folder containing the `Cargo.toml`).\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.", "default": null, "type": [ "null", diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index c6d64ebc1ed..82cdf0390ac 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -333,6 +333,7 @@ class ExperimentalFeatures implements lc.StaticFeature { caps.codeActionGroup = true; caps.hoverActions = true; caps.serverStatusNotification = true; + caps.colorDiagnosticOutput = true; caps.commands = { commands: [ "rust-analyzer.runSingle", -- cgit 1.4.1-3-g733a5 From 4267b11c4097b52ec59323c29709a8d06ed7b4cd Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Tue, 10 Jan 2023 09:43:08 +0100 Subject: Revert "Use ZWNJ to prevent VSCode from forming ligatures between hints and code" --- editors/code/src/client.ts | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 82cdf0390ac..1fbe9d4ea03 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -102,24 +102,6 @@ export async function createClient( } }, }, - async provideInlayHints(document, viewPort, token, next) { - const inlays = await next(document, viewPort, token); - if (!inlays) { - return inlays; - } - // U+200C is a zero-width non-joiner to prevent the editor from forming a ligature - // between code and hints - for (const inlay of inlays) { - if (typeof inlay.label === "string") { - inlay.label = `\u{200c}${inlay.label}\u{200c}`; - } else if (Array.isArray(inlay.label)) { - for (const it of inlay.label) { - it.value = `\u{200c}${it.value}\u{200c}`; - } - } - } - return inlays; - }, async handleDiagnostics( uri: vscode.Uri, diagnosticList: vscode.Diagnostic[], -- cgit 1.4.1-3-g733a5 From 1c454736a4f593eebac2c43de9dfb50f9200c250 Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Tue, 17 Jan 2023 15:29:52 -0500 Subject: Suppress extra indent after the end of dot chains --- editors/code/src/config.ts | 118 +++++++++++++++++++++++++++------------------ 1 file changed, 70 insertions(+), 48 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index eb4f965291f..029dc3afd55 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -86,58 +86,80 @@ export class Config { * [1]: https://github.com/Microsoft/vscode/issues/11514#issuecomment-244707076 */ private configureLanguage() { - if (this.typingContinueCommentsOnNewline && !this.configureLang) { + if (this.configureLang) return; + + let onEnterRules: vscode.OnEnterRule[] = [ + { + // Carry indentation from the previous line + beforeText: /^\s*$/, + action: { indentAction: vscode.IndentAction.None }, + }, + { + // After the end of a function/field chain, + // with the semicolon on the same line + beforeText: /^\s+\..*;/, + action: { indentAction: vscode.IndentAction.Outdent }, + }, + { + // After the end of a function/field chain, + // with semicolon detached from the rest + beforeText: /^\s+;/, + previousLineText: /^\s+\..*/, + action: { indentAction: vscode.IndentAction.Outdent }, + }, + ]; + + if (this.typingContinueCommentsOnNewline) { const indentAction = vscode.IndentAction.None; - this.configureLang = vscode.languages.setLanguageConfiguration("rust", { - onEnterRules: [ - { - // Doc single-line comment - // e.g. ///| - beforeText: /^\s*\/{3}.*$/, - action: { indentAction, appendText: "/// " }, + onEnterRules = [ + ...onEnterRules, + { + // Doc single-line comment + // e.g. ///| + beforeText: /^\s*\/{3}.*$/, + action: { indentAction, appendText: "/// " }, + }, + { + // Parent doc single-line comment + // e.g. //!| + beforeText: /^\s*\/{2}\!.*$/, + action: { indentAction, appendText: "//! " }, + }, + { + // Begins an auto-closed multi-line comment (standard or parent doc) + // e.g. /** | */ or /*! | */ + beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/, + afterText: /^\s*\*\/$/, + action: { + indentAction: vscode.IndentAction.IndentOutdent, + appendText: " * ", }, - { - // Parent doc single-line comment - // e.g. //!| - beforeText: /^\s*\/{2}\!.*$/, - action: { indentAction, appendText: "//! " }, - }, - { - // Begins an auto-closed multi-line comment (standard or parent doc) - // e.g. /** | */ or /*! | */ - beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/, - afterText: /^\s*\*\/$/, - action: { - indentAction: vscode.IndentAction.IndentOutdent, - appendText: " * ", - }, - }, - { - // Begins a multi-line comment (standard or parent doc) - // e.g. /** ...| or /*! ...| - beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/, - action: { indentAction, appendText: " * " }, - }, - { - // Continues a multi-line comment - // e.g. * ...| - beforeText: /^(\ \ )*\ \*(\ ([^\*]|\*(?!\/))*)?$/, - action: { indentAction, appendText: "* " }, - }, - { - // Dedents after closing a multi-line comment - // e.g. */| - beforeText: /^(\ \ )*\ \*\/\s*$/, - action: { indentAction, removeText: 1 }, - }, - ], - }); - } - if (!this.typingContinueCommentsOnNewline && this.configureLang) { - this.configureLang.dispose(); - this.configureLang = undefined; + }, + { + // Begins a multi-line comment (standard or parent doc) + // e.g. /** ...| or /*! ...| + beforeText: /^\s*\/\*(\*|\!)(?!\/)([^\*]|\*(?!\/))*$/, + action: { indentAction, appendText: " * " }, + }, + { + // Continues a multi-line comment + // e.g. * ...| + beforeText: /^(\ \ )*\ \*(\ ([^\*]|\*(?!\/))*)?$/, + action: { indentAction, appendText: "* " }, + }, + { + // Dedents after closing a multi-line comment + // e.g. */| + beforeText: /^(\ \ )*\ \*\/\s*$/, + action: { indentAction, removeText: 1 }, + }, + ]; } + + this.configureLang = vscode.languages.setLanguageConfiguration("rust", { + onEnterRules, + }); } // We don't do runtime config validation here for simplicity. More on stackoverflow: -- cgit 1.4.1-3-g733a5 From 992bafa773255cf9858bdc25c302abbd07d89c7b Mon Sep 17 00:00:00 2001 From: DropDemBits Date: Tue, 17 Jan 2023 16:25:46 -0500 Subject: Fix change detection for relevant lang config opts --- editors/code/src/config.ts | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/config.ts b/editors/code/src/config.ts index 029dc3afd55..30ff9784829 100644 --- a/editors/code/src/config.ts +++ b/editors/code/src/config.ts @@ -11,7 +11,9 @@ export type RunnableEnvCfg = export class Config { readonly extensionId = "rust-lang.rust-analyzer"; - configureLang: vscode.Disposable | undefined; + configureLang: + | { handle: vscode.Disposable; typingContinueCommentsOnNewline: boolean } + | undefined; readonly rootSection = "rust-analyzer"; private readonly requiresReloadOpts = [ @@ -43,7 +45,7 @@ export class Config { } dispose() { - this.configureLang?.dispose(); + this.configureLang?.handle.dispose(); } private refreshLogging() { @@ -86,7 +88,15 @@ export class Config { * [1]: https://github.com/Microsoft/vscode/issues/11514#issuecomment-244707076 */ private configureLanguage() { - if (this.configureLang) return; + // Only need to dispose of the config if there's a change + if ( + this.configureLang && + this.typingContinueCommentsOnNewline !== + this.configureLang.typingContinueCommentsOnNewline + ) { + this.configureLang.handle.dispose(); + this.configureLang = undefined; + } let onEnterRules: vscode.OnEnterRule[] = [ { @@ -157,9 +167,12 @@ export class Config { ]; } - this.configureLang = vscode.languages.setLanguageConfiguration("rust", { - onEnterRules, - }); + this.configureLang = { + handle: vscode.languages.setLanguageConfiguration("rust", { + onEnterRules, + }), + typingContinueCommentsOnNewline: this.typingContinueCommentsOnNewline, + }; } // We don't do runtime config validation here for simplicity. More on stackoverflow: -- cgit 1.4.1-3-g733a5 From 7198cd04ac14a31fc9e401e6a4e411e401acc326 Mon Sep 17 00:00:00 2001 From: OmarTawfik <15987992+OmarTawfik@users.noreply.github.com> Date: Fri, 20 Jan 2023 13:22:11 -0800 Subject: allow using vscode variables in `config.serverPath` --- editors/code/src/bootstrap.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/bootstrap.ts b/editors/code/src/bootstrap.ts index 374c3b8144c..cabc7407172 100644 --- a/editors/code/src/bootstrap.ts +++ b/editors/code/src/bootstrap.ts @@ -1,6 +1,6 @@ import * as vscode from "vscode"; import * as os from "os"; -import { Config } from "./config"; +import { Config, substituteVSCodeVariables } from "./config"; import { log, isValidExecutable } from "./util"; import { PersistentState } from "./persistent_state"; import { exec } from "child_process"; @@ -132,7 +132,7 @@ async function getServer( return undefined; } function serverPath(config: Config): string | null { - return process.env.__RA_LSP_SERVER_DEBUG ?? config.serverPath; + return process.env.__RA_LSP_SERVER_DEBUG ?? substituteVSCodeVariables(config.serverPath); } async function isNixOs(): Promise { -- cgit 1.4.1-3-g733a5 From b9fe5afb30d00e14aaf5d02ff12a74efb774d4f1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 23 Jan 2023 13:10:25 +0100 Subject: Add a 'open server logs' button to the error notification --- crates/rust-analyzer/src/config.rs | 4 +++ crates/rust-analyzer/src/lsp_ext.rs | 7 +++++ crates/rust-analyzer/src/lsp_utils.rs | 50 +++++++++++++++++++++++++++-------- docs/dev/lsp-extensions.md | 2 +- editors/code/src/client.ts | 1 + editors/code/src/ctx.ts | 5 ++++ editors/code/src/lsp_ext.ts | 1 + 7 files changed, 58 insertions(+), 12 deletions(-) (limited to 'editors/code/src') diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index c4d9ad7dff5..8ea161dbdc4 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -989,6 +989,10 @@ impl Config { self.experimental("codeActionGroup") } + pub fn open_server_logs(&self) -> bool { + self.experimental("openServerLogs") + } + pub fn server_status_notification(&self) -> bool { self.experimental("serverStatusNotification") } diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index b117acd1b0f..08b2c837de3 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -151,6 +151,13 @@ impl Notification for ClearFlycheck { const METHOD: &'static str = "rust-analyzer/clearFlycheck"; } +pub enum OpenServerLogs {} + +impl Notification for OpenServerLogs { + type Params = (); + const METHOD: &'static str = "rust-analyzer/openServerLogs"; +} + #[derive(Deserialize, Serialize, Debug)] #[serde(rename_all = "camelCase")] pub struct RunFlycheckParams { diff --git a/crates/rust-analyzer/src/lsp_utils.rs b/crates/rust-analyzer/src/lsp_utils.rs index dcaee92857a..baa77a005e2 100644 --- a/crates/rust-analyzer/src/lsp_utils.rs +++ b/crates/rust-analyzer/src/lsp_utils.rs @@ -2,12 +2,13 @@ use std::{mem, ops::Range, sync::Arc}; use lsp_server::Notification; +use lsp_types::request::Request; use crate::{ from_proto, global_state::GlobalState, line_index::{LineEndings, LineIndex, PositionEncoding}, - LspError, + lsp_ext, LspError, }; pub(crate) fn invalid_params_error(message: String) -> LspError { @@ -46,20 +47,47 @@ impl GlobalState { /// If `additional_info` is [`Some`], appends a note to the notification telling to check the logs. /// This will always log `message` + `additional_info` to the server's error log. pub(crate) fn show_and_log_error(&mut self, message: String, additional_info: Option) { - let mut message = message; match additional_info { Some(additional_info) => { - tracing::error!("{}\n\n{}", &message, &additional_info); - if tracing::enabled!(tracing::Level::ERROR) { - message.push_str("\n\nCheck the server logs for additional info."); + tracing::error!("{}:\n{}", &message, &additional_info); + match self.config.open_server_logs() && tracing::enabled!(tracing::Level::ERROR) { + true => self.send_request::( + lsp_types::ShowMessageRequestParams { + typ: lsp_types::MessageType::ERROR, + message, + actions: Some(vec![lsp_types::MessageActionItem { + title: "Open server logs".to_owned(), + properties: Default::default(), + }]), + }, + |this, resp| { + let lsp_server::Response { error: None, result: Some(result), .. } = resp + else { return }; + if let Ok(Some(_item)) = crate::from_json::< + ::Result, + >( + lsp_types::request::ShowMessageRequest::METHOD, &result + ) { + this.send_notification::(()); + } + }, + ), + false => self.send_notification::( + lsp_types::ShowMessageParams { + typ: lsp_types::MessageType::ERROR, + message, + }, + ), } } - None => tracing::error!("{}", &message), - } + None => { + tracing::error!("{}", &message); - self.send_notification::( - lsp_types::ShowMessageParams { typ: lsp_types::MessageType::ERROR, message }, - ) + self.send_notification::( + lsp_types::ShowMessageParams { typ: lsp_types::MessageType::ERROR, message }, + ); + } + } } /// rust-analyzer is resilient -- if it fails, this doesn't usually affect @@ -77,7 +105,7 @@ impl GlobalState { let from_source_build = option_env!("POKE_RA_DEVS").is_some(); let profiling_enabled = std::env::var("RA_PROFILE").is_ok(); if from_source_build || profiling_enabled { - self.show_message(lsp_types::MessageType::ERROR, message) + self.show_and_log_error(message, None); } } diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index 0f24ddbbc00..a794e866181 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@ [^\n]+\n/m, ""); } - let value; - if (errorCode) { - if (typeof diag.code === "string" || typeof diag.code === "number") { - value = diag.code; - } else { - value = diag.code?.value; - } - } diag.code = { target: vscode.Uri.from({ scheme: diagnostics.URI_SCHEME, @@ -153,7 +206,8 @@ export async function createClient( fragment: uri.toString(), query: idx.toString(), }), - value: value ?? "Click for full compiler diagnostic", + value: + errorCode && value ? value : "Click for full compiler diagnostic", }; } }); diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index c2dca733df8..5515921ed14 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -82,6 +82,7 @@ export class Ctx { private state: PersistentState; private commandFactories: Record; private commandDisposables: Disposable[]; + private unlinkedFiles: vscode.Uri[]; get client() { return this._client; @@ -99,6 +100,7 @@ export class Ctx { this.clientSubscriptions = []; this.commandDisposables = []; this.commandFactories = commandFactories; + this.unlinkedFiles = []; this.state = new PersistentState(extCtx.globalState); this.config = new Config(extCtx); @@ -218,7 +220,8 @@ export class Ctx { this.outputChannel, initializationOptions, serverOptions, - this.config + this.config, + this.unlinkedFiles ); this.pushClientCleanup( this._client.onNotification(ra.serverStatus, (params) => -- cgit 1.4.1-3-g733a5 From 9745a25d57fbd9a73aec1306554ac87936a63e7d Mon Sep 17 00:00:00 2001 From: x2cf Date: Tue, 21 Mar 2023 14:56:07 +0800 Subject: Fix VS Code status message formatting error --- editors/code/src/ctx.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'editors/code/src') diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index c2dca733df8..85579453a63 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -383,7 +383,7 @@ export class Ctx { ); statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)"); statusBar.tooltip.appendMarkdown("\n\n[Restart server](command:rust-analyzer.startServer)"); - statusBar.tooltip.appendMarkdown("[Stop server](command:rust-analyzer.stopServer)"); + statusBar.tooltip.appendMarkdown("\n\n[Stop server](command:rust-analyzer.stopServer)"); if (!status.quiescent) icon = "$(sync~spin) "; statusBar.text = `${icon}rust-analyzer`; } -- cgit 1.4.1-3-g733a5 From 3622fb645632fcf81af82919259be4b0012a3939 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 20 Mar 2023 21:24:53 +0100 Subject: Fix lints --- editors/code/src/client.ts | 75 ++++++++++++++++++++++------------------------ editors/code/src/ctx.ts | 4 +-- 2 files changed, 37 insertions(+), 42 deletions(-) (limited to 'editors/code/src') diff --git a/editors/code/src/client.ts b/editors/code/src/client.ts index 2a1c757dfef..4ca6601a6aa 100644 --- a/editors/code/src/client.ts +++ b/editors/code/src/client.ts @@ -121,59 +121,54 @@ export async function createClient( const preview = config.previewRustcOutput; const errorCode = config.useRustcErrorCode; diagnosticList.forEach((diag, idx) => { - let value = + const value = typeof diag.code === "string" || typeof diag.code === "number" ? diag.code : diag.code?.value; if (value === "unlinked-file" && !unlinkedFiles.includes(uri)) { - let config = vscode.workspace.getConfiguration("rust-analyzer"); + const config = vscode.workspace.getConfiguration("rust-analyzer"); if (config.get("showUnlinkedFileNotification")) { unlinkedFiles.push(uri); - let folder = vscode.workspace.getWorkspaceFolder(uri)?.uri.fsPath; + const folder = vscode.workspace.getWorkspaceFolder(uri)?.uri.fsPath; if (folder) { - let parent_backslash = uri.fsPath.lastIndexOf( + const parentBackslash = uri.fsPath.lastIndexOf( pathSeparator + "src" ); - let parent = uri.fsPath.substring(0, parent_backslash); + const parent = uri.fsPath.substring(0, parentBackslash); if (parent.startsWith(folder)) { - let path = vscode.Uri.file( + const path = vscode.Uri.file( parent + pathSeparator + "Cargo.toml" ); - void vscode.workspace.fs.stat(path).then(() => { - vscode.window - .showInformationMessage( - `This rust file does not belong to a loaded cargo project. It looks like it might belong to the workspace at ${path}, do you want to add it to the linked Projects?`, - "Yes", - "No", - "Don't show this again" - ) - .then((choice) => { - switch (choice) { - case "Yes": - break; - case "No": - config.update( - "linkedProjects", - config - .get("linkedProjects") - ?.concat( - path.fsPath.substring( - folder!.length - ) - ), - false - ); - break; - case "Don't show this again": - config.update( - "showUnlinkedFileNotification", - false, - false - ); - break; - } - }); + void vscode.workspace.fs.stat(path).then(async () => { + const choice = await vscode.window.showInformationMessage( + `This rust file does not belong to a loaded cargo project. It looks like it might belong to the workspace at ${path}, do you want to add it to the linked Projects?`, + "Yes", + "No", + "Don't show this again" + ); + switch (choice) { + case "Yes": + break; + case "No": + await config.update( + "linkedProjects", + config + .get("linkedProjects") + ?.concat( + path.fsPath.substring(folder.length) + ), + false + ); + break; + case "Don't show this again": + await config.update( + "showUnlinkedFileNotification", + false, + false + ); + break; + } }); } } diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 5515921ed14..8a0f4ea4773 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -95,7 +95,6 @@ export class Ctx { ) { extCtx.subscriptions.push(this); this.statusBar = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left); - this.statusBar.show(); this.workspace = workspace; this.clientSubscriptions = []; this.commandDisposables = []; @@ -338,6 +337,7 @@ export class Ctx { setServerStatus(status: ServerStatusParams | { health: "stopped" }) { let icon = ""; const statusBar = this.statusBar; + statusBar.show(); statusBar.tooltip = new vscode.MarkdownString("", true); statusBar.tooltip.isTrusted = true; switch (status.health) { @@ -386,7 +386,7 @@ export class Ctx { ); statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)"); statusBar.tooltip.appendMarkdown("\n\n[Restart server](command:rust-analyzer.startServer)"); - statusBar.tooltip.appendMarkdown("[Stop server](command:rust-analyzer.stopServer)"); + statusBar.tooltip.appendMarkdown("\n\n[Stop server](command:rust-analyzer.stopServer)"); if (!status.quiescent) icon = "$(sync~spin) "; statusBar.text = `${icon}rust-analyzer`; } -- cgit 1.4.1-3-g733a5 From e9fb2ffe4572471588b6bd602521a1421baf4dc3 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 25 Mar 2023 16:47:41 +0100 Subject: Add lsp command for rebuilding proc macros --- crates/rust-analyzer/src/handlers.rs | 8 ++++++++ crates/rust-analyzer/src/lsp_ext.rs | 8 ++++++++ crates/rust-analyzer/src/main_loop.rs | 1 + editors/code/src/commands.ts | 4 ++++ editors/code/src/ctx.ts | 5 ++++- editors/code/src/lsp_ext.ts | 1 + editors/code/src/main.ts | 1 + 7 files changed, 27 insertions(+), 1 deletion(-) (limited to 'editors/code/src') diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 2fca2ab851d..a56c245dca5 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -52,6 +52,14 @@ pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result< Ok(()) } +pub(crate) fn handle_proc_macros_reload(state: &mut GlobalState, _: ()) -> Result<()> { + state.proc_macro_clients.clear(); + state.proc_macro_changed = false; + + state.fetch_build_data_queue.request_op("reload proc macros request".to_string()); + Ok(()) +} + pub(crate) fn handle_cancel_flycheck(state: &mut GlobalState, _: ()) -> Result<()> { let _p = profile::span("handle_stop_flycheck"); state.flycheck.iter().for_each(|flycheck| flycheck.cancel()); diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index c7b513db981..2f8829ec738 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -51,6 +51,14 @@ impl Request for ReloadWorkspace { const METHOD: &'static str = "rust-analyzer/reloadWorkspace"; } +pub enum ReloadProcMacros {} + +impl Request for ReloadProcMacros { + type Params = (); + type Result = (); + const METHOD: &'static str = "rust-analyzer/reloadProcMacros"; +} + pub enum SyntaxTree {} impl Request for SyntaxTree { diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 67a54cde68c..ae7457e3473 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -633,6 +633,7 @@ impl GlobalState { dispatcher .on_sync_mut::(handlers::handle_workspace_reload) + .on_sync_mut::(handlers::handle_proc_macros_reload) .on_sync_mut::(handlers::handle_memory_usage) .on_sync_mut::(handlers::handle_shuffle_crate_graph) .on_sync::(handlers::handle_join_lines) diff --git a/editors/code/src/commands.ts b/editors/code/src/commands.ts index 8a953577e99..8ce3466ed44 100644 --- a/editors/code/src/commands.ts +++ b/editors/code/src/commands.ts @@ -749,6 +749,10 @@ export function reloadWorkspace(ctx: CtxInit): Cmd { return async () => ctx.client.sendRequest(ra.reloadWorkspace); } +export function reloadProcMacros(ctx: CtxInit): Cmd { + return async () => ctx.client.sendRequest(ra.reloadProcMacros); +} + export function addProject(ctx: CtxInit): Cmd { return async () => { const discoverProjectCommand = ctx.config.discoverProjectCommand; diff --git a/editors/code/src/ctx.ts b/editors/code/src/ctx.ts index 85579453a63..8da8b0d63a1 100644 --- a/editors/code/src/ctx.ts +++ b/editors/code/src/ctx.ts @@ -378,10 +378,13 @@ export class Ctx { if (statusBar.tooltip.value) { statusBar.tooltip.appendText("\n\n"); } + statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)"); statusBar.tooltip.appendMarkdown( "\n\n[Reload Workspace](command:rust-analyzer.reloadWorkspace)" ); - statusBar.tooltip.appendMarkdown("\n\n[Open logs](command:rust-analyzer.openLogs)"); + statusBar.tooltip.appendMarkdown( + "\n\n[Rebuild Proc Macros](command:rust-analyzer.reloadProcMacros)" + ); statusBar.tooltip.appendMarkdown("\n\n[Restart server](command:rust-analyzer.startServer)"); statusBar.tooltip.appendMarkdown("\n\n[Stop server](command:rust-analyzer.stopServer)"); if (!status.quiescent) icon = "$(sync~spin) "; diff --git a/editors/code/src/lsp_ext.ts b/editors/code/src/lsp_ext.ts index 872d7199b83..a03777d1c0a 100644 --- a/editors/code/src/lsp_ext.ts +++ b/editors/code/src/lsp_ext.ts @@ -43,6 +43,7 @@ export const relatedTests = new lc.RequestType("rust-analyzer/reloadWorkspace"); +export const reloadProcMacros = new lc.RequestType0("rust-analyzer/reloadProcMacros"); export const runFlycheck = new lc.NotificationType<{ textDocument: lc.TextDocumentIdentifier | null; diff --git a/editors/code/src/main.ts b/editors/code/src/main.ts index d5de00561b1..7079f235cab 100644 --- a/editors/code/src/main.ts +++ b/editors/code/src/main.ts @@ -153,6 +153,7 @@ function createCommands(): Record { memoryUsage: { enabled: commands.memoryUsage }, shuffleCrateGraph: { enabled: commands.shuffleCrateGraph }, reloadWorkspace: { enabled: commands.reloadWorkspace }, + reloadProcMacros: { enabled: commands.reloadProcMacros }, addProject: { enabled: commands.addProject }, matchingBrace: { enabled: commands.matchingBrace }, joinLines: { enabled: commands.joinLines }, -- cgit 1.4.1-3-g733a5 From ee02213e65b1fd9c5e331ffe4a1ebd1e2ebab9c8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 26 Mar 2023 08:39:28 +0200 Subject: Handle proc macro fetching via OpQueue --- crates/base-db/src/input.rs | 4 +- .../src/handlers/unresolved_proc_macro.rs | 24 ++++---- crates/project-model/src/workspace.rs | 16 +++--- crates/rust-analyzer/src/cli/load_cargo.rs | 5 +- crates/rust-analyzer/src/config.rs | 4 ++ crates/rust-analyzer/src/global_state.rs | 20 ++++--- crates/rust-analyzer/src/handlers.rs | 7 +-- crates/rust-analyzer/src/lsp_ext.rs | 6 +- crates/rust-analyzer/src/main_loop.rs | 29 ++++++---- crates/rust-analyzer/src/op_queue.rs | 14 ++--- crates/rust-analyzer/src/reload.rs | 64 +++++++++++++--------- docs/dev/lsp-extensions.md | 6 +- editors/code/package.json | 4 +- editors/code/src/commands.ts | 4 +- editors/code/src/ctx.ts | 2 +- editors/code/src/lsp_ext.ts | 2 +- editors/code/src/main.ts | 2 +- 17 files changed, 121 insertions(+), 92 deletions(-) (limited to 'editors/code/src') diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs index 9580b76faa2..c43941d6ac1 100644 --- a/crates/base-db/src/input.rs +++ b/crates/base-db/src/input.rs @@ -15,7 +15,9 @@ use syntax::SmolStr; use tt::token_id::Subtree; use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath}; -pub type ProcMacroPaths = FxHashMap, AbsPathBuf), String>>; +// Map from crate id to the name of the crate and path of the proc-macro. If the value is `None`, +// then the crate for the proc-macro hasn't been build yet as the build data is missing. +pub type ProcMacroPaths = FxHashMap, AbsPathBuf)>>; pub type ProcMacros = FxHashMap; /// Files are grouped into source roots. A source root is a directory on the diff --git a/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs b/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs index 9a984ba6bf0..ae5cf135839 100644 --- a/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs +++ b/crates/ide-diagnostics/src/handlers/unresolved_proc_macro.rs @@ -25,25 +25,21 @@ pub(crate) fn unresolved_proc_macro( _ => proc_macros_enabled, }; - let message = match &d.macro_name { + let not_expanded_message = match &d.macro_name { Some(name) => format!("proc macro `{name}` not expanded"), None => "proc macro not expanded".to_string(), }; let severity = if config_enabled { Severity::Error } else { Severity::WeakWarning }; let def_map = ctx.sema.db.crate_def_map(d.krate); - let message = format!( - "{message}: {}", - if config_enabled { - def_map.proc_macro_loading_error().unwrap_or("proc macro not found in the built dylib") - } else { - match d.kind { - hir::MacroKind::Attr if proc_macros_enabled => { - "attribute macro expansion is disabled" - } - _ => "proc-macro expansion is disabled", - } - }, - ); + let message = if config_enabled { + def_map.proc_macro_loading_error().unwrap_or("proc macro not found in the built dylib") + } else { + match d.kind { + hir::MacroKind::Attr if proc_macros_enabled => "attribute macro expansion is disabled", + _ => "proc-macro expansion is disabled", + } + }; + let message = format!("{not_expanded_message}: {message}"); Diagnostic::new("unresolved-proc-macro", message, display_range).severity(severity) } diff --git a/crates/project-model/src/workspace.rs b/crates/project-model/src/workspace.rs index 1fd7c681938..fa966f70aa4 100644 --- a/crates/project-model/src/workspace.rs +++ b/crates/project-model/src/workspace.rs @@ -707,7 +707,7 @@ fn project_json_to_crate_graph( if let Some(path) = krate.proc_macro_dylib_path.clone() { proc_macros.insert( crate_id, - Ok(( + Some(( krate.display_name.as_ref().map(|it| it.canonical_name().to_owned()), path, )), @@ -1185,12 +1185,14 @@ fn add_target_crate_root( CrateOrigin::CratesIo { repo: pkg.repository.clone(), name: Some(pkg.name.clone()) }, target_layout, ); - let proc_macro = match build_data.as_ref().map(|it| &it.proc_macro_dylib_path) { - Some(it) => it.clone().map(Ok), - None => Some(Err("crate has not (yet) been built".into())), - }; - if let Some(proc_macro) = proc_macro { - proc_macros.insert(crate_id, proc_macro.map(|path| (Some(cargo_name.to_owned()), path))); + if is_proc_macro { + let proc_macro = match build_data.as_ref().map(|it| it.proc_macro_dylib_path.as_ref()) { + Some(it) => it.cloned().map(|path| Some((Some(cargo_name.to_owned()), path))), + None => Some(None), + }; + if let Some(proc_macro) = proc_macro { + proc_macros.insert(crate_id, proc_macro); + } } crate_id diff --git a/crates/rust-analyzer/src/cli/load_cargo.rs b/crates/rust-analyzer/src/cli/load_cargo.rs index f5bc3c12c14..268f59e7e4b 100644 --- a/crates/rust-analyzer/src/cli/load_cargo.rs +++ b/crates/rust-analyzer/src/cli/load_cargo.rs @@ -101,7 +101,10 @@ pub fn load_workspace( .map(|(crate_id, path)| { ( crate_id, - path.and_then(|(_, path)| load_proc_macro(proc_macro_server, &path, &[])), + path.map_or_else( + || Err("proc macro crate is missing dylib".to_owned()), + |(_, path)| load_proc_macro(proc_macro_server, &path, &[]), + ), ) }) .collect() diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index c35cce103fa..e917f107f3b 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -1106,6 +1106,10 @@ impl Config { &self.data.procMacro_ignored } + pub fn expand_proc_macros(&self) -> bool { + self.data.procMacro_enable + } + pub fn expand_proc_attr_macros(&self) -> bool { self.data.procMacro_enable && self.data.procMacro_attributes_enable } diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs index d02714ad1ea..e0c143310d8 100644 --- a/crates/rust-analyzer/src/global_state.rs +++ b/crates/rust-analyzer/src/global_state.rs @@ -8,7 +8,7 @@ use std::{sync::Arc, time::Instant}; use crossbeam_channel::{unbounded, Receiver, Sender}; use flycheck::FlycheckHandle; use ide::{Analysis, AnalysisHost, Cancellable, Change, FileId}; -use ide_db::base_db::{CrateId, FileLoader, SourceDatabase}; +use ide_db::base_db::{CrateId, FileLoader, ProcMacroPaths, SourceDatabase}; use lsp_types::{SemanticTokens, Url}; use parking_lot::{Mutex, RwLock}; use proc_macro_api::ProcMacroServer; @@ -101,11 +101,12 @@ pub(crate) struct GlobalState { /// the user just adds comments or whitespace to Cargo.toml, we do not want /// to invalidate any salsa caches. pub(crate) workspaces: Arc>, - pub(crate) fetch_workspaces_queue: OpQueue>>>, + pub(crate) fetch_workspaces_queue: OpQueue<(), Option>>>, pub(crate) fetch_build_data_queue: - OpQueue<(Arc>, Vec>)>, + OpQueue<(), (Arc>, Vec>)>, + pub(crate) fetch_proc_macros_queue: OpQueue, bool>, - pub(crate) prime_caches_queue: OpQueue<()>, + pub(crate) prime_caches_queue: OpQueue, } /// An immutable snapshot of the world's state at a point in time. @@ -117,6 +118,7 @@ pub(crate) struct GlobalStateSnapshot { pub(crate) semantic_tokens_cache: Arc>>, vfs: Arc)>>, pub(crate) workspaces: Arc>, + // used to signal semantic highlighting to fall back to syntax based highlighting until proc-macros have been loaded pub(crate) proc_macros_loaded: bool, pub(crate) flycheck: Arc<[FlycheckHandle]>, } @@ -170,9 +172,10 @@ impl GlobalState { workspaces: Arc::new(Vec::new()), fetch_workspaces_queue: OpQueue::default(), - prime_caches_queue: OpQueue::default(), - fetch_build_data_queue: OpQueue::default(), + fetch_proc_macros_queue: OpQueue::default(), + + prime_caches_queue: OpQueue::default(), }; // Apply any required database inputs from the config. this.update_configuration(config); @@ -286,7 +289,7 @@ impl GlobalState { // crate see https://github.com/rust-lang/rust-analyzer/issues/13029 if let Some(path) = workspace_structure_change { self.fetch_workspaces_queue - .request_op(format!("workspace vfs file change: {}", path.display())); + .request_op(format!("workspace vfs file change: {}", path.display()), ()); } self.proc_macro_changed = changed_files.iter().filter(|file| !file.is_created_or_deleted()).any(|file| { @@ -309,7 +312,8 @@ impl GlobalState { check_fixes: Arc::clone(&self.diagnostics.check_fixes), mem_docs: self.mem_docs.clone(), semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache), - proc_macros_loaded: !self.fetch_build_data_queue.last_op_result().0.is_empty(), + proc_macros_loaded: !self.config.expand_proc_macros() + || *self.fetch_proc_macros_queue.last_op_result(), flycheck: self.flycheck.clone(), } } diff --git a/crates/rust-analyzer/src/handlers.rs b/crates/rust-analyzer/src/handlers.rs index 8866515bb94..2dae2c82b93 100644 --- a/crates/rust-analyzer/src/handlers.rs +++ b/crates/rust-analyzer/src/handlers.rs @@ -48,16 +48,15 @@ pub(crate) fn handle_workspace_reload(state: &mut GlobalState, _: ()) -> Result< state.proc_macro_clients = Arc::new([]); state.proc_macro_changed = false; - state.fetch_workspaces_queue.request_op("reload workspace request".to_string()); - state.fetch_build_data_queue.request_op("reload workspace request".to_string()); + state.fetch_workspaces_queue.request_op("reload workspace request".to_string(), ()); Ok(()) } -pub(crate) fn handle_proc_macros_reload(state: &mut GlobalState, _: ()) -> Result<()> { +pub(crate) fn handle_proc_macros_rebuild(state: &mut GlobalState, _: ()) -> Result<()> { state.proc_macro_clients = Arc::new([]); state.proc_macro_changed = false; - state.fetch_build_data_queue.request_op("reload proc macros request".to_string()); + state.fetch_build_data_queue.request_op("rebuild proc macros request".to_string(), ()); Ok(()) } diff --git a/crates/rust-analyzer/src/lsp_ext.rs b/crates/rust-analyzer/src/lsp_ext.rs index 2f8829ec738..2adefcc5e83 100644 --- a/crates/rust-analyzer/src/lsp_ext.rs +++ b/crates/rust-analyzer/src/lsp_ext.rs @@ -51,12 +51,12 @@ impl Request for ReloadWorkspace { const METHOD: &'static str = "rust-analyzer/reloadWorkspace"; } -pub enum ReloadProcMacros {} +pub enum RebuildProcMacros {} -impl Request for ReloadProcMacros { +impl Request for RebuildProcMacros { type Params = (); type Result = (); - const METHOD: &'static str = "rust-analyzer/reloadProcMacros"; + const METHOD: &'static str = "rust-analyzer/rebuildProcMacros"; } pub enum SyntaxTree {} diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs index 8db526e0b76..f9930fb2599 100644 --- a/crates/rust-analyzer/src/main_loop.rs +++ b/crates/rust-analyzer/src/main_loop.rs @@ -149,8 +149,8 @@ impl GlobalState { ); } - self.fetch_workspaces_queue.request_op("startup".to_string()); - if let Some(cause) = self.fetch_workspaces_queue.should_start_op() { + self.fetch_workspaces_queue.request_op("startup".to_string(), ()); + if let Some((cause, ())) = self.fetch_workspaces_queue.should_start_op() { self.fetch_workspaces(cause); } @@ -248,7 +248,7 @@ impl GlobalState { self.prime_caches_queue.op_completed(()); if cancelled { self.prime_caches_queue - .request_op("restart after cancellation".to_string()); + .request_op("restart after cancellation".to_string(), ()); } } }; @@ -280,7 +280,8 @@ impl GlobalState { if self.is_quiescent() { let became_quiescent = !(was_quiescent || self.fetch_workspaces_queue.op_requested() - || self.fetch_build_data_queue.op_requested()); + || self.fetch_build_data_queue.op_requested() + || self.fetch_proc_macros_queue.op_requested()); if became_quiescent { if self.config.check_on_save() { @@ -288,7 +289,7 @@ impl GlobalState { self.flycheck.iter().for_each(FlycheckHandle::restart); } if self.config.prefill_caches() { - self.prime_caches_queue.request_op("became quiescent".to_string()); + self.prime_caches_queue.request_op("became quiescent".to_string(), ()); } } @@ -358,18 +359,20 @@ impl GlobalState { } if self.config.cargo_autoreload() { - if let Some(cause) = self.fetch_workspaces_queue.should_start_op() { + if let Some((cause, ())) = self.fetch_workspaces_queue.should_start_op() { self.fetch_workspaces(cause); } } if !self.fetch_workspaces_queue.op_in_progress() { - if let Some(cause) = self.fetch_build_data_queue.should_start_op() { + if let Some((cause, ())) = self.fetch_build_data_queue.should_start_op() { self.fetch_build_data(cause); + } else if let Some((cause, paths)) = self.fetch_proc_macros_queue.should_start_op() { + self.fetch_proc_macros(cause, paths); } } - if let Some(cause) = self.prime_caches_queue.should_start_op() { + if let Some((cause, ())) = self.prime_caches_queue.should_start_op() { tracing::debug!(%cause, "will prime caches"); let num_worker_threads = self.config.prime_caches_num_threads(); @@ -463,7 +466,8 @@ impl GlobalState { let workspaces_updated = !Arc::ptr_eq(&old, &self.workspaces); if self.config.run_build_scripts() && workspaces_updated { - self.fetch_build_data_queue.request_op(format!("workspace updated")); + self.fetch_build_data_queue + .request_op(format!("workspace updated"), ()); } (Progress::End, None) @@ -497,6 +501,7 @@ impl GlobalState { ProcMacroProgress::Begin => (Some(Progress::Begin), None), ProcMacroProgress::Report(msg) => (Some(Progress::Report), Some(msg)), ProcMacroProgress::End(proc_macro_load_result) => { + self.fetch_proc_macros_queue.op_completed(true); self.set_proc_macros(proc_macro_load_result); (Some(Progress::End), None) @@ -649,7 +654,7 @@ impl GlobalState { dispatcher .on_sync_mut::(handlers::handle_workspace_reload) - .on_sync_mut::(handlers::handle_proc_macros_reload) + .on_sync_mut::(handlers::handle_proc_macros_rebuild) .on_sync_mut::(handlers::handle_memory_usage) .on_sync_mut::(handlers::handle_shuffle_crate_graph) .on_sync::(handlers::handle_join_lines) @@ -904,7 +909,7 @@ impl GlobalState { if let Some(abs_path) = vfs_path.as_path() { if reload::should_refresh_for_change(abs_path, ChangeKind::Modify) { this.fetch_workspaces_queue - .request_op(format!("DidSaveTextDocument {}", abs_path.display())); + .request_op(format!("DidSaveTextDocument {}", abs_path.display()), ()); } } @@ -980,7 +985,7 @@ impl GlobalState { config.workspace_roots.extend(added); if !config.has_linked_projects() && config.detached_files().is_empty() { config.rediscover_workspaces(); - this.fetch_workspaces_queue.request_op("client workspaces changed".to_string()) + this.fetch_workspaces_queue.request_op("client workspaces changed".to_string(), ()) } Ok(()) diff --git a/crates/rust-analyzer/src/op_queue.rs b/crates/rust-analyzer/src/op_queue.rs index 97aca016187..932730fc234 100644 --- a/crates/rust-analyzer/src/op_queue.rs +++ b/crates/rust-analyzer/src/op_queue.rs @@ -3,23 +3,23 @@ pub(crate) type Cause = String; -pub(crate) struct OpQueue { - op_requested: Option, +pub(crate) struct OpQueue { + op_requested: Option<(Cause, Args)>, op_in_progress: bool, last_op_result: Output, } -impl Default for OpQueue { +impl Default for OpQueue { fn default() -> Self { Self { op_requested: None, op_in_progress: false, last_op_result: Default::default() } } } -impl OpQueue { - pub(crate) fn request_op(&mut self, reason: Cause) { - self.op_requested = Some(reason); +impl OpQueue { + pub(crate) fn request_op(&mut self, reason: Cause, args: Args) { + self.op_requested = Some((reason, args)); } - pub(crate) fn should_start_op(&mut self) -> Option { + pub(crate) fn should_start_op(&mut self) -> Option<(Cause, Args)> { if self.op_in_progress { return None; } diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 9c6edb46f41..987eb8aad6d 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -66,6 +66,7 @@ impl GlobalState { !(self.last_reported_status.is_none() || self.fetch_workspaces_queue.op_in_progress() || self.fetch_build_data_queue.op_in_progress() + || self.fetch_proc_macros_queue.op_in_progress() || self.vfs_progress_config_version < self.vfs_config_version || self.vfs_progress_n_done < self.vfs_progress_n_total) } @@ -77,7 +78,7 @@ impl GlobalState { self.analysis_host.update_lru_capacity(self.config.lru_capacity()); } if self.config.linked_projects() != old_config.linked_projects() { - self.fetch_workspaces_queue.request_op("linked projects changed".to_string()) + self.fetch_workspaces_queue.request_op("linked projects changed".to_string(), ()) } else if self.config.flycheck() != old_config.flycheck() { self.reload_flycheck(); } @@ -101,7 +102,7 @@ impl GlobalState { if self.proc_macro_changed { status.health = lsp_ext::Health::Warning; - message.push_str("Reload required due to source changes of a procedural macro.\n\n"); + message.push_str("Proc-macros have changed and need to be rebuild.\n\n"); } if let Err(_) = self.fetch_build_data_error() { status.health = lsp_ext::Health::Warning; @@ -223,8 +224,8 @@ impl GlobalState { }); } - pub(crate) fn load_proc_macros(&mut self, paths: Vec) { - tracing::info!("will load proc macros"); + pub(crate) fn fetch_proc_macros(&mut self, cause: Cause, paths: Vec) { + tracing::info!(%cause, "will load proc macros"); let dummy_replacements = self.config.dummy_replacements().clone(); let proc_macro_clients = self.proc_macro_clients.clone(); @@ -240,28 +241,30 @@ impl GlobalState { }; let mut res = FxHashMap::default(); - for (client, paths) in proc_macro_clients + let chain = proc_macro_clients .iter() .map(|res| res.as_ref().map_err(|e| &**e)) - .chain(iter::repeat_with(|| Err("Proc macros are disabled"))) - .zip(paths) - { + .chain(iter::repeat_with(|| Err("Proc macros servers are not running"))); + for (client, paths) in chain.zip(paths) { res.extend(paths.into_iter().map(move |(crate_id, res)| { ( crate_id, - res.and_then(|(crate_name, path)| { - progress(path.display().to_string()); - load_proc_macro( - client, - &path, - crate_name - .as_deref() - .and_then(|crate_name| { - dummy_replacements.get(crate_name).map(|v| &**v) - }) - .unwrap_or_default(), - ) - }), + res.map_or_else( + || Err("proc macro crate is missing dylib".to_owned()), + |(crate_name, path)| { + progress(path.display().to_string()); + load_proc_macro( + client, + &path, + crate_name + .as_deref() + .and_then(|crate_name| { + dummy_replacements.get(crate_name).map(|v| &**v) + }) + .unwrap_or_default(), + ) + }, + ), ) })); } @@ -443,14 +446,25 @@ impl GlobalState { (crate_graph, proc_macros) }; let mut change = Change::new(); + + if same_workspaces { + if self.config.expand_proc_macros() { + self.fetch_proc_macros_queue.request_op(cause, proc_macro_paths); + } + } else { + // Set up errors for proc-macros upfront that we haven't run build scripts yet + let mut proc_macros = FxHashMap::default(); + for paths in proc_macro_paths { + proc_macros.extend(paths.into_iter().map(move |(crate_id, _)| { + (crate_id, Err("crate has not yet been build".to_owned())) + })); + } + change.set_proc_macros(proc_macros); + } change.set_crate_graph(crate_graph); self.analysis_host.apply_change(change); self.process_changes(); - if same_workspaces && !self.fetch_workspaces_queue.op_requested() { - self.load_proc_macros(proc_macro_paths); - } - self.reload_flycheck(); tracing::info!("did switch workspaces"); diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md index c157bab590e..bb5c6589489 100644 --- a/docs/dev/lsp-extensions.md +++ b/docs/dev/lsp-extensions.md @@ -1,5 +1,5 @@