1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
use log::*;
use serde::{Deserialize, Serialize};
use serde_repr::*;
use std::io;
use std::path::Path;
use std::process::Stdio;
use tokio_net::process::Command;
#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize, Deserialize)]
pub struct ForwardSearchOptions {
pub executable: Option<String>,
pub args: Option<Vec<String>>,
}
#[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 async fn search<'a>(
tex_file: &'a Path,
parent: &'a Path,
line_number: u64,
options: ForwardSearchOptions,
) -> Option<ForwardSearchResult> {
if options.executable.is_none() || options.args.is_none() {
return Some(ForwardSearchResult {
status: ForwardSearchStatus::Unconfigured,
});
}
let pdf_file = parent
.parent()?
.join(format!("{}.pdf", parent.file_stem()?.to_str()?));
let args: Vec<String> = options
.args
.unwrap()
.into_iter()
.flat_map(|arg| replace_placeholder(&tex_file, &pdf_file, line_number, arg))
.collect();
let status = match spawn_process(options.executable.unwrap(), args).await {
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: u64,
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.to_string())
};
Some(result)
}
async fn spawn_process(executable: String, args: Vec<String>) -> io::Result<()> {
Command::new(executable)
.args(args)
.stdout(Stdio::null())
.stderr(Stdio::null())
.spawn()?
.await?;
Ok(())
}
|