Add server command to clean build directory

See #607.
This commit is contained in:
Patrick Förster 2022-05-27 18:24:13 +02:00
parent 5657aed565
commit 6edb7d4aa7
5 changed files with 158 additions and 5 deletions

21
docs/custom_commands.md Normal file
View file

@ -0,0 +1,21 @@
# Custom Commands
The server provides the following commands through the `workspace/executeCommand` request:
## texlab.cleanAuxiliary
Removes the auxiliary files produced by compiling the specified LaTeX document.
At the moment, this command simply calls `latexmk -c` with the currently configured output directory.
Parameters:
- `document`: `TextDocumentIdentifier` (_Required_)
## texlab.cleanArtifacts
Removes the auxiliary files and the artifacts produced by compiling the specified LaTeX document.
At the moment, this command simply calls `latexmk -C` with the currently configured output directory.
Parameters:
- `document`: `TextDocumentIdentifier` (_Required_)

View file

@ -3,6 +3,7 @@ mod build;
mod completion;
mod cursor;
mod definition;
mod execute_command;
mod folding;
mod formatting;
mod forward_search;
@ -25,6 +26,7 @@ pub use self::completion::{complete, CompletionItemData, COMPLETION_LIMIT};
pub use self::{
build::{BuildEngine, BuildParams, BuildResult, BuildStatus},
definition::goto_definition,
execute_command::execute_command,
folding::find_foldings,
formatting::format_source_code,
forward_search::{execute_forward_search, ForwardSearchResult, ForwardSearchStatus},

View file

@ -0,0 +1,103 @@
use std::{path::PathBuf, process::Stdio, sync::Arc};
use anyhow::Result;
use lsp_types::{TextDocumentIdentifier, Url};
use crate::Workspace;
pub fn execute_command(
workspace: &Workspace,
name: &str,
args: Vec<serde_json::Value>,
) -> Result<()> {
match name {
"texlab.cleanAuxiliary" => {
let params = args
.into_iter()
.next()
.ok_or_else(|| anyhow::anyhow!("texlab.cleanAuxiliary requires one argument"))?;
clean_output_files(workspace, CleanOptions::Auxiliary, params)?;
}
"texlab.cleanArtifacts" => {
let params = args
.into_iter()
.next()
.ok_or_else(|| anyhow::anyhow!("texlab.cleanArtifacts requires one argument"))?;
clean_output_files(workspace, CleanOptions::Artifacts, params)?;
}
_ => anyhow::bail!("Unknown command: {}", name),
}
Ok(())
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
enum CleanOptions {
Auxiliary,
Artifacts,
}
fn clean_output_files(
workspace: &Workspace,
options: CleanOptions,
params: serde_json::Value,
) -> Result<()> {
let params: TextDocumentIdentifier = serde_json::from_value(params)?;
let uri = workspace
.find_parent(&params.uri)
.map(|document| document.uri)
.unwrap_or_else(|| Arc::new(params.uri));
if let Some(cx) = BuildContext::find(workspace, &uri) {
let flag = match options {
CleanOptions::Auxiliary => "-c",
CleanOptions::Artifacts => "-C",
};
std::process::Command::new("latexmk")
.arg(format!("-outdir={}", cx.output_dir.to_string_lossy()))
.arg(flag)
.arg(cx.input_file)
.stdin(Stdio::null())
.stdout(Stdio::null())
.stderr(Stdio::null())
.status()?;
}
Ok(())
}
struct BuildContext {
input_file: PathBuf,
output_dir: PathBuf,
}
impl BuildContext {
pub fn find(workspace: &Workspace, uri: &Url) -> Option<Self> {
if uri.scheme() != "file" {
return None;
}
let input_file = uri.to_file_path().ok()?;
let options = &workspace.environment.options;
let current_dir = &workspace.environment.current_directory;
let output_dir = match (
options.root_directory.as_ref(),
options.aux_directory.as_ref(),
) {
(_, Some(aux_dir)) => current_dir.join(aux_dir),
(Some(root_dir), None) => current_dir.join(root_dir),
(None, None) => input_file.parent()?.to_path_buf(),
};
log::info!("Output = {:#?}", output_dir);
Some(Self {
input_file,
output_dir,
})
}
}

View file

@ -17,10 +17,11 @@ use crate::{
dispatch::{NotificationDispatcher, RequestDispatcher},
distro::Distribution,
features::{
find_all_references, find_document_highlights, find_document_links, find_document_symbols,
find_foldings, find_hover, find_workspace_symbols, format_source_code, goto_definition,
prepare_rename_all, rename_all, BuildEngine, BuildParams, BuildResult, BuildStatus,
FeatureRequest, ForwardSearchResult, ForwardSearchStatus,
execute_command, find_all_references, find_document_highlights, find_document_links,
find_document_symbols, find_foldings, find_hover, find_workspace_symbols,
format_source_code, goto_definition, prepare_rename_all, rename_all, BuildEngine,
BuildParams, BuildResult, BuildStatus, FeatureRequest, ForwardSearchResult,
ForwardSearchStatus,
},
req_queue::{IncomingData, ReqQueue},
ClientCapabilitiesExt, DocumentLanguage, Environment, LineIndex, LineIndexExt, Options,
@ -127,6 +128,13 @@ impl Server {
})),
document_highlight_provider: Some(OneOf::Left(true)),
document_formatting_provider: Some(OneOf::Left(true)),
execute_command_provider: Some(ExecuteCommandOptions {
commands: vec![
"texlab.cleanAuxiliary".into(),
"texlab.cleanArtifacts".into(),
],
..Default::default()
}),
..ServerCapabilities::default()
}
}
@ -668,6 +676,24 @@ impl Server {
Ok(())
}
fn execute_command(&self, id: RequestId, params: ExecuteCommandParams) -> Result<()> {
self.spawn(move |server| {
let result = execute_command(&server.workspace, &params.command, params.arguments);
let response = match result {
Ok(()) => lsp_server::Response::new_ok(id, ()),
Err(why) => lsp_server::Response::new_err(
id,
lsp_server::ErrorCode::InternalError as i32,
why.to_string(),
),
};
server.connection.sender.send(response.into()).unwrap();
});
Ok(())
}
fn semantic_tokens_range(
&self,
_id: RequestId,
@ -765,6 +791,7 @@ impl Server {
.on::<ForwardSearchRequest, _>(|id, params| {
self.forward_search(id, params)
})?
.on::<ExecuteCommand,_>(|id, params| self.execute_command(id, params))?
.on::<SemanticTokensRangeRequest, _>(|id, params| {
self.semantic_tokens_range(id, params)
})?

View file

@ -137,7 +137,7 @@ impl Workspace {
.unwrap_or_default()
}
fn find_parent(&self, uri: &Url) -> Option<Document> {
pub fn find_parent(&self, uri: &Url) -> Option<Document> {
self.slice(uri)
.documents_by_uri
.values()