use either::Either;
use hir::{InFile, Semantics};
-use ide_db::{call_info::ActiveParameter, SymbolKind};
+use ide_db::{call_info::ActiveParameter, helpers::rust_doc::is_rust_fence, SymbolKind};
use syntax::{
ast::{self, AstNode},
AstToken, NodeOrToken, SyntaxNode, SyntaxToken, TextRange, TextSize,
}
const RUSTDOC_FENCE: &'static str = "```";
-const RUSTDOC_FENCE_TOKENS: &[&'static str] = &[
- "",
- "rust",
- "should_panic",
- "ignore",
- "no_run",
- "compile_fail",
- "allow_fail",
- "test_harness",
- "edition2015",
- "edition2018",
- "edition2021",
-];
-
-fn is_rustdoc_fence_token(token: &str) -> bool {
- if RUSTDOC_FENCE_TOKENS.contains(&token) {
- return true;
- }
- token.starts_with('E') && token.len() == 5 && token[1..].parse::<u32>().is_ok()
-}
/// Injection of syntax highlighting of doctests.
pub(super) fn doc_comment(
is_codeblock = !is_codeblock;
// Check whether code is rust by inspecting fence guards
let guards = &line[idx + RUSTDOC_FENCE.len()..];
- let is_rust = guards.split(',').any(|sub| is_rustdoc_fence_token(sub.trim()));
+ let is_rust = is_rust_fence(guards);
is_doctest = is_codeblock && is_rust;
continue;
}
.filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Function))
.count()
};
- assert_eq!(hash, 1629);
+ assert_eq!(hash, 1632);
}
#[test]
//! A module with ide helpers for high-level ide features.
pub mod insert_use;
pub mod import_assets;
+pub mod rust_doc;
use std::collections::VecDeque;
--- /dev/null
+//! Rustdoc specific doc comment handling
+
+// 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;
+ let mut seen_other_tags = false;
+
+ let tokens = s
+ .trim()
+ .split(|c| c == ',' || c == ' ' || c == '\t')
+ .map(str::trim)
+ .filter(|t| !t.is_empty());
+
+ for token in tokens {
+ match token {
+ "should_panic" | "no_run" | "ignore" | "allow_fail" => {
+ seen_rust_tags = !seen_other_tags
+ }
+ "rust" => seen_rust_tags = true,
+ "test_harness" | "compile_fail" => seen_rust_tags = !seen_other_tags || seen_rust_tags,
+ x if x.starts_with("edition") => {}
+ x if x.starts_with('E') && x.len() == 5 => {
+ if x[1..].parse::<u32>().is_ok() {
+ seen_rust_tags = !seen_other_tags || seen_rust_tags;
+ } else {
+ seen_other_tags = true;
+ }
+ }
+ _ => seen_other_tags = true,
+ }
+ }
+
+ !seen_other_tags || seen_rust_tags
+}
//! Transforms markdown
+use ide_db::helpers::rust_doc::is_rust_fence;
const RUSTDOC_FENCE: &str = "```";
-const RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC: &[&str] = &[
- "",
- "rust",
- "should_panic",
- "ignore",
- "no_run",
- "compile_fail",
- "allow_fail",
- "test_harness",
- "edition2015",
- "edition2018",
- "edition2021",
-];
pub(crate) fn format_docs(src: &str) -> String {
let mut processed_lines = Vec::new();
in_code_block ^= true;
if in_code_block {
- is_rust =
- header.split(',').any(|sub| is_rust_specific_code_block_attribute(sub.trim()));
+ is_rust = is_rust_fence(header);
if is_rust {
line = "```rust";
processed_lines.join("\n")
}
-fn is_rust_specific_code_block_attribute(attr: &str) -> bool {
- if RUSTDOC_CODE_BLOCK_ATTRIBUTES_RUST_SPECIFIC.contains(&attr) {
- return true;
- }
- attr.starts_with('E') && attr.len() == 5 && attr[1..].parse::<u32>().is_ok()
-}
-
fn code_line_ignored_by_rustdoc(line: &str) -> bool {
let trimmed = line.trim();
trimmed == "#" || trimmed.starts_with("# ") || trimmed.starts_with("#\t")