Unnamed repository; edit this file 'description' to name the repository.
Remove markdown module from rust-analyzer crate
Lukas Wirth 2023-09-02
parent 8eddc64 · commit 81f0108
-rw-r--r--crates/hir-def/src/attr.rs4
-rw-r--r--crates/ide-db/src/documentation.rs1
-rw-r--r--crates/ide-db/src/rust_doc.rs169
-rw-r--r--crates/ide/src/signature_help.rs26
-rw-r--r--crates/rust-analyzer/src/lib.rs1
-rw-r--r--crates/rust-analyzer/src/lsp/to_proto.rs7
-rw-r--r--crates/rust-analyzer/src/markdown.rs167
7 files changed, 188 insertions, 187 deletions
diff --git a/crates/hir-def/src/attr.rs b/crates/hir-def/src/attr.rs
index 07dbafec74..c6454eb9ea 100644
--- a/crates/hir-def/src/attr.rs
+++ b/crates/hir-def/src/attr.rs
@@ -5,7 +5,7 @@ pub mod builtin;
#[cfg(test)]
mod tests;
-use std::{hash::Hash, ops};
+use std::{hash::Hash, ops, slice::Iter as SliceIter};
use base_db::CrateId;
use cfg::{CfgExpr, CfgOptions};
@@ -251,7 +251,6 @@ impl Attrs {
}
}
-use std::slice::Iter as SliceIter;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum DocAtom {
/// eg. `#[doc(hidden)]`
@@ -265,7 +264,6 @@ pub enum DocAtom {
// Adapted from `CfgExpr` parsing code
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-// #[cfg_attr(test, derive(derive_arbitrary::Arbitrary))]
pub enum DocExpr {
Invalid,
/// eg. `#[doc(hidden)]`, `#[doc(alias = "x")]`
diff --git a/crates/ide-db/src/documentation.rs b/crates/ide-db/src/documentation.rs
index aa8a374218..26f3cd28a2 100644
--- a/crates/ide-db/src/documentation.rs
+++ b/crates/ide-db/src/documentation.rs
@@ -1,3 +1,4 @@
+//! Documentation attribute related utilties.
use either::Either;
use hir::{
db::{DefDatabase, HirDatabase},
diff --git a/crates/ide-db/src/rust_doc.rs b/crates/ide-db/src/rust_doc.rs
index e27e23867a..ab2a250289 100644
--- a/crates/ide-db/src/rust_doc.rs
+++ b/crates/ide-db/src/rust_doc.rs
@@ -1,5 +1,7 @@
//! Rustdoc specific doc comment handling
+use crate::documentation::Documentation;
+
// stripped down version of https://github.com/rust-lang/rust/blob/392ba2ba1a7d6c542d2459fb8133bebf62a4a423/src/librustdoc/html/markdown.rs#L810-L933
pub fn is_rust_fence(s: &str) -> bool {
let mut seen_rust_tags = false;
@@ -32,3 +34,170 @@ pub fn is_rust_fence(s: &str) -> bool {
!seen_other_tags || seen_rust_tags
}
+
+const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
+
+pub fn format_docs(src: &Documentation) -> String {
+ format_docs_(src.as_str())
+}
+
+fn format_docs_(src: &str) -> String {
+ let mut processed_lines = Vec::new();
+ let mut in_code_block = false;
+ let mut is_rust = false;
+
+ for mut line in src.lines() {
+ if in_code_block && is_rust && code_line_ignored_by_rustdoc(line) {
+ continue;
+ }
+
+ if let Some(header) = RUSTDOC_FENCES.into_iter().find_map(|fence| line.strip_prefix(fence))
+ {
+ in_code_block ^= true;
+
+ if in_code_block {
+ is_rust = is_rust_fence(header);
+
+ if is_rust {
+ line = "```rust";
+ }
+ }
+ }
+
+ if in_code_block {
+ let trimmed = line.trim_start();
+ if is_rust && trimmed.starts_with("##") {
+ line = &trimmed[1..];
+ }
+ }
+
+ processed_lines.push(line);
+ }
+ processed_lines.join("\n")
+}
+
+fn code_line_ignored_by_rustdoc(line: &str) -> bool {
+ let trimmed = line.trim();
+ trimmed == "#" || trimmed.starts_with("# ") || trimmed.starts_with("#\t")
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn test_format_docs_adds_rust() {
+ let comment = "```\nfn some_rust() {}\n```";
+ assert_eq!(format_docs_(comment), "```rust\nfn some_rust() {}\n```");
+ }
+
+ #[test]
+ fn test_format_docs_handles_plain_text() {
+ let comment = "```text\nthis is plain text\n```";
+ assert_eq!(format_docs_(comment), "```text\nthis is plain text\n```");
+ }
+
+ #[test]
+ fn test_format_docs_handles_non_rust() {
+ let comment = "```sh\nsupposedly shell code\n```";
+ assert_eq!(format_docs_(comment), "```sh\nsupposedly shell code\n```");
+ }
+
+ #[test]
+ fn test_format_docs_handles_rust_alias() {
+ let comment = "```ignore\nlet z = 55;\n```";
+ assert_eq!(format_docs_(comment), "```rust\nlet z = 55;\n```");
+ }
+
+ #[test]
+ fn test_format_docs_handles_complex_code_block_attrs() {
+ let comment = "```rust,no_run\nlet z = 55;\n```";
+ assert_eq!(format_docs_(comment), "```rust\nlet z = 55;\n```");
+ }
+
+ #[test]
+ fn test_format_docs_handles_error_codes() {
+ let comment = "```compile_fail,E0641\nlet b = 0 as *const _;\n```";
+ assert_eq!(format_docs_(comment), "```rust\nlet b = 0 as *const _;\n```");
+ }
+
+ #[test]
+ fn test_format_docs_skips_comments_in_rust_block() {
+ let comment =
+ "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```";
+ assert_eq!(format_docs_(comment), "```rust\n#stay1\nstay2\n```");
+ }
+
+ #[test]
+ fn test_format_docs_does_not_skip_lines_if_plain_text() {
+ let comment =
+ "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```";
+ assert_eq!(
+ format_docs_(comment),
+ "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```",
+ );
+ }
+
+ #[test]
+ fn test_format_docs_keeps_comments_outside_of_rust_block() {
+ let comment = " # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t";
+ assert_eq!(format_docs_(comment), comment);
+ }
+
+ #[test]
+ fn test_format_docs_preserves_newlines() {
+ let comment = "this\nis\nmultiline";
+ assert_eq!(format_docs_(comment), comment);
+ }
+
+ #[test]
+ fn test_code_blocks_in_comments_marked_as_rust() {
+ let comment = r#"```rust
+fn main(){}
+```
+Some comment.
+```
+let a = 1;
+```"#;
+
+ assert_eq!(
+ format_docs_(comment),
+ "```rust\nfn main(){}\n```\nSome comment.\n```rust\nlet a = 1;\n```"
+ );
+ }
+
+ #[test]
+ fn test_code_blocks_in_comments_marked_as_text() {
+ let comment = r#"```text
+filler
+text
+```
+Some comment.
+```
+let a = 1;
+```"#;
+
+ assert_eq!(
+ format_docs_(comment),
+ "```text\nfiller\ntext\n```\nSome comment.\n```rust\nlet a = 1;\n```"
+ );
+ }
+
+ #[test]
+ fn test_format_docs_handles_escape_double_hashes() {
+ let comment = r#"```rust
+let s = "foo
+## bar # baz";
+```"#;
+
+ assert_eq!(format_docs_(comment), "```rust\nlet s = \"foo\n# bar # baz\";\n```");
+ }
+
+ #[test]
+ fn test_format_docs_handles_double_hashes_non_rust() {
+ let comment = r#"```markdown
+## A second-level heading
+```"#;
+ assert_eq!(format_docs_(comment), "```markdown\n## A second-level heading\n```");
+ }
+}
diff --git a/crates/ide/src/signature_help.rs b/crates/ide/src/signature_help.rs
index f57eb2ba3e..e020b52e17 100644
--- a/crates/ide/src/signature_help.rs
+++ b/crates/ide/src/signature_help.rs
@@ -8,7 +8,7 @@ use hir::{AssocItem, GenericParam, HirDisplay, ModuleDef, PathResolution, Semant
use ide_db::{
active_parameter::{callable_for_node, generic_def_for_node},
base_db::FilePosition,
- documentation::HasDocs,
+ documentation::{Documentation, HasDocs},
FxIndexMap,
};
use stdx::format_to;
@@ -27,7 +27,7 @@ use crate::RootDatabase;
/// edited.
#[derive(Debug)]
pub struct SignatureHelp {
- pub doc: Option<String>,
+ pub doc: Option<Documentation>,
pub signature: String,
pub active_parameter: Option<usize>,
parameters: Vec<TextRange>,
@@ -178,7 +178,7 @@ fn signature_help_for_call(
let mut fn_params = None;
match callable.kind() {
hir::CallableKind::Function(func) => {
- res.doc = func.docs(db).map(|it| it.into());
+ res.doc = func.docs(db);
format_to!(res.signature, "fn {}", func.name(db).display(db));
fn_params = Some(match callable.receiver_param(db) {
Some(_self) => func.params_without_self(db),
@@ -186,11 +186,11 @@ fn signature_help_for_call(
});
}
hir::CallableKind::TupleStruct(strukt) => {
- res.doc = strukt.docs(db).map(|it| it.into());
+ res.doc = strukt.docs(db);
format_to!(res.signature, "struct {}", strukt.name(db).display(db));
}
hir::CallableKind::TupleEnumVariant(variant) => {
- res.doc = variant.docs(db).map(|it| it.into());
+ res.doc = variant.docs(db);
format_to!(
res.signature,
"enum {}::{}",
@@ -264,38 +264,38 @@ fn signature_help_for_generics(
let db = sema.db;
match generics_def {
hir::GenericDef::Function(it) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "fn {}", it.name(db).display(db));
}
hir::GenericDef::Adt(hir::Adt::Enum(it)) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "enum {}", it.name(db).display(db));
}
hir::GenericDef::Adt(hir::Adt::Struct(it)) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "struct {}", it.name(db).display(db));
}
hir::GenericDef::Adt(hir::Adt::Union(it)) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "union {}", it.name(db).display(db));
}
hir::GenericDef::Trait(it) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "trait {}", it.name(db).display(db));
}
hir::GenericDef::TraitAlias(it) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "trait {}", it.name(db).display(db));
}
hir::GenericDef::TypeAlias(it) => {
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
format_to!(res.signature, "type {}", it.name(db).display(db));
}
hir::GenericDef::Variant(it) => {
// In paths, generics of an enum can be specified *after* one of its variants.
// eg. `None::<u8>`
// We'll use the signature of the enum, but include the docs of the variant.
- res.doc = it.docs(db).map(|it| it.into());
+ res.doc = it.docs(db);
let enum_ = it.parent_enum(db);
format_to!(res.signature, "enum {}", enum_.name(db).display(db));
generics_def = enum_.into();
diff --git a/crates/rust-analyzer/src/lib.rs b/crates/rust-analyzer/src/lib.rs
index 04ac77b1f6..6c62577f69 100644
--- a/crates/rust-analyzer/src/lib.rs
+++ b/crates/rust-analyzer/src/lib.rs
@@ -26,7 +26,6 @@ mod dispatch;
mod global_state;
mod line_index;
mod main_loop;
-mod markdown;
mod mem_docs;
mod op_queue;
mod reload;
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index daa7f5fe19..57f4602254 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -13,6 +13,7 @@ use ide::{
RenameError, Runnable, Severity, SignatureHelp, SnippetEdit, SourceChange, StructureNodeKind,
SymbolKind, TextEdit, TextRange, TextSize,
};
+use ide_db::rust_doc::format_docs;
use itertools::Itertools;
use serde_json::to_value;
use vfs::AbsPath;
@@ -105,7 +106,7 @@ pub(crate) fn diagnostic_severity(severity: Severity) -> lsp_types::DiagnosticSe
}
pub(crate) fn documentation(documentation: Documentation) -> lsp_types::Documentation {
- let value = crate::markdown::format_docs(documentation.as_str());
+ let value = format_docs(&documentation);
let markup_content = lsp_types::MarkupContent { kind: lsp_types::MarkupKind::Markdown, value };
lsp_types::Documentation::MarkupContent(markup_content)
}
@@ -416,7 +417,7 @@ pub(crate) fn signature_help(
let documentation = call_info.doc.filter(|_| config.docs).map(|doc| {
lsp_types::Documentation::MarkupContent(lsp_types::MarkupContent {
kind: lsp_types::MarkupKind::Markdown,
- value: crate::markdown::format_docs(&doc),
+ value: format_docs(&doc),
})
});
@@ -1531,7 +1532,7 @@ pub(crate) fn markup_content(
ide::HoverDocFormat::Markdown => lsp_types::MarkupKind::Markdown,
ide::HoverDocFormat::PlainText => lsp_types::MarkupKind::PlainText,
};
- let value = crate::markdown::format_docs(markup.as_str());
+ let value = format_docs(&Documentation::new(markup.into()));
lsp_types::MarkupContent { kind, value }
}
diff --git a/crates/rust-analyzer/src/markdown.rs b/crates/rust-analyzer/src/markdown.rs
deleted file mode 100644
index 140e16ecf1..0000000000
--- a/crates/rust-analyzer/src/markdown.rs
+++ /dev/null
@@ -1,167 +0,0 @@
-//! Transforms rust like doc content to markdown, replacing rustdoc fences and removing rustdoc code
-//! block comments.
-use ide_db::rust_doc::is_rust_fence;
-
-const RUSTDOC_FENCES: [&str; 2] = ["```", "~~~"];
-
-// FIXME: why is this in this crate?
-pub(crate) fn format_docs(src: &str) -> String {
- let mut processed_lines = Vec::new();
- let mut in_code_block = false;
- let mut is_rust = false;
-
- for mut line in src.lines() {
- if in_code_block && is_rust && code_line_ignored_by_rustdoc(line) {
- continue;
- }
-
- if let Some(header) = RUSTDOC_FENCES.into_iter().find_map(|fence| line.strip_prefix(fence))
- {
- in_code_block ^= true;
-
- if in_code_block {
- is_rust = is_rust_fence(header);
-
- if is_rust {
- line = "```rust";
- }
- }
- }
-
- if in_code_block {
- let trimmed = line.trim_start();
- if is_rust && trimmed.starts_with("##") {
- line = &trimmed[1..];
- }
- }
-
- processed_lines.push(line);
- }
- processed_lines.join("\n")
-}
-
-fn code_line_ignored_by_rustdoc(line: &str) -> bool {
- let trimmed = line.trim();
- trimmed == "#" || trimmed.starts_with("# ") || trimmed.starts_with("#\t")
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_format_docs_adds_rust() {
- let comment = "```\nfn some_rust() {}\n```";
- assert_eq!(format_docs(comment), "```rust\nfn some_rust() {}\n```");
- }
-
- #[test]
- fn test_format_docs_handles_plain_text() {
- let comment = "```text\nthis is plain text\n```";
- assert_eq!(format_docs(comment), "```text\nthis is plain text\n```");
- }
-
- #[test]
- fn test_format_docs_handles_non_rust() {
- let comment = "```sh\nsupposedly shell code\n```";
- assert_eq!(format_docs(comment), "```sh\nsupposedly shell code\n```");
- }
-
- #[test]
- fn test_format_docs_handles_rust_alias() {
- let comment = "```ignore\nlet z = 55;\n```";
- assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```");
- }
-
- #[test]
- fn test_format_docs_handles_complex_code_block_attrs() {
- let comment = "```rust,no_run\nlet z = 55;\n```";
- assert_eq!(format_docs(comment), "```rust\nlet z = 55;\n```");
- }
-
- #[test]
- fn test_format_docs_handles_error_codes() {
- let comment = "```compile_fail,E0641\nlet b = 0 as *const _;\n```";
- assert_eq!(format_docs(comment), "```rust\nlet b = 0 as *const _;\n```");
- }
-
- #[test]
- fn test_format_docs_skips_comments_in_rust_block() {
- let comment =
- "```rust\n # skip1\n# skip2\n#stay1\nstay2\n#\n #\n # \n #\tskip3\n\t#\t\n```";
- assert_eq!(format_docs(comment), "```rust\n#stay1\nstay2\n```");
- }
-
- #[test]
- fn test_format_docs_does_not_skip_lines_if_plain_text() {
- let comment =
- "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```";
- assert_eq!(
- format_docs(comment),
- "```text\n # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t\n```",
- );
- }
-
- #[test]
- fn test_format_docs_keeps_comments_outside_of_rust_block() {
- let comment = " # stay1\n# stay2\n#stay3\nstay4\n#\n #\n # \n #\tstay5\n\t#\t";
- assert_eq!(format_docs(comment), comment);
- }
-
- #[test]
- fn test_format_docs_preserves_newlines() {
- let comment = "this\nis\nmultiline";
- assert_eq!(format_docs(comment), comment);
- }
-
- #[test]
- fn test_code_blocks_in_comments_marked_as_rust() {
- let comment = r#"```rust
-fn main(){}
-```
-Some comment.
-```
-let a = 1;
-```"#;
-
- assert_eq!(
- format_docs(comment),
- "```rust\nfn main(){}\n```\nSome comment.\n```rust\nlet a = 1;\n```"
- );
- }
-
- #[test]
- fn test_code_blocks_in_comments_marked_as_text() {
- let comment = r#"```text
-filler
-text
-```
-Some comment.
-```
-let a = 1;
-```"#;
-
- assert_eq!(
- format_docs(comment),
- "```text\nfiller\ntext\n```\nSome comment.\n```rust\nlet a = 1;\n```"
- );
- }
-
- #[test]
- fn test_format_docs_handles_escape_double_hashes() {
- let comment = r#"```rust
-let s = "foo
-## bar # baz";
-```"#;
-
- assert_eq!(format_docs(comment), "```rust\nlet s = \"foo\n# bar # baz\";\n```");
- }
-
- #[test]
- fn test_format_docs_handles_double_hashes_non_rust() {
- let comment = r#"```markdown
-## A second-level heading
-```"#;
- assert_eq!(format_docs(comment), "```markdown\n## A second-level heading\n```");
- }
-}