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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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(())
}
|