use crate::server::ServerState;
use lsp_types::{DocumentFormattingParams, Position, Range, TextEdit};
pub fn handle(
state: &ServerState,
params: DocumentFormattingParams,
) -> anyhow::Result<Option<Vec<TextEdit>>> {
if let Some(document) = state.documents.get(params.text_document.uri.as_str()) {
let formatted = format_achitek_source(&document.text);
if formatted == document.text {
Ok(Some(Vec::new()))
} else {
Ok(Some(vec![TextEdit {
range: full_document_range(&document.text),
new_text: formatted,
}]))
}
} else {
Ok(None)
}
}
fn format_achitek_source(source: &str) -> String {
let mut formatted = String::new();
let mut indent = 0usize;
for raw_line in source.lines() {
let line = raw_line.trim();
if line.starts_with('}') {
indent = indent.saturating_sub(1);
}
if line.is_empty() {
formatted.push('\n');
} else {
formatted.push_str(&" ".repeat(indent));
formatted.push_str(line);
formatted.push('\n');
}
if line.ends_with('{') {
indent += 1;
}
}
formatted
}
fn full_document_range(source: &str) -> Range {
let last_line = source.lines().last().unwrap_or("");
Range {
start: Position {
line: 0,
character: 0,
},
end: Position {
line: u32::try_from(source.lines().count()).expect("line count should fit into u32"),
character: u32::try_from(last_line.len()).expect("line length should fit into u32"),
},
}
}