use lsp_types::{
DocumentFormattingParams, DocumentRangeFormattingParams, Position, Range, TextEdit,
};
use super::super::conversions::{offset_to_position, position_to_offset};
use super::super::helpers::is_uri_excluded;
use crate::lsp::global_state::StateSnapshot;
use crate::{parser, range_utils};
pub(crate) fn format_document(
snap: &StateSnapshot,
params: DocumentFormattingParams,
) -> Option<Vec<TextEdit>> {
let uri = params.text_document.uri;
log::debug!("format_document uri={}", uri.as_str());
let (text, config, source, workspace_root) = snap.document_config_and_source(&uri)?;
if is_uri_excluded(&uri, &config, &source, workspace_root.as_deref()) {
log::info!(
"Skipping formatting (matched exclude pattern): {}",
uri.as_str()
);
return None;
}
let formatted = match snap.parsed_tree(&uri) {
Some(tree) => crate::format_with_tree(&text, &tree, &config, None),
None => crate::format(&text, Some(config), None),
};
if formatted == text {
return None;
}
let end_position = offset_to_position(&text, text.len());
let range = Range {
start: Position {
line: 0,
character: 0,
},
end: end_position,
};
Some(vec![TextEdit {
range,
new_text: formatted,
}])
}
pub(crate) fn format_range(
snap: &StateSnapshot,
params: DocumentRangeFormattingParams,
) -> Option<Vec<TextEdit>> {
let uri = params.text_document.uri;
let range = params.range;
log::debug!(
"format_range uri={} start={:?} end={:?}",
uri.as_str(),
range.start,
range.end
);
let (text, config) = snap.document_and_config(&uri)?;
let start_line = (range.start.line + 1) as usize;
let mut end_line = (range.end.line + 1) as usize;
if range.end.character == 0 && range.end.line > range.start.line {
end_line = range.end.line as usize;
}
let _ = (
position_to_offset(&text, range.start),
position_to_offset(&text, range.end),
);
let tree = snap
.parsed_tree(&uri)
.unwrap_or_else(|| parser::parse(&text, Some(config.clone())));
let expanded_range =
range_utils::expand_line_range_to_blocks(&tree, &text, start_line, end_line);
let formatted = crate::format_with_tree(&text, &tree, &config, Some((start_line, end_line)));
if formatted.is_empty() || formatted == text {
return None;
}
let (start_offset, end_offset) = expanded_range?;
let edit_range = Range {
start: offset_to_position(&text, start_offset),
end: offset_to_position(&text, end_offset.min(text.len())),
};
Some(vec![TextEdit {
range: edit_range,
new_text: formatted,
}])
}