Skip to content

Commit

Permalink
Implement build request
Browse files Browse the repository at this point in the history
  • Loading branch information
pfoerster committed Jun 1, 2019
1 parent 3c80d5f commit 45b5f6b
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 14 deletions.
14 changes: 11 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ jsonrpc = { path = "jsonrpc" }
jsonrpc-derive = { path = "jsonrpc_derive" }
lazy_static = "1.3.0"
log = "0.4.6"
lsp-types = { git = "https://github.com/latex-lsp/lsp-types", rev = "8ce88a90c6753cf10d4b97d8cd6c9b8bb663c9d1" }
lsp-types = { git = "https://github.com/latex-lsp/lsp-types", rev = "8ce88a90c6753cf10d4b97d8cd6c9b8bb663c9d1", features = ["proposed"] }
nom = "4.2.3"
path-clean = "0.1.0"
regex = "1.1.6"
reqwest = "0.9.16"
serde = { version = "1.0.90", features = ["derive", "rc"] }
serde_json = "1.0.39"
serde_repr = "0.1"
stderrlog = "0.4.1"
tempfile = "3"
tokio = "0.1"
Expand Down
1 change: 1 addition & 0 deletions src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub enum Action {
PublishDiagnostics,
RunLinter(Uri),
ParseLog { tex_uri: Uri, log_path: PathBuf },
Build(Uri),
}

#[derive(Debug, Default)]
Expand Down
138 changes: 138 additions & 0 deletions src/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
use crate::client::LspClient;
use crate::feature::{FeatureProvider, FeatureRequest};
use futures::prelude::*;
use futures_boxed::boxed;
use lsp_types::*;
use serde::{Deserialize, Serialize};
use serde_repr::*;
use std::borrow::Cow;
use std::io;
use std::io::BufRead;
use std::path::Path;
use std::process::{Command, Stdio};
use std::sync::Arc;

#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BuildParams {
pub text_document: TextDocumentIdentifier,
}

#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BuildOptions {
pub executable: String,
pub args: Vec<String>,
pub on_save: bool,
}

impl Default for BuildOptions {
fn default() -> Self {
Self {
executable: "latexmk".to_owned(),
args: vec![
"-pdf".to_owned(),
"-interaction=nonstopmode".to_owned(),
"-synctex=1".to_owned(),
],
on_save: false,
}
}
}

#[derive(Debug, PartialEq, Eq, Clone, Copy, Serialize_repr, Deserialize_repr)]
#[repr(i32)]
pub enum BuildStatus {
Success = 0,
Error = 1,
Failure = 2,
}

#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct BuildResult {
pub status: BuildStatus,
}

#[derive(Debug, PartialEq, Eq, Clone)]
pub struct BuildProvider<C> {
client: Arc<C>,
options: BuildOptions,
}

impl<C> BuildProvider<C>
where
C: LspClient + Send + Sync,
{
pub fn new(client: Arc<C>, options: BuildOptions) -> Self {
Self { client, options }
}

async fn build<'a>(&'a self, path: &'a Path) -> io::Result<bool> {
let mut options = self.options.clone();
let mut args = Vec::new();
args.append(&mut options.args);
args.push(path.to_string_lossy().into_owned());

let mut process = Command::new(options.executable)
.args(args)
.stdout(Stdio::piped())
.stderr(Stdio::inherit())
.current_dir(path.parent().unwrap())
.spawn()?;

let stdout = process.stdout.as_mut().unwrap();
let mut reader = io::BufReader::new(stdout);
loop {
let mut line = String::new();
let count = reader.read_line(&mut line)?;
if count == 0 {
break;
}
let params = LogMessageParams {
typ: MessageType::Log,
message: Cow::from(line.trim().to_owned()),
};
self.client.log_message(params).await;
}

Ok(process.wait()?.success())
}
}

impl<C> FeatureProvider for BuildProvider<C>
where
C: LspClient + Send + Sync,
{
type Params = BuildParams;
type Output = BuildResult;

#[boxed]
async fn execute<'a>(&'a self, request: &'a FeatureRequest<BuildParams>) -> BuildResult {
let document = request
.workspace
.find_parent(&request.document.uri)
.unwrap();
let path = document.uri.to_file_path().unwrap();

let title = path.file_name().unwrap().to_string_lossy().into_owned();
let mut progress_params = ProgressParams {
id: Cow::from("build"),
title: Cow::from(title),
message: Some(Cow::from("Building...")),
percentage: None,
done: None,
};
self.client.progress(progress_params.clone()).await;

let status = match self.build(&path).await {
Ok(true) => BuildStatus::Success,
Ok(false) => BuildStatus::Error,
Err(_) => BuildStatus::Failure,
};

progress_params.done = Some(true);
self.client.progress(progress_params).await;
BuildResult { status }
}
}
12 changes: 12 additions & 0 deletions src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ pub trait LspClient {

#[jsonrpc_method("textDocument/publishDiagnostics", kind = "notification")]
fn publish_diagnostics(&self, params: PublishDiagnosticsParams) -> BoxFuture<'_, ()>;

#[jsonrpc_method("window/progress", kind = "notification")]
fn progress(&self, params: ProgressParams) -> BoxFuture<'_, ()>;

#[jsonrpc_method("window/logMessage", kind = "notification")]
fn log_message(&self, params: LogMessageParams) -> BoxFuture<'_, ()>;
}

#[derive(Debug, PartialEq, Eq, Clone, Default)]
Expand Down Expand Up @@ -78,4 +84,10 @@ impl LspClient for LspClientMock {
let mut diagnostics_by_uri = self.diagnostics_by_uri.lock().await;
diagnostics_by_uri.insert(params.uri, params.diagnostics);
}

#[boxed]
async fn progress(&self, _params: ProgressParams) {}

#[boxed]
async fn log_message(&self, _params: LogMessageParams) {}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#![recursion_limit = "128"]

pub mod action;
pub mod build;
pub mod client;
pub mod codec;
pub mod completion;
Expand Down
22 changes: 21 additions & 1 deletion src/server.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::action::{Action, ActionMananger};
use crate::build::*;
use crate::client::LspClient;
use crate::completion::factory::CompletionItemData;
use crate::completion::CompletionProvider;
Expand Down Expand Up @@ -124,6 +125,7 @@ impl<C: LspClient + Send + Sync> LatexLspServer<C> {
folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
execute_command_provider: None,
workspace: None,
selection_range_provider: None,
};

Ok(InitializeResult { capabilities })
Expand Down Expand Up @@ -185,8 +187,10 @@ impl<C: LspClient + Send + Sync> LatexLspServer<C> {
#[jsonrpc_method("textDocument/didSave", kind = "notification")]
pub fn did_save(&self, params: DidSaveTextDocumentParams) {
self.action_manager
.push(Action::RunLinter(params.text_document.uri));
.push(Action::RunLinter(params.text_document.uri.clone()));
self.action_manager.push(Action::PublishDiagnostics);
self.action_manager
.push(Action::Build(params.text_document.uri));
}

#[jsonrpc_method("textDocument/didClose", kind = "notification")]
Expand Down Expand Up @@ -308,6 +312,15 @@ impl<C: LspClient + Send + Sync> LatexLspServer<C> {
Ok(foldings)
}

#[jsonrpc_method("textDocument/build", kind = "request")]
pub async fn build(&self, params: BuildParams) -> Result<BuildResult> {
let request = request!(self, params)?;
let options = self.configuration::<BuildOptions>("latex.build").await;
let provider = BuildProvider::new(Arc::clone(&self.client), options);
let result = provider.execute(&request).await;
Ok(result)
}

async fn configuration<T>(&self, section: &'static str) -> T
where
T: DeserializeOwned + Default,
Expand Down Expand Up @@ -431,6 +444,13 @@ impl<C: LspClient + Send + Sync> jsonrpc::ActionHandler for LatexLspServer<C> {
diagnostics_manager.build.update(&tex_uri, &log);
}
}
Action::Build(uri) => {
let config: BuildOptions = self.configuration("latex.build").await;
if config.on_save {
let text_document = TextDocumentIdentifier::new(uri);
self.build(BuildParams { text_document }).await.unwrap();
}
}
}
}
}
Expand Down
21 changes: 12 additions & 9 deletions tests/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
use futures::executor::block_on;
use jsonrpc::server::ActionHandler;
use lsp_types::*;
use texlab::diagnostics::{BibtexErrorCode, LatexLintOptions};
use texlab::scenario::Scenario;
use texlab::diagnostics::{LatexLintOptions, BibtexErrorCode};

#[test]
fn test_lint_latex() {
Expand Down Expand Up @@ -52,7 +52,10 @@ fn test_lint_bibtex() {
let diagnostics_by_uri = scenario.client.diagnostics_by_uri.lock().await;
let diagnostics = diagnostics_by_uri.get(&scenario.uri("foo.bib")).unwrap();
assert_eq!(diagnostics.len(), 1);
assert_eq!(diagnostics[0].message, BibtexErrorCode::MissingBeginBrace.message());
assert_eq!(
diagnostics[0].message,
BibtexErrorCode::MissingBeginBrace.message()
);
assert_eq!(diagnostics[0].range.start.line, 0);
});
}
Expand All @@ -61,14 +64,14 @@ fn test_lint_bibtex() {
fn test_build() {
block_on(async move {
let scenario = Scenario::new("diagnostics/build").await;
scenario.server.did_change_watched_files(DidChangeWatchedFilesParams {
changes: vec![
FileEvent {
scenario
.server
.did_change_watched_files(DidChangeWatchedFilesParams {
changes: vec![FileEvent {
uri: scenario.uri("foo.log"),
typ: FileChangeType::Changed
}
]
});
typ: FileChangeType::Changed,
}],
});
scenario.server.execute_actions().await;

let diagnostics_by_uri = scenario.client.diagnostics_by_uri.lock().await;
Expand Down

0 comments on commit 45b5f6b

Please sign in to comment.