use std::{ops::RangeFull, thread::park_timeout};
use lady_deirdre::{lexis::ToSpan, sync::Trigger};
use log::{error, warn};
use lsp_types::{
error_codes::{REQUEST_CANCELLED, REQUEST_FAILED},
request::Formatting,
FormattingOptions,
TextEdit,
Uri,
};
use crate::{
analysis::{ModuleError, ModuleRead},
format::ScriptFormatConfig,
report::system_panic,
server::{
file::{LspModule, ANALYSIS_PRIORITY},
logger::LSP_SERVER_LOG,
rpc::{LspHandle, OutgoingEx, RpcId, RpcLatches},
tasks::{Task, TaskExecution, COOL_DOWN},
utils::span_to_range,
RpcSender,
},
};
pub(super) struct SendFormatting {
pub(super) latches: RpcLatches,
pub(super) outgoing: RpcSender,
pub(super) module: LspModule,
}
impl Task for SendFormatting {
const EXECUTION: TaskExecution = TaskExecution::ExecuteEach;
type Config = Self;
type Message = SendFormattingMessage;
#[inline(always)]
fn init(config: Self::Config) -> Self {
config
}
fn handle(&mut self, message: Self::Message) -> bool {
loop {
if message.cancel.is_active() {
warn!(target: LSP_SERVER_LOG, "[{}] Send formatting cancelled by the client.", message.uri.as_str());
self.outgoing.send_err_response(
&self.latches,
message.id,
REQUEST_CANCELLED,
"Send formatting cancelled by the client.",
);
break;
}
let handle = LspHandle::new(&message.cancel);
let module_read_guard = match self.module.as_ref().read(&handle, ANALYSIS_PRIORITY) {
Ok(guard) => guard,
Err(ModuleError::Interrupted(_)) => {
if message.cancel.is_active() {
warn!(target: LSP_SERVER_LOG, "[{}] Send formatting cancelled by the client.", message.uri.as_str());
self.outgoing.send_err_response(
&self.latches,
message.id,
REQUEST_CANCELLED,
"Send formatting cancelled by the client.",
);
break;
}
warn!(target: LSP_SERVER_LOG, "[{}] Send formatting interrupted.", message.uri.as_str());
park_timeout(COOL_DOWN);
continue;
}
Err(error) => {
error!(target: LSP_SERVER_LOG, "[{}] Send formatting error. {error}", message.uri.as_str());
self.outgoing.send_err_response(
&self.latches,
message.id,
REQUEST_FAILED,
"Send formatting error.",
);
break;
}
};
let text = module_read_guard.text();
let config = ScriptFormatConfig {
indent: message.options.tab_size as u16,
..Default::default()
};
let Some(new_text) = text.format(config) else {
self.outgoing
.send_ok_response::<Formatting>(&self.latches, message.id, None);
break;
};
let range = match RangeFull.to_position_span(&text) {
Some(span) => span_to_range(&span),
None => system_panic!("Span to position conversion failure."),
};
self.outgoing.send_ok_response::<Formatting>(
&self.latches,
message.id,
Some(vec![TextEdit { range, new_text }]),
);
break;
}
true
}
#[inline(always)]
fn module(&self) -> &LspModule {
&self.module
}
}
pub(super) struct SendFormattingMessage {
pub(super) id: RpcId,
pub(super) uri: Uri,
pub(super) cancel: Trigger,
pub(super) options: FormattingOptions,
}