mirror of
https://github.com/latex-lsp/texlab.git
synced 2025-08-04 18:58:31 +00:00
Refactor textDocument/build request
This commit is contained in:
parent
ab36f6f712
commit
2323ef7f85
9 changed files with 217 additions and 297 deletions
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -1522,7 +1522,6 @@ dependencies = [
|
|||
"typed-builder",
|
||||
"unicode-normalization",
|
||||
"url",
|
||||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1685,15 +1684,6 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
|
|
|
@ -70,7 +70,6 @@ titlecase = "2.2.0"
|
|||
typed-builder = "0.10.0"
|
||||
unicode-normalization = "0.1.22"
|
||||
url = { version = "2.3.1", features = ["serde"] }
|
||||
uuid = { version = "1.1.2", features = ["v4"] }
|
||||
|
||||
[dependencies.derive_more]
|
||||
version = "0.99.17"
|
||||
|
|
|
@ -11,6 +11,7 @@ mod hover;
|
|||
mod inlay_hint;
|
||||
mod link;
|
||||
mod lsp_kinds;
|
||||
pub(crate) mod progress;
|
||||
mod reference;
|
||||
mod rename;
|
||||
mod symbol;
|
||||
|
@ -22,7 +23,7 @@ use lsp_types::Url;
|
|||
use crate::{Document, Workspace};
|
||||
|
||||
pub use self::{
|
||||
build::{BuildEngine, BuildParams, BuildResult, BuildStatus},
|
||||
build::{BuildParams, BuildResult, BuildRunner, BuildStatus},
|
||||
completion::{complete, CompletionItemData, COMPLETION_LIMIT},
|
||||
definition::goto_definition,
|
||||
execute_command::execute_command,
|
||||
|
|
|
@ -2,27 +2,19 @@ use std::{
|
|||
io::{BufRead, BufReader, Read},
|
||||
path::Path,
|
||||
process::{Command, Stdio},
|
||||
sync::{Arc, Mutex},
|
||||
thread::{self, JoinHandle},
|
||||
thread::JoinHandle,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
use dashmap::DashMap;
|
||||
use anyhow::{Ok, Result};
|
||||
use encoding_rs_io::DecodeReaderBytesBuilder;
|
||||
use lsp_types::{
|
||||
notification::{LogMessage, Progress},
|
||||
LogMessageParams, NumberOrString, Position, ProgressParams, ProgressParamsValue,
|
||||
TextDocumentIdentifier, Url, WorkDoneProgress, WorkDoneProgressBegin,
|
||||
WorkDoneProgressCreateParams, WorkDoneProgressEnd,
|
||||
};
|
||||
use lsp_types::{notification::LogMessage, LogMessageParams, TextDocumentIdentifier, Url};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_repr::{Deserialize_repr, Serialize_repr};
|
||||
use uuid::Uuid;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
use crate::{client::LspClient, ClientCapabilitiesExt, DocumentLanguage};
|
||||
use crate::{client::LspClient, DocumentLanguage, Workspace};
|
||||
|
||||
use super::{FeatureRequest, ForwardSearch};
|
||||
use super::progress::ProgressReporter;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
@ -45,196 +37,93 @@ pub struct BuildResult {
|
|||
pub status: BuildStatus,
|
||||
}
|
||||
|
||||
struct ProgressReporter<'a> {
|
||||
supports_progress: bool,
|
||||
client: LspClient,
|
||||
token: &'a str,
|
||||
#[derive(TypedBuilder)]
|
||||
pub struct BuildRunner<'a> {
|
||||
executable: &'a str,
|
||||
args: &'a [String],
|
||||
workspace: &'a Workspace,
|
||||
tex_uri: &'a Url,
|
||||
client: &'a LspClient,
|
||||
report_progress: bool,
|
||||
}
|
||||
|
||||
impl<'a> ProgressReporter<'a> {
|
||||
pub fn start(&self, uri: &Url) -> Result<()> {
|
||||
if self.supports_progress {
|
||||
self.client
|
||||
.send_request::<lsp_types::request::WorkDoneProgressCreate>(
|
||||
WorkDoneProgressCreateParams {
|
||||
token: NumberOrString::String(self.token.to_string()),
|
||||
},
|
||||
)?;
|
||||
|
||||
self.client.send_notification::<Progress>(ProgressParams {
|
||||
token: NumberOrString::String(self.token.to_string()),
|
||||
value: ProgressParamsValue::WorkDone(WorkDoneProgress::Begin(
|
||||
WorkDoneProgressBegin {
|
||||
title: "Building".to_string(),
|
||||
message: Some(uri.as_str().to_string()),
|
||||
cancellable: Some(false),
|
||||
percentage: None,
|
||||
},
|
||||
)),
|
||||
})?;
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for ProgressReporter<'a> {
|
||||
fn drop(&mut self) {
|
||||
if self.supports_progress {
|
||||
drop(self.client.send_notification::<Progress>(ProgressParams {
|
||||
token: NumberOrString::String(self.token.to_string()),
|
||||
value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(WorkDoneProgressEnd {
|
||||
message: None,
|
||||
})),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct BuildEngine {
|
||||
lock: Mutex<()>,
|
||||
pub positions_by_uri: DashMap<Arc<Url>, Position>,
|
||||
}
|
||||
|
||||
impl BuildEngine {
|
||||
pub fn build(
|
||||
&self,
|
||||
request: FeatureRequest<BuildParams>,
|
||||
client: LspClient,
|
||||
) -> Result<BuildResult> {
|
||||
let lock = self.lock.lock().unwrap();
|
||||
|
||||
let document = request
|
||||
impl<'a> BuildRunner<'a> {
|
||||
pub fn run(self) -> Result<BuildStatus> {
|
||||
let path = match self
|
||||
.workspace
|
||||
.iter()
|
||||
.find(|document| {
|
||||
if let Some(data) = document.data().as_latex() {
|
||||
data.extras.has_document_environment
|
||||
} else {
|
||||
false
|
||||
}
|
||||
document
|
||||
.data()
|
||||
.as_latex()
|
||||
.map_or(false, |document| document.extras.can_be_built)
|
||||
})
|
||||
.unwrap_or_else(|| request.main_document());
|
||||
|
||||
if document.data().language() != DocumentLanguage::Latex {
|
||||
return Ok(BuildResult {
|
||||
status: BuildStatus::SUCCESS,
|
||||
});
|
||||
}
|
||||
|
||||
if document.uri().scheme() != "file" {
|
||||
return Ok(BuildResult {
|
||||
status: BuildStatus::FAILURE,
|
||||
});
|
||||
}
|
||||
let path = document.uri().to_file_path().unwrap();
|
||||
|
||||
let supports_progress = request
|
||||
.workspace
|
||||
.environment
|
||||
.client_capabilities
|
||||
.has_work_done_progress_support();
|
||||
|
||||
let token = format!("texlab-build-{}", Uuid::new_v4());
|
||||
let progress_reporter = ProgressReporter {
|
||||
supports_progress,
|
||||
client: client.clone(),
|
||||
token: &token,
|
||||
.or_else(|| self.workspace.get(self.tex_uri))
|
||||
.filter(|document| document.data().language() == DocumentLanguage::Latex)
|
||||
.filter(|document| document.uri().scheme() == "file")
|
||||
.and_then(|document| document.uri().to_file_path().ok())
|
||||
{
|
||||
Some(path) => path,
|
||||
None => return Ok(BuildStatus::FAILURE),
|
||||
};
|
||||
progress_reporter.start(document.uri())?;
|
||||
|
||||
let options = &request.workspace.environment.options;
|
||||
let reporter = if self.report_progress {
|
||||
Some(ProgressReporter::new(
|
||||
self.client,
|
||||
"Building".to_string(),
|
||||
path.display().to_string(),
|
||||
)?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let build_dir = options
|
||||
.root_directory
|
||||
.as_ref()
|
||||
.map(AsRef::as_ref)
|
||||
.or_else(|| path.parent())
|
||||
.unwrap();
|
||||
|
||||
let args: Vec<_> = options
|
||||
.build
|
||||
let args: Vec<_> = self
|
||||
.args
|
||||
.0
|
||||
.iter()
|
||||
.map(|arg| replace_placeholder(arg.clone(), &path))
|
||||
.collect();
|
||||
|
||||
let mut process = Command::new(&options.build.executable.0)
|
||||
let mut process = Command::new(self.executable)
|
||||
.args(args)
|
||||
.stdin(Stdio::null())
|
||||
.stdout(Stdio::piped())
|
||||
.current_dir(path.parent().unwrap())
|
||||
.stderr(Stdio::piped())
|
||||
.current_dir(build_dir)
|
||||
.stdout(Stdio::piped())
|
||||
.stdin(Stdio::null())
|
||||
.spawn()?;
|
||||
|
||||
let (exit_sender, exit_receiver) = crossbeam_channel::bounded(1);
|
||||
let log_handle = capture_output(&mut process, client, exit_receiver);
|
||||
let success = process.wait().map(|status| status.success())?;
|
||||
exit_sender.send(())?;
|
||||
drop(exit_sender);
|
||||
self.track_output(process.stdout.take().unwrap());
|
||||
self.track_output(process.stderr.take().unwrap());
|
||||
|
||||
log_handle.join().unwrap();
|
||||
let status = if success {
|
||||
let status = if process.wait()?.success() {
|
||||
BuildStatus::SUCCESS
|
||||
} else {
|
||||
BuildStatus::ERROR
|
||||
BuildStatus::FAILURE
|
||||
};
|
||||
|
||||
drop(progress_reporter);
|
||||
drop(lock);
|
||||
|
||||
if let Some((executable, args)) = options
|
||||
.forward_search
|
||||
.executable
|
||||
.as_deref()
|
||||
.zip(options.forward_search.args.as_deref())
|
||||
.filter(|_| options.build.forward_search_after)
|
||||
{
|
||||
let position = self
|
||||
.positions_by_uri
|
||||
.get(&request.uri)
|
||||
.map(|entry| *entry.value())
|
||||
.unwrap_or_default();
|
||||
|
||||
ForwardSearch::builder()
|
||||
.executable(executable)
|
||||
.args(args)
|
||||
.line(position.line)
|
||||
.workspace(&request.workspace)
|
||||
.tex_uri(&request.uri)
|
||||
.build()
|
||||
.execute();
|
||||
}
|
||||
|
||||
Ok(BuildResult { status })
|
||||
drop(reporter);
|
||||
Ok(status)
|
||||
}
|
||||
}
|
||||
|
||||
fn capture_output(
|
||||
process: &mut std::process::Child,
|
||||
client: LspClient,
|
||||
exit_receiver: Receiver<()>,
|
||||
) -> JoinHandle<()> {
|
||||
let (log_sender, log_receiver) = crossbeam_channel::unbounded();
|
||||
track_output(process.stdout.take().unwrap(), log_sender.clone());
|
||||
track_output(process.stderr.take().unwrap(), log_sender);
|
||||
thread::spawn(move || loop {
|
||||
crossbeam_channel::select! {
|
||||
recv(&log_receiver) -> message => {
|
||||
if let Ok(message) = message {
|
||||
client.send_notification::<LogMessage>(
|
||||
LogMessageParams {
|
||||
message,
|
||||
typ: lsp_types::MessageType::LOG,
|
||||
},
|
||||
)
|
||||
fn track_output(&self, output: impl Read + Send + 'static) -> JoinHandle<()> {
|
||||
let client = self.client.clone();
|
||||
let reader = BufReader::new(
|
||||
DecodeReaderBytesBuilder::new()
|
||||
.encoding(Some(encoding_rs::UTF_8))
|
||||
.utf8_passthru(true)
|
||||
.strip_bom(true)
|
||||
.build(output),
|
||||
);
|
||||
|
||||
std::thread::spawn(move || {
|
||||
for line in reader.lines() {
|
||||
let message = line.unwrap();
|
||||
let typ = lsp_types::MessageType::LOG;
|
||||
client
|
||||
.send_notification::<LogMessage>(LogMessageParams { message, typ })
|
||||
.unwrap();
|
||||
}
|
||||
},
|
||||
recv(&exit_receiver) -> _ => break,
|
||||
};
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn replace_placeholder(arg: String, file: &Path) -> String {
|
||||
|
@ -244,19 +133,3 @@ fn replace_placeholder(arg: String, file: &Path) -> String {
|
|||
arg.replace("%f", &file.to_string_lossy())
|
||||
}
|
||||
}
|
||||
|
||||
fn track_output(output: impl Read + Send + 'static, sender: Sender<String>) -> JoinHandle<()> {
|
||||
let reader = BufReader::new(
|
||||
DecodeReaderBytesBuilder::new()
|
||||
.encoding(Some(encoding_rs::UTF_8))
|
||||
.utf8_passthru(true)
|
||||
.strip_bom(true)
|
||||
.build(output),
|
||||
);
|
||||
|
||||
thread::spawn(move || {
|
||||
for line in reader.lines() {
|
||||
sender.send(line.unwrap()).unwrap();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -37,22 +37,15 @@ pub struct ForwardSearch<'a> {
|
|||
}
|
||||
|
||||
impl<'a> ForwardSearch<'a> {
|
||||
pub fn execute(self) -> Option<ForwardSearchResult> {
|
||||
pub fn execute(self) -> Option<ForwardSearchStatus> {
|
||||
let root_document = self
|
||||
.workspace
|
||||
.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
|
||||
}
|
||||
document
|
||||
.data()
|
||||
.as_latex()
|
||||
.map_or(false, |data| data.extras.can_be_root)
|
||||
})
|
||||
.filter(|document| document.uri().scheme() == "file")?;
|
||||
|
||||
|
@ -81,7 +74,7 @@ impl<'a> ForwardSearch<'a> {
|
|||
}
|
||||
};
|
||||
|
||||
Some(ForwardSearchResult { status })
|
||||
Some(status)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
51
src/features/progress.rs
Normal file
51
src/features/progress.rs
Normal file
|
@ -0,0 +1,51 @@
|
|||
use std::sync::atomic::{AtomicI32, Ordering};
|
||||
|
||||
use anyhow::Result;
|
||||
use lsp_types::{
|
||||
notification::Progress, NumberOrString, ProgressParams, ProgressParamsValue, WorkDoneProgress,
|
||||
WorkDoneProgressBegin, WorkDoneProgressCreateParams, WorkDoneProgressEnd,
|
||||
};
|
||||
|
||||
use crate::client::LspClient;
|
||||
|
||||
static NEXT_TOKEN: AtomicI32 = AtomicI32::new(1);
|
||||
|
||||
pub struct ProgressReporter<'a> {
|
||||
client: &'a LspClient,
|
||||
token: i32,
|
||||
}
|
||||
|
||||
impl<'a> ProgressReporter<'a> {
|
||||
pub fn new(client: &'a LspClient, title: String, message: String) -> Result<Self> {
|
||||
let token = NEXT_TOKEN.fetch_add(1, Ordering::SeqCst);
|
||||
|
||||
client.send_request::<lsp_types::request::WorkDoneProgressCreate>(
|
||||
WorkDoneProgressCreateParams {
|
||||
token: NumberOrString::Number(token),
|
||||
},
|
||||
)?;
|
||||
|
||||
client.send_notification::<Progress>(ProgressParams {
|
||||
token: NumberOrString::Number(token),
|
||||
value: ProgressParamsValue::WorkDone(WorkDoneProgress::Begin(WorkDoneProgressBegin {
|
||||
title,
|
||||
message: Some(message),
|
||||
cancellable: Some(false),
|
||||
percentage: None,
|
||||
})),
|
||||
})?;
|
||||
|
||||
Ok(Self { client, token })
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for ProgressReporter<'a> {
|
||||
fn drop(&mut self) {
|
||||
let _ = self.client.send_notification::<Progress>(ProgressParams {
|
||||
token: NumberOrString::String(self.token.to_string()),
|
||||
value: ProgressParamsValue::WorkDone(WorkDoneProgress::End(WorkDoneProgressEnd {
|
||||
message: None,
|
||||
})),
|
||||
});
|
||||
}
|
||||
}
|
160
src/server.rs
160
src/server.rs
|
@ -5,6 +5,7 @@ use std::{
|
|||
|
||||
use anyhow::Result;
|
||||
use crossbeam_channel::{Receiver, Sender};
|
||||
use dashmap::DashMap;
|
||||
use log::{error, info, warn};
|
||||
use lsp_server::{Connection, Message, RequestId};
|
||||
use lsp_types::{notification::*, request::*, *};
|
||||
|
@ -23,8 +24,8 @@ use crate::{
|
|||
features::{
|
||||
execute_command, find_all_references, find_document_highlights, find_document_links,
|
||||
find_document_symbols, find_foldings, find_hover, find_inlay_hints, find_workspace_symbols,
|
||||
format_source_code, goto_definition, prepare_rename_all, rename_all, BuildEngine,
|
||||
BuildParams, BuildResult, BuildStatus, CompletionItemData, FeatureRequest, ForwardSearch,
|
||||
format_source_code, goto_definition, prepare_rename_all, rename_all, BuildParams,
|
||||
BuildResult, BuildRunner, BuildStatus, CompletionItemData, FeatureRequest, ForwardSearch,
|
||||
ForwardSearchResult, ForwardSearchStatus,
|
||||
},
|
||||
normalize_uri,
|
||||
|
@ -33,6 +34,8 @@ use crate::{
|
|||
LineIndexExt, Options, StartupOptions, Workspace, WorkspaceEvent,
|
||||
};
|
||||
|
||||
static BUILD_LOCK: Mutex<()> = Mutex::new(());
|
||||
|
||||
#[derive(Debug)]
|
||||
enum InternalMessage {
|
||||
SetDistro(Distribution),
|
||||
|
@ -47,7 +50,7 @@ struct ServerSnapshot {
|
|||
workspace: Workspace,
|
||||
diagnostic_tx: debouncer::Sender<Workspace>,
|
||||
diagnostic_manager: DiagnosticManager,
|
||||
build_engine: Arc<BuildEngine>,
|
||||
cursor_positions: Arc<DashMap<Arc<Url>, Position>>,
|
||||
}
|
||||
|
||||
impl ServerSnapshot {
|
||||
|
@ -111,6 +114,49 @@ impl ServerSnapshot {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn build(&self, uri: &Url) -> BuildResult {
|
||||
let guard = BUILD_LOCK.lock().unwrap();
|
||||
|
||||
let client_capabilities = &self.workspace.environment.client_capabilities;
|
||||
let options = &self.workspace.environment.options.build;
|
||||
let status = BuildRunner::builder()
|
||||
.executable(&options.executable.0)
|
||||
.args(&options.args.0)
|
||||
.tex_uri(uri)
|
||||
.client(&self.client)
|
||||
.report_progress(client_capabilities.has_work_done_progress_support())
|
||||
.workspace(&self.workspace)
|
||||
.build()
|
||||
.run()
|
||||
.unwrap_or(BuildStatus::FAILURE);
|
||||
|
||||
if options.forward_search_after {
|
||||
let line = self.cursor_positions.get(uri).map_or(0, |pos| pos.line);
|
||||
self.forward_search(uri, line);
|
||||
}
|
||||
|
||||
drop(guard);
|
||||
BuildResult { status }
|
||||
}
|
||||
|
||||
fn forward_search(&self, uri: &Url, line: u32) -> ForwardSearchResult {
|
||||
let options = &self.workspace.environment.options.forward_search;
|
||||
let status = match options.executable.as_deref().zip(options.args.as_deref()) {
|
||||
Some((executable, args)) => ForwardSearch::builder()
|
||||
.executable(executable)
|
||||
.args(args)
|
||||
.line(line)
|
||||
.workspace(&self.workspace)
|
||||
.tex_uri(uri)
|
||||
.build()
|
||||
.execute()
|
||||
.unwrap_or(ForwardSearchStatus::ERROR),
|
||||
None => ForwardSearchStatus::UNCONFIGURED,
|
||||
};
|
||||
|
||||
ForwardSearchResult { status }
|
||||
}
|
||||
|
||||
fn feature_request<P>(&self, uri: Arc<Url>, params: P) -> FeatureRequest<P> {
|
||||
FeatureRequest {
|
||||
params,
|
||||
|
@ -129,7 +175,7 @@ pub struct Server {
|
|||
diagnostic_tx: debouncer::Sender<Workspace>,
|
||||
diagnostic_manager: DiagnosticManager,
|
||||
pool: Arc<Mutex<ThreadPool>>,
|
||||
build_engine: Arc<BuildEngine>,
|
||||
cursor_positions: Arc<DashMap<Arc<Url>, Position>>,
|
||||
}
|
||||
|
||||
impl Server {
|
||||
|
@ -147,8 +193,8 @@ impl Server {
|
|||
workspace,
|
||||
diagnostic_tx,
|
||||
diagnostic_manager,
|
||||
cursor_positions: Arc::default(),
|
||||
pool: Arc::new(Mutex::new(threadpool::Builder::new().build())),
|
||||
build_engine: Arc::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -160,7 +206,7 @@ impl Server {
|
|||
client: self.client.clone(),
|
||||
diagnostic_tx: self.diagnostic_tx.clone(),
|
||||
diagnostic_manager: self.diagnostic_manager.clone(),
|
||||
build_engine: Arc::clone(&self.build_engine),
|
||||
cursor_positions: Arc::clone(&self.cursor_positions),
|
||||
};
|
||||
|
||||
self.pool.lock().unwrap().execute(move || job(server));
|
||||
|
@ -359,7 +405,7 @@ impl Server {
|
|||
.viewport
|
||||
.insert(Arc::clone(new_document.uri()));
|
||||
|
||||
self.build_engine.positions_by_uri.insert(
|
||||
self.cursor_positions.insert(
|
||||
Arc::clone(&uri),
|
||||
Position::new(
|
||||
old_document
|
||||
|
@ -374,7 +420,7 @@ impl Server {
|
|||
|
||||
if self.workspace.environment.options.chktex.on_edit {
|
||||
self.run_chktex(new_document);
|
||||
};
|
||||
}
|
||||
}
|
||||
None => match uri.to_file_path() {
|
||||
Ok(path) => {
|
||||
|
@ -390,38 +436,19 @@ impl Server {
|
|||
fn did_save(&mut self, params: DidSaveTextDocumentParams) -> Result<()> {
|
||||
let mut uri = params.text_document.uri;
|
||||
normalize_uri(&mut uri);
|
||||
let document = match self.workspace.get(&uri) {
|
||||
Some(document) => document,
|
||||
_ => return Ok(()),
|
||||
};
|
||||
|
||||
if let Some(request) = self
|
||||
.workspace
|
||||
.get(&uri)
|
||||
.filter(|_| self.workspace.environment.options.build.on_save)
|
||||
.map(|document| {
|
||||
self.feature_request(
|
||||
Arc::clone(document.uri()),
|
||||
BuildParams {
|
||||
text_document: TextDocumentIdentifier::new(uri.clone()),
|
||||
},
|
||||
)
|
||||
})
|
||||
{
|
||||
if self.workspace.environment.options.build.on_save {
|
||||
let uri = Arc::clone(document.uri());
|
||||
self.spawn(move |server| {
|
||||
server
|
||||
.build_engine
|
||||
.build(request, server.client)
|
||||
.unwrap_or_else(|why| {
|
||||
error!("Build failed: {}", why);
|
||||
BuildResult {
|
||||
status: BuildStatus::FAILURE,
|
||||
}
|
||||
});
|
||||
server.build(&uri);
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(document) = self
|
||||
.workspace
|
||||
.get(&uri)
|
||||
.filter(|_| self.workspace.environment.options.chktex.on_open_and_save)
|
||||
{
|
||||
if self.workspace.environment.options.chktex.on_open_and_save {
|
||||
self.run_chktex(document);
|
||||
}
|
||||
|
||||
|
@ -448,14 +475,6 @@ impl Server {
|
|||
});
|
||||
}
|
||||
|
||||
fn feature_request<P>(&self, uri: Arc<Url>, params: P) -> FeatureRequest<P> {
|
||||
FeatureRequest {
|
||||
params,
|
||||
workspace: self.workspace.slice(&uri),
|
||||
uri,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_feature_request<P, R, H>(
|
||||
&self,
|
||||
id: RequestId,
|
||||
|
@ -518,8 +537,7 @@ impl Server {
|
|||
normalize_uri(&mut params.text_document_position.text_document.uri);
|
||||
let uri = Arc::new(params.text_document_position.text_document.uri.clone());
|
||||
|
||||
self.build_engine
|
||||
.positions_by_uri
|
||||
self.cursor_positions
|
||||
.insert(Arc::clone(&uri), params.text_document_position.position);
|
||||
|
||||
self.handle_feature_request(id, params, uri, crate::features::complete)?;
|
||||
|
@ -587,7 +605,8 @@ impl Server {
|
|||
.uri
|
||||
.clone(),
|
||||
);
|
||||
self.build_engine.positions_by_uri.insert(
|
||||
|
||||
self.cursor_positions.insert(
|
||||
Arc::clone(&uri),
|
||||
params.text_document_position_params.position,
|
||||
);
|
||||
|
@ -684,42 +703,29 @@ impl Server {
|
|||
|
||||
fn build(&self, id: RequestId, mut params: BuildParams) -> Result<()> {
|
||||
normalize_uri(&mut params.text_document.uri);
|
||||
let uri = Arc::new(params.text_document.uri.clone());
|
||||
let client = self.client.clone();
|
||||
let build_engine = Arc::clone(&self.build_engine);
|
||||
self.handle_feature_request(id, params, uri, move |request| {
|
||||
build_engine.build(request, client).unwrap_or_else(|why| {
|
||||
error!("Build failed: {}", why);
|
||||
BuildResult {
|
||||
status: BuildStatus::FAILURE,
|
||||
}
|
||||
})
|
||||
})?;
|
||||
self.spawn(move |server| {
|
||||
let result = server.build(¶ms.text_document.uri);
|
||||
server
|
||||
.connection
|
||||
.sender
|
||||
.send(lsp_server::Response::new_ok(id, result).into())
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn forward_search(&self, id: RequestId, mut params: TextDocumentPositionParams) -> Result<()> {
|
||||
normalize_uri(&mut params.text_document.uri);
|
||||
let uri = Arc::new(params.text_document.uri.clone());
|
||||
self.handle_feature_request(id, params, uri, |req| {
|
||||
let options = &req.workspace.environment.options.forward_search;
|
||||
match options.executable.as_deref().zip(options.args.as_deref()) {
|
||||
Some((executable, args)) => ForwardSearch::builder()
|
||||
.executable(executable)
|
||||
.args(args)
|
||||
.line(req.params.position.line)
|
||||
.workspace(&req.workspace)
|
||||
.tex_uri(&req.uri)
|
||||
.build()
|
||||
.execute()
|
||||
.unwrap_or(ForwardSearchResult {
|
||||
status: ForwardSearchStatus::ERROR,
|
||||
}),
|
||||
None => ForwardSearchResult {
|
||||
status: ForwardSearchStatus::UNCONFIGURED,
|
||||
},
|
||||
}
|
||||
})?;
|
||||
self.spawn(move |server| {
|
||||
let result = server.forward_search(¶ms.text_document.uri, params.position.line);
|
||||
server
|
||||
.connection
|
||||
.sender
|
||||
.send(lsp_server::Response::new_ok(id, result).into())
|
||||
.unwrap();
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -45,9 +45,15 @@ pub fn analyze(context: &mut LatexAnalyzerContext, root: &latex::SyntaxNode) {
|
|||
.filter_map(ExplicitLink::as_component_name)
|
||||
.any(|name| name == "subfiles.cls");
|
||||
|
||||
context.extras.can_be_built = context
|
||||
.extras
|
||||
.explicit_links
|
||||
.iter()
|
||||
.any(|link| link.kind == ExplicitLinkKind::Class);
|
||||
|
||||
context.extras.can_be_root = context
|
||||
.extras
|
||||
.explicit_links
|
||||
.iter()
|
||||
.any(|link| link.kind == ExplicitLinkKind::Class && link.stem.as_str() != "subfiles")
|
||||
.any(|link| link.kind == ExplicitLinkKind::Class && link.stem.as_str() != "subfiles");
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ pub struct Extras {
|
|||
pub label_numbers_by_name: FxHashMap<String, String>,
|
||||
pub theorem_environments: Vec<TheoremEnvironment>,
|
||||
pub graphics_paths: FxHashSet<String>,
|
||||
pub can_be_built: bool,
|
||||
pub can_be_root: bool,
|
||||
pub has_document_environment: bool,
|
||||
pub has_subfiles_package: bool,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue