use super::helpers::*;
use std::fs;
use tempfile::TempDir;
use tower_lsp_server::ls_types::*;
#[tokio::test]
async fn test_document_symbols_hierarchical() {
let server = TestLspServer::new();
let content = "# Top Level\n\n## Section 1\n\nContent.\n\n## Section 2\n\n### Subsection\n\nMore content.";
server
.open_document("file:///test.qmd", content, "quarto")
.await;
let symbols = server.get_symbols("file:///test.qmd").await;
assert!(symbols.is_some());
if let Some(DocumentSymbolResponse::Nested(syms)) = symbols {
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "Top Level");
let children = syms[0].children.as_ref().unwrap();
assert_eq!(children.len(), 2);
assert_eq!(children[0].name, "Section 1");
assert_eq!(children[1].name, "Section 2");
let subsections = children[1].children.as_ref().unwrap();
assert_eq!(subsections.len(), 1);
assert_eq!(subsections[0].name, "Subsection");
} else {
panic!("Expected nested document symbols");
}
}
#[tokio::test]
async fn test_document_symbols_with_table() {
let server = TestLspServer::new();
let content = "# Report\n\n| Col1 | Col2 |\n|------|------|\n| A | B |\n\nText.";
server
.open_document("file:///test.qmd", content, "quarto")
.await;
let symbols = server.get_symbols("file:///test.qmd").await;
assert!(symbols.is_some());
if let Some(DocumentSymbolResponse::Nested(syms)) = symbols {
assert_eq!(syms.len(), 1);
assert_eq!(syms[0].name, "Report");
let children = syms[0].children.as_ref();
assert!(children.is_some());
let children = children.unwrap();
let table_symbol = children.iter().find(|s| s.name.starts_with("Table"));
assert!(table_symbol.is_some(), "Should have a table symbol");
} else {
panic!("Expected nested document symbols");
}
}
#[tokio::test]
async fn test_document_symbols_include_yaml_frontmatter_symbol() {
let server = TestLspServer::new();
let content = "---\ntitle: Report\n---\n\n# Report\n\nBody.";
server
.open_document("file:///test.qmd", content, "quarto")
.await;
let symbols = server.get_symbols("file:///test.qmd").await;
let Some(DocumentSymbolResponse::Nested(syms)) = symbols else {
panic!("Expected nested document symbols");
};
assert!(
syms.iter().any(|symbol| symbol.name == "YAML Frontmatter"),
"Expected a YAML frontmatter document symbol"
);
}
#[tokio::test]
async fn test_document_symbols_headings_use_outline_visible_kind_for_qmd() {
let server = TestLspServer::new();
let content = "---\ntitle: Report\n---\n\n# Report\n\n## Details\n";
server
.open_document("file:///test.qmd", content, "quarto")
.await;
let symbols = server.get_symbols("file:///test.qmd").await;
let Some(DocumentSymbolResponse::Nested(syms)) = symbols else {
panic!("Expected nested document symbols");
};
let heading = syms
.iter()
.find(|symbol| symbol.name == "Report")
.expect("Expected heading symbol");
assert_eq!(heading.kind, SymbolKind::NAMESPACE);
}
#[tokio::test]
async fn test_document_symbols_headings_use_outline_visible_kind_for_rmd() {
let server = TestLspServer::new();
let content = "---\ntitle: Report\n---\n\n# Report\n\n## Details\n";
server
.open_document("file:///test.Rmd", content, "rmarkdown")
.await;
let symbols = server.get_symbols("file:///test.Rmd").await;
let Some(DocumentSymbolResponse::Nested(syms)) = symbols else {
panic!("Expected nested document symbols");
};
let heading = syms
.iter()
.find(|symbol| symbol.name == "Report")
.expect("Expected heading symbol");
assert_eq!(heading.kind, SymbolKind::NAMESPACE);
}
#[tokio::test]
async fn test_folding_ranges_headings() {
let server = TestLspServer::new();
let content = "# Heading 1\n\nContent 1.\n\n# Heading 2\n\nContent 2.";
server
.open_document("file:///test.qmd", content, "quarto")
.await;
let ranges = server.get_folding_ranges("file:///test.qmd").await;
assert!(ranges.is_some());
let ranges = ranges.unwrap();
assert!(ranges.len() >= 2, "Should have at least 2 folding ranges");
assert_eq!(ranges[0].start_line, 0);
}
#[tokio::test]
async fn test_folding_ranges_code_block() {
let server = TestLspServer::new();
let content = "# Doc\n\n```python\nprint('hello')\nprint('world')\n```\n\nText.";
server
.open_document("file:///test.qmd", content, "quarto")
.await;
let ranges = server.get_folding_ranges("file:///test.qmd").await;
assert!(ranges.is_some());
let ranges = ranges.unwrap();
assert!(!ranges.is_empty());
let code_fold = ranges.iter().find(|r| r.start_line == 2);
assert!(code_fold.is_some(), "Should have fold for code block");
}
#[tokio::test]
async fn test_workspace_symbols_include_open_standalone_document() {
let server = TestLspServer::new();
let content = "# Intro\n\n## Methods\n";
let temp_dir = TempDir::new().unwrap();
let uri = Uri::from_file_path(temp_dir.path().join("standalone.md"))
.unwrap()
.to_string();
server.open_document(&uri, content, "markdown").await;
let symbols = server.get_workspace_symbols("intro").await;
let Some(symbols) = symbols else {
panic!("Expected workspace symbols");
};
assert_eq!(symbols.len(), 1);
assert_eq!(symbols[0].name, "Intro");
}
#[tokio::test]
async fn test_workspace_symbols_include_multiple_open_documents() {
let server = TestLspServer::new();
let temp_dir = TempDir::new().unwrap();
let doc1_uri = Uri::from_file_path(temp_dir.path().join("doc1.qmd"))
.unwrap()
.to_string();
let doc2_uri = Uri::from_file_path(temp_dir.path().join("doc2.qmd"))
.unwrap()
.to_string();
server
.open_document(&doc1_uri, "# Alpha\n\n## Shared\n", "quarto")
.await;
server
.open_document(&doc2_uri, "# Beta\n\n## Shared\n", "quarto")
.await;
let symbols = server.get_workspace_symbols("shared").await;
let Some(symbols) = symbols else {
panic!("Expected workspace symbols");
};
assert_eq!(symbols.len(), 2);
assert_eq!(symbols[0].name, "Shared");
assert_eq!(symbols[1].name, "Shared");
assert_ne!(symbols[0].location.uri, symbols[1].location.uri);
}
#[tokio::test]
async fn test_workspace_symbols_include_graph_documents() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
let parent_path = root.join("parent.qmd");
let child_path = root.join("child.qmd");
fs::write(&child_path, "# Child Heading\n\n## Child Section\n").unwrap();
fs::write(
&parent_path,
"{{< include child.qmd >}}\n\n# Parent Heading\n",
)
.unwrap();
let server = TestLspServer::new();
let root_uri = Uri::from_file_path(root).unwrap().to_string();
server.initialize(&root_uri).await;
server
.open_document(
&Uri::from_file_path(&parent_path).unwrap().to_string(),
&fs::read_to_string(&parent_path).unwrap(),
"quarto",
)
.await;
let symbols = server.get_workspace_symbols("child heading").await;
let Some(symbols) = symbols else {
panic!("Expected workspace symbols");
};
assert_eq!(symbols.len(), 1);
assert_eq!(symbols[0].name, "Child Heading");
assert_eq!(
symbols[0].location.uri,
Uri::from_file_path(&child_path).unwrap()
);
}