diff options
Diffstat (limited to 'support/texlab/src/features/forward_search.rs')
-rw-r--r-- | support/texlab/src/features/forward_search.rs | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/support/texlab/src/features/forward_search.rs b/support/texlab/src/features/forward_search.rs new file mode 100644 index 0000000000..39470a64ec --- /dev/null +++ b/support/texlab/src/features/forward_search.rs @@ -0,0 +1,124 @@ +use std::{ + io, + path::Path, + process::{Command, Stdio}, +}; + +use cancellation::CancellationToken; +use log::error; +use lsp_types::TextDocumentPositionParams; +use serde::{Deserialize, Serialize}; +use serde_repr::{Deserialize_repr, Serialize_repr}; + +use super::FeatureRequest; + +#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize_repr, Deserialize_repr)] +#[repr(i32)] +pub enum ForwardSearchStatus { + Success = 0, + Error = 1, + Failure = 2, + Unconfigured = 3, +} + +#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)] +pub struct ForwardSearchResult { + pub status: ForwardSearchStatus, +} + +pub fn execute_forward_search( + request: FeatureRequest<TextDocumentPositionParams>, + _cancellation_token: &CancellationToken, +) -> Option<ForwardSearchResult> { + let options = { + request + .context + .options + .read() + .unwrap() + .forward_search + .clone() + .unwrap_or_default() + }; + + if options.executable.is_none() || options.args.is_none() { + return Some(ForwardSearchResult { + status: ForwardSearchStatus::Unconfigured, + }); + } + + let root_document = request + .subset + .documents + .iter() + .find(|document| { + if let Some(data) = document.data.as_latex() { + data.extras.has_document_environment + && !data + .extras + .explicit_links + .iter() + .filter_map(|link| link.as_component_name()) + .any(|name| name == "subfiles.cls") + } else { + false + } + }) + .filter(|document| document.uri.scheme() == "file")?; + + let data = root_document.data.as_latex()?; + let pdf_path = data + .extras + .implicit_links + .pdf + .iter() + .filter_map(|uri| uri.to_file_path().ok()) + .find(|path| path.exists())?; + + let tex_path = request.main_document().uri.to_file_path().ok()?; + + let args: Vec<String> = options + .args + .unwrap() + .into_iter() + .flat_map(|arg| { + replace_placeholder(&tex_path, &pdf_path, request.params.position.line, arg) + }) + .collect(); + + let status = match run_process(options.executable.unwrap(), args) { + Ok(()) => ForwardSearchStatus::Success, + Err(why) => { + error!("Unable to execute forward search: {}", why); + ForwardSearchStatus::Failure + } + }; + Some(ForwardSearchResult { status }) +} + +fn replace_placeholder( + tex_file: &Path, + pdf_file: &Path, + line_number: u32, + argument: String, +) -> Option<String> { + let result = if argument.starts_with('"') || argument.ends_with('"') { + argument + } else { + argument + .replace("%f", tex_file.to_str()?) + .replace("%p", pdf_file.to_str()?) + .replace("%l", &(line_number + 1).to_string()) + }; + Some(result) +} + +fn run_process(executable: String, args: Vec<String>) -> io::Result<()> { + Command::new(executable) + .args(args) + .stdin(Stdio::null()) + .stdout(Stdio::null()) + .stderr(Stdio::null()) + .status()?; + Ok(()) +} |