diff --git a/crates/ra_ide/src/snapshots/highlight_doctest.html b/crates/ra_ide/src/snapshots/highlight_doctest.html
index 5228df2674..13a5d1b12a 100644
--- a/crates/ra_ide/src/snapshots/highlight_doctest.html
+++ b/crates/ra_ide/src/snapshots/highlight_doctest.html
@@ -73,9 +73,13 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
-
+
let foobar = Foo::new().bar();
+
+
+
+
pub fn foo(&self) -> bool {
true
}
diff --git a/crates/ra_ide/src/syntax_highlighting/injection.rs b/crates/ra_ide/src/syntax_highlighting/injection.rs
index a02ffe59ee..929a5cc5c0 100644
--- a/crates/ra_ide/src/syntax_highlighting/injection.rs
+++ b/crates/ra_ide/src/syntax_highlighting/injection.rs
@@ -53,6 +53,10 @@ pub(super) fn highlight_injection(
/// Mapping from extracted documentation code to original code
type RangesMap = BTreeMap;
+const RUSTDOC_FENCE: &'static str = "```";
+const RUSTDOC_FENCE_TOKENS: &[&'static str] =
+ &["", "rust", "should_panic", "ignore", "no_run", "compile_fail", "edition2015", "edition2018"];
+
/// Extracts Rust code from documentation comments as well as a mapping from
/// the extracted source code back to the original source ranges.
/// Lastly, a vector of new comment highlight ranges (spanning only the
@@ -67,6 +71,7 @@ pub(super) fn extract_doc_comments(
// Mapping from extracted documentation code to original code
let mut range_mapping: RangesMap = BTreeMap::new();
let mut line_start = TextSize::try_from(prefix.len()).unwrap();
+ let mut is_codeblock = false;
let mut is_doctest = false;
// Replace the original, line-spanning comment ranges by new, only comment-prefix
// spanning comment ranges.
@@ -76,8 +81,13 @@ pub(super) fn extract_doc_comments(
.filter_map(|el| el.into_token().and_then(ast::Comment::cast))
.filter(|comment| comment.kind().doc.is_some())
.filter(|comment| {
- if comment.text().contains("```") {
- is_doctest = !is_doctest;
+ if let Some(idx) = comment.text().find(RUSTDOC_FENCE) {
+ is_codeblock = !is_codeblock;
+ // Check whether code is rust by inspecting fence guards
+ let guards = &comment.text()[idx + RUSTDOC_FENCE.len()..];
+ let is_rust =
+ guards.split(',').all(|sub| RUSTDOC_FENCE_TOKENS.contains(&sub.trim()));
+ is_doctest = is_codeblock && is_rust;
false
} else {
is_doctest
diff --git a/crates/ra_ide/src/syntax_highlighting/tests.rs b/crates/ra_ide/src/syntax_highlighting/tests.rs
index 062b3ff4ae..ebf5b50ac0 100644
--- a/crates/ra_ide/src/syntax_highlighting/tests.rs
+++ b/crates/ra_ide/src/syntax_highlighting/tests.rs
@@ -329,9 +329,13 @@ impl Foo {
///
/// ```
///
- /// ```
+ /// ```rust,no_run
/// let foobar = Foo::new().bar();
/// ```
+ ///
+ /// ```sh
+ /// echo 1
+ /// ```
pub fn foo(&self) -> bool {
true
}
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 0e7a937a07..0df7427cb9 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -9,6 +9,7 @@
use std::{ffi::OsString, path::PathBuf};
+use crate::diagnostics::DiagnosticsConfig;
use lsp_types::ClientCapabilities;
use ra_flycheck::FlycheckConfig;
use ra_ide::{AssistConfig, CompletionConfig, HoverConfig, InlayHintsConfig};
@@ -20,6 +21,7 @@ pub struct Config {
pub client_caps: ClientCapsConfig,
pub publish_diagnostics: bool,
+ pub diagnostics: DiagnosticsConfig,
pub lru_capacity: Option,
pub proc_macro_srv: Option<(PathBuf, Vec)>,
pub files: FilesConfig,
@@ -136,6 +138,7 @@ impl Default for Config {
with_sysroot: true,
publish_diagnostics: true,
+ diagnostics: DiagnosticsConfig::default(),
lru_capacity: None,
proc_macro_srv: None,
files: FilesConfig { watcher: FilesWatcher::Notify, exclude: Vec::new() },
@@ -184,6 +187,8 @@ impl Config {
set(value, "/withSysroot", &mut self.with_sysroot);
set(value, "/diagnostics/enable", &mut self.publish_diagnostics);
+ set(value, "/diagnostics/warningsAsInfo", &mut self.diagnostics.warnings_as_info);
+ set(value, "/diagnostics/warningsAsHint", &mut self.diagnostics.warnings_as_hint);
set(value, "/lruCapacity", &mut self.lru_capacity);
self.files.watcher = match get(value, "/files/watcher") {
Some("client") => FilesWatcher::Client,
diff --git a/crates/rust-analyzer/src/diagnostics.rs b/crates/rust-analyzer/src/diagnostics.rs
index 25856c5436..290609e7f9 100644
--- a/crates/rust-analyzer/src/diagnostics.rs
+++ b/crates/rust-analyzer/src/diagnostics.rs
@@ -10,6 +10,12 @@ use crate::lsp_ext;
pub type CheckFixes = Arc>>;
+#[derive(Debug, Default, Clone)]
+pub struct DiagnosticsConfig {
+ pub warnings_as_info: Vec,
+ pub warnings_as_hint: Vec,
+}
+
#[derive(Debug, Default, Clone)]
pub struct DiagnosticCollection {
pub native: HashMap>,
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_hint.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_hint.snap
new file mode 100644
index 0000000000..f0273315e9
--- /dev/null
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_hint.snap
@@ -0,0 +1,86 @@
+---
+source: crates/rust-analyzer/src/diagnostics/to_proto.rs
+expression: diag
+---
+[
+ MappedRustDiagnostic {
+ location: Location {
+ uri: "file:///test/driver/subcommand/repl.rs",
+ range: Range {
+ start: Position {
+ line: 290,
+ character: 8,
+ },
+ end: Position {
+ line: 290,
+ character: 11,
+ },
+ },
+ },
+ diagnostic: Diagnostic {
+ range: Range {
+ start: Position {
+ line: 290,
+ character: 8,
+ },
+ end: Position {
+ line: 290,
+ character: 11,
+ },
+ },
+ severity: Some(
+ Hint,
+ ),
+ code: Some(
+ String(
+ "unused_variables",
+ ),
+ ),
+ source: Some(
+ "rustc",
+ ),
+ message: "unused variable: `foo`\n#[warn(unused_variables)] on by default",
+ related_information: None,
+ tags: Some(
+ [
+ Unnecessary,
+ ],
+ ),
+ },
+ fixes: [
+ CodeAction {
+ title: "consider prefixing with an underscore",
+ id: None,
+ group: None,
+ kind: Some(
+ "quickfix",
+ ),
+ command: None,
+ edit: Some(
+ SnippetWorkspaceEdit {
+ changes: Some(
+ {
+ "file:///test/driver/subcommand/repl.rs": [
+ TextEdit {
+ range: Range {
+ start: Position {
+ line: 290,
+ character: 8,
+ },
+ end: Position {
+ line: 290,
+ character: 11,
+ },
+ },
+ new_text: "_foo",
+ },
+ ],
+ },
+ ),
+ document_changes: None,
+ },
+ ),
+ },
+ ],
+ },
+]
diff --git a/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_info.snap b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_info.snap
new file mode 100644
index 0000000000..85fd050fd7
--- /dev/null
+++ b/crates/rust-analyzer/src/diagnostics/snapshots/rust_analyzer__diagnostics__to_proto__tests__snap_rustc_unused_variable_as_info.snap
@@ -0,0 +1,86 @@
+---
+source: crates/rust-analyzer/src/diagnostics/to_proto.rs
+expression: diag
+---
+[
+ MappedRustDiagnostic {
+ location: Location {
+ uri: "file:///test/driver/subcommand/repl.rs",
+ range: Range {
+ start: Position {
+ line: 290,
+ character: 8,
+ },
+ end: Position {
+ line: 290,
+ character: 11,
+ },
+ },
+ },
+ diagnostic: Diagnostic {
+ range: Range {
+ start: Position {
+ line: 290,
+ character: 8,
+ },
+ end: Position {
+ line: 290,
+ character: 11,
+ },
+ },
+ severity: Some(
+ Information,
+ ),
+ code: Some(
+ String(
+ "unused_variables",
+ ),
+ ),
+ source: Some(
+ "rustc",
+ ),
+ message: "unused variable: `foo`\n#[warn(unused_variables)] on by default",
+ related_information: None,
+ tags: Some(
+ [
+ Unnecessary,
+ ],
+ ),
+ },
+ fixes: [
+ CodeAction {
+ title: "consider prefixing with an underscore",
+ id: None,
+ group: None,
+ kind: Some(
+ "quickfix",
+ ),
+ command: None,
+ edit: Some(
+ SnippetWorkspaceEdit {
+ changes: Some(
+ {
+ "file:///test/driver/subcommand/repl.rs": [
+ TextEdit {
+ range: Range {
+ start: Position {
+ line: 290,
+ character: 8,
+ },
+ end: Position {
+ line: 290,
+ character: 11,
+ },
+ },
+ new_text: "_foo",
+ },
+ ],
+ },
+ ),
+ document_changes: None,
+ },
+ ),
+ },
+ ],
+ },
+]
diff --git a/crates/rust-analyzer/src/diagnostics/to_proto.rs b/crates/rust-analyzer/src/diagnostics/to_proto.rs
index 24ff9b280a..ba74f15f3c 100644
--- a/crates/rust-analyzer/src/diagnostics/to_proto.rs
+++ b/crates/rust-analyzer/src/diagnostics/to_proto.rs
@@ -9,14 +9,24 @@ use lsp_types::{
use ra_flycheck::{Applicability, DiagnosticLevel, DiagnosticSpan, DiagnosticSpanMacroExpansion};
use stdx::format_to;
+use super::DiagnosticsConfig;
use crate::{lsp_ext, to_proto::url_from_abs_path};
-/// Converts a Rust level string to a LSP severity
-fn map_level_to_severity(val: DiagnosticLevel) -> Option {
- let res = match val {
+/// Determines the LSP severity from a diagnostic
+fn map_diagnostic_to_severity(
+ config: &DiagnosticsConfig,
+ val: &ra_flycheck::Diagnostic,
+) -> Option {
+ let res = match val.level {
DiagnosticLevel::Ice => DiagnosticSeverity::Error,
DiagnosticLevel::Error => DiagnosticSeverity::Error,
- DiagnosticLevel::Warning => DiagnosticSeverity::Warning,
+ DiagnosticLevel::Warning => match &val.code {
+ Some(code) if config.warnings_as_hint.contains(&code.code) => DiagnosticSeverity::Hint,
+ Some(code) if config.warnings_as_info.contains(&code.code) => {
+ DiagnosticSeverity::Information
+ }
+ _ => DiagnosticSeverity::Warning,
+ },
DiagnosticLevel::Note => DiagnosticSeverity::Information,
DiagnosticLevel::Help => DiagnosticSeverity::Hint,
DiagnosticLevel::Unknown => return None,
@@ -172,6 +182,7 @@ pub(crate) struct MappedRustDiagnostic {
///
/// If the diagnostic has no primary span this will return `None`
pub(crate) fn map_rust_diagnostic_to_lsp(
+ config: &DiagnosticsConfig,
rd: &ra_flycheck::Diagnostic,
workspace_root: &Path,
) -> Vec {
@@ -180,7 +191,7 @@ pub(crate) fn map_rust_diagnostic_to_lsp(
return Vec::new();
}
- let severity = map_level_to_severity(rd.level);
+ let severity = map_diagnostic_to_severity(config, rd);
let mut source = String::from("rustc");
let mut code = rd.code.as_ref().map(|c| c.code.clone());
@@ -328,7 +339,7 @@ mod tests {
);
let workspace_root = Path::new("/test/");
- let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
+ let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
insta::assert_debug_snapshot!(diag);
}
@@ -410,7 +421,183 @@ mod tests {
);
let workspace_root = Path::new("/test/");
- let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
+ let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
+ insta::assert_debug_snapshot!(diag);
+ }
+
+ #[test]
+ #[cfg(not(windows))]
+ fn snap_rustc_unused_variable_as_info() {
+ let diag = parse_diagnostic(
+ r##"{
+ "message": "unused variable: `foo`",
+ "code": {
+ "code": "unused_variables",
+ "explanation": null
+ },
+ "level": "warning",
+ "spans": [
+ {
+ "file_name": "driver/subcommand/repl.rs",
+ "byte_start": 9228,
+ "byte_end": 9231,
+ "line_start": 291,
+ "line_end": 291,
+ "column_start": 9,
+ "column_end": 12,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " let foo = 42;",
+ "highlight_start": 9,
+ "highlight_end": 12
+ }
+ ],
+ "label": null,
+ "suggested_replacement": null,
+ "suggestion_applicability": null,
+ "expansion": null
+ }
+ ],
+ "children": [
+ {
+ "message": "#[warn(unused_variables)] on by default",
+ "code": null,
+ "level": "note",
+ "spans": [],
+ "children": [],
+ "rendered": null
+ },
+ {
+ "message": "consider prefixing with an underscore",
+ "code": null,
+ "level": "help",
+ "spans": [
+ {
+ "file_name": "driver/subcommand/repl.rs",
+ "byte_start": 9228,
+ "byte_end": 9231,
+ "line_start": 291,
+ "line_end": 291,
+ "column_start": 9,
+ "column_end": 12,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " let foo = 42;",
+ "highlight_start": 9,
+ "highlight_end": 12
+ }
+ ],
+ "label": null,
+ "suggested_replacement": "_foo",
+ "suggestion_applicability": "MachineApplicable",
+ "expansion": null
+ }
+ ],
+ "children": [],
+ "rendered": null
+ }
+ ],
+ "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
+ }"##,
+ );
+
+ let config = DiagnosticsConfig {
+ warnings_as_info: vec!["unused_variables".to_string()],
+ ..DiagnosticsConfig::default()
+ };
+
+ let workspace_root = Path::new("/test/");
+ let diag = map_rust_diagnostic_to_lsp(&config, &diag, workspace_root);
+ insta::assert_debug_snapshot!(diag);
+ }
+
+ #[test]
+ #[cfg(not(windows))]
+ fn snap_rustc_unused_variable_as_hint() {
+ let diag = parse_diagnostic(
+ r##"{
+ "message": "unused variable: `foo`",
+ "code": {
+ "code": "unused_variables",
+ "explanation": null
+ },
+ "level": "warning",
+ "spans": [
+ {
+ "file_name": "driver/subcommand/repl.rs",
+ "byte_start": 9228,
+ "byte_end": 9231,
+ "line_start": 291,
+ "line_end": 291,
+ "column_start": 9,
+ "column_end": 12,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " let foo = 42;",
+ "highlight_start": 9,
+ "highlight_end": 12
+ }
+ ],
+ "label": null,
+ "suggested_replacement": null,
+ "suggestion_applicability": null,
+ "expansion": null
+ }
+ ],
+ "children": [
+ {
+ "message": "#[warn(unused_variables)] on by default",
+ "code": null,
+ "level": "note",
+ "spans": [],
+ "children": [],
+ "rendered": null
+ },
+ {
+ "message": "consider prefixing with an underscore",
+ "code": null,
+ "level": "help",
+ "spans": [
+ {
+ "file_name": "driver/subcommand/repl.rs",
+ "byte_start": 9228,
+ "byte_end": 9231,
+ "line_start": 291,
+ "line_end": 291,
+ "column_start": 9,
+ "column_end": 12,
+ "is_primary": true,
+ "text": [
+ {
+ "text": " let foo = 42;",
+ "highlight_start": 9,
+ "highlight_end": 12
+ }
+ ],
+ "label": null,
+ "suggested_replacement": "_foo",
+ "suggestion_applicability": "MachineApplicable",
+ "expansion": null
+ }
+ ],
+ "children": [],
+ "rendered": null
+ }
+ ],
+ "rendered": "warning: unused variable: `foo`\n --> driver/subcommand/repl.rs:291:9\n |\n291 | let foo = 42;\n | ^^^ help: consider prefixing with an underscore: `_foo`\n |\n = note: #[warn(unused_variables)] on by default\n\n"
+ }"##,
+ );
+
+ let config = DiagnosticsConfig {
+ warnings_as_hint: vec!["unused_variables".to_string()],
+ ..DiagnosticsConfig::default()
+ };
+
+ let workspace_root = Path::new("/test/");
+ let diag = map_rust_diagnostic_to_lsp(&config, &diag, workspace_root);
insta::assert_debug_snapshot!(diag);
}
@@ -534,7 +721,7 @@ mod tests {
);
let workspace_root = Path::new("/test/");
- let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
+ let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
insta::assert_debug_snapshot!(diag);
}
@@ -654,7 +841,7 @@ mod tests {
);
let workspace_root = Path::new("/test/");
- let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
+ let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
insta::assert_debug_snapshot!(diag);
}
@@ -697,7 +884,7 @@ mod tests {
);
let workspace_root = Path::new("/test/");
- let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
+ let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
insta::assert_debug_snapshot!(diag);
}
@@ -968,7 +1155,7 @@ mod tests {
);
let workspace_root = Path::new("/test/");
- let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
+ let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
insta::assert_debug_snapshot!(diag);
}
@@ -1197,7 +1384,7 @@ mod tests {
);
let workspace_root = Path::new("/test/");
- let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
+ let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
insta::assert_debug_snapshot!(diag);
}
@@ -1330,7 +1517,7 @@ mod tests {
);
let workspace_root = Path::new("/test/");
- let diag = map_rust_diagnostic_to_lsp(&diag, workspace_root);
+ let diag = map_rust_diagnostic_to_lsp(&DiagnosticsConfig::default(), &diag, workspace_root);
insta::assert_debug_snapshot!(diag);
}
}
diff --git a/crates/rust-analyzer/src/main_loop.rs b/crates/rust-analyzer/src/main_loop.rs
index cc9bb1726e..80cfd3c28e 100644
--- a/crates/rust-analyzer/src/main_loop.rs
+++ b/crates/rust-analyzer/src/main_loop.rs
@@ -669,14 +669,11 @@ fn apply_document_changes(
mut line_index: Cow<'_, LineIndex>,
content_changes: Vec,
) {
- // Remove when https://github.com/rust-analyzer/rust-analyzer/issues/4263 is fixed.
- let backup_text = old_text.clone();
- let backup_changes = content_changes.clone();
-
// The changes we got must be applied sequentially, but can cross lines so we
// have to keep our line index updated.
// Some clients (e.g. Code) sort the ranges in reverse. As an optimization, we
// remember the last valid line in the index and only rebuild it if needed.
+ // The VFS will normalize the end of lines to `\n`.
enum IndexValid {
All,
UpToLineExclusive(u64),
@@ -700,19 +697,7 @@ fn apply_document_changes(
}
index_valid = IndexValid::UpToLineExclusive(range.start.line);
let range = from_proto::text_range(&line_index, range);
- let mut text = old_text.to_owned();
- match std::panic::catch_unwind(move || {
- text.replace_range(Range::::from(range), &change.text);
- text
- }) {
- Ok(t) => *old_text = t,
- Err(e) => {
- eprintln!("Bug in incremental text synchronization. Please report the following output on https://github.com/rust-analyzer/rust-analyzer/issues/4263");
- dbg!(&backup_text);
- dbg!(&backup_changes);
- std::panic::resume_unwind(e);
- }
- }
+ old_text.replace_range(Range::::from(range), &change.text);
}
None => {
*old_text = change.text;
@@ -734,6 +719,7 @@ fn on_check_task(
CheckTask::AddDiagnostic { workspace_root, diagnostic } => {
let diagnostics = crate::diagnostics::to_proto::map_rust_diagnostic_to_lsp(
+ &global_state.config.diagnostics,
&diagnostic,
&workspace_root,
);
diff --git a/editors/code/package.json b/editors/code/package.json
index e2027970db..3acc375f67 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -525,6 +525,24 @@
"markdownDescription": "Internal config for debugging, disables loading of sysroot crates",
"type": "boolean",
"default": true
+ },
+ "rust-analyzer.diagnostics.warningsAsInfo": {
+ "type": "array",
+ "uniqueItems": true,
+ "items": {
+ "type": "string"
+ },
+ "description": "List of warnings that should be displayed with info severity.\nThe warnings will be indicated by a blue squiggly underline in code and a blue icon in the problems panel.",
+ "default": []
+ },
+ "rust-analyzer.diagnostics.warningsAsHint": {
+ "type": "array",
+ "uniqueItems": true,
+ "items": {
+ "type": "string"
+ },
+ "description": "List of warnings warnings that should be displayed with hint severity.\nThe warnings will be indicated by faded text or three dots in code and will not show up in te problems panel.",
+ "default": []
}
}
},