summaryrefslogtreecommitdiff
path: root/support/texlab/crates/tex
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/crates/tex')
-rw-r--r--support/texlab/crates/tex/Cargo.toml18
-rw-r--r--support/texlab/crates/tex/src/compile.rs87
-rw-r--r--support/texlab/crates/tex/src/language.rs23
-rw-r--r--support/texlab/crates/tex/src/lib.rs106
-rw-r--r--support/texlab/crates/tex/src/miktex.rs18
-rw-r--r--support/texlab/crates/tex/src/tectonic.rs32
-rw-r--r--support/texlab/crates/tex/src/texlive.rs18
7 files changed, 302 insertions, 0 deletions
diff --git a/support/texlab/crates/tex/Cargo.toml b/support/texlab/crates/tex/Cargo.toml
new file mode 100644
index 0000000000..5c26ff83d8
--- /dev/null
+++ b/support/texlab/crates/tex/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "tex"
+version = "0.1.0"
+authors = [
+ "Eric Förster <efoerster@users.noreply.github.com>",
+ "Patrick Förster <pfoerster@users.noreply.github.com>"]
+edition = "2018"
+
+[dependencies]
+byteorder = "1"
+futures-boxed = { path = "../futures_boxed" }
+futures-preview = "0.3.0-alpha.18"
+log = "0.4.6"
+tempfile = "3"
+tokio = "0.2.0-alpha.6"
+tokio-net = { version = "0.2.0-alpha.6", features = ["process"]}
+serde = { version = "1.0", features = ["derive"] }
+serde_json = "1.0"
diff --git a/support/texlab/crates/tex/src/compile.rs b/support/texlab/crates/tex/src/compile.rs
new file mode 100644
index 0000000000..eeb7db9e4e
--- /dev/null
+++ b/support/texlab/crates/tex/src/compile.rs
@@ -0,0 +1,87 @@
+use futures::future::TryFutureExt;
+use std::io;
+use std::process::Stdio;
+use std::time::Duration;
+use tempfile::{tempdir, TempDir};
+use tokio::fs;
+use tokio::future::FutureExt;
+use tokio_net::process::Command;
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum Format {
+ Latex,
+ Pdflatex,
+ Xelatex,
+ Lualatex,
+}
+
+impl Format {
+ pub fn executable(self) -> &'static str {
+ match self {
+ Self::Latex => "latex",
+ Self::Pdflatex => "pdflatex",
+ Self::Xelatex => "xelatex",
+ Self::Lualatex => "lualatex",
+ }
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum OutputKind {
+ Dvi,
+ Pdf,
+}
+
+#[derive(Debug)]
+pub struct CompileResult {
+ pub log: String,
+ pub directory: TempDir,
+}
+
+#[derive(Debug)]
+pub enum CompileError {
+ IO(io::Error),
+ NotInstalled,
+ Timeout,
+}
+
+impl From<io::Error> for CompileError {
+ fn from(error: io::Error) -> Self {
+ Self::IO(error)
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct CompileParams<'a> {
+ pub file_name: &'a str,
+ pub code: &'a str,
+ pub format: Format,
+ pub timeout: Duration,
+}
+
+pub async fn compile<'a>(
+ executable: &'a str,
+ args: &'a [&'a str],
+ params: CompileParams<'a>,
+) -> Result<CompileResult, CompileError> {
+ let directory = tempdir()?;
+ let code_file = directory.path().join(params.file_name);
+ fs::write(code_file.clone(), params.code).await?;
+
+ Command::new(executable)
+ .args(args)
+ .current_dir(&directory)
+ .stdout(Stdio::null())
+ .stderr(Stdio::null())
+ .status()
+ .map_err(|_| CompileError::NotInstalled)
+ .timeout(params.timeout)
+ .map_err(|_| CompileError::Timeout)
+ .await?
+ .map_err(|_| CompileError::NotInstalled)?;
+
+ let log_file = code_file.with_extension("log");
+ let log_bytes = fs::read(log_file).await?;
+ let log = String::from_utf8_lossy(&log_bytes).into_owned();
+ Ok(CompileResult { log, directory })
+}
diff --git a/support/texlab/crates/tex/src/language.rs b/support/texlab/crates/tex/src/language.rs
new file mode 100644
index 0000000000..ea2b6eec90
--- /dev/null
+++ b/support/texlab/crates/tex/src/language.rs
@@ -0,0 +1,23 @@
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum Language {
+ Latex,
+ Bibtex,
+}
+
+impl Language {
+ pub fn by_extension(extension: &str) -> Option<Self> {
+ match extension.to_lowercase().as_ref() {
+ "tex" | "sty" | "cls" | "lco" | "aux" => Some(Language::Latex),
+ "bib" => Some(Language::Bibtex),
+ _ => None,
+ }
+ }
+
+ pub fn by_language_id(language_id: &str) -> Option<Self> {
+ match language_id {
+ "latex" | "tex" => Some(Language::Latex),
+ "bibtex" | "bib" => Some(Language::Bibtex),
+ _ => None,
+ }
+ }
+}
diff --git a/support/texlab/crates/tex/src/lib.rs b/support/texlab/crates/tex/src/lib.rs
new file mode 100644
index 0000000000..5075ec0efb
--- /dev/null
+++ b/support/texlab/crates/tex/src/lib.rs
@@ -0,0 +1,106 @@
+mod compile;
+mod language;
+mod miktex;
+mod tectonic;
+mod texlive;
+
+pub use self::compile::*;
+pub use self::language::Language;
+
+use self::miktex::Miktex;
+use self::tectonic::Tectonic;
+use self::texlive::Texlive;
+use futures_boxed::boxed;
+use tokio_net::process::Command;
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum DistributionKind {
+ Texlive,
+ Miktex,
+ Tectonic,
+ Unknown,
+}
+
+impl DistributionKind {
+ pub async fn detect() -> Self {
+ if Command::new("tectonic")
+ .arg("--version")
+ .status()
+ .await
+ .is_ok()
+ {
+ return Self::Tectonic;
+ }
+
+ match Command::new("latex").arg("--version").output().await {
+ Ok(output) => {
+ let stdout = String::from_utf8_lossy(&output.stdout);
+ if stdout.contains("TeX Live") {
+ Self::Texlive
+ } else if stdout.contains("MiKTeX") {
+ Self::Miktex
+ } else {
+ Self::Unknown
+ }
+ }
+ Err(_) => Self::Unknown,
+ }
+ }
+}
+
+pub trait Distribution: Send + Sync {
+ fn kind(&self) -> DistributionKind;
+
+ fn supports_format(&self, format: Format) -> bool;
+
+ fn output_kind(&self, format: Format) -> OutputKind {
+ match format {
+ Format::Latex => OutputKind::Dvi,
+ Format::Pdflatex | Format::Xelatex | Format::Lualatex => OutputKind::Pdf,
+ }
+ }
+
+ #[boxed]
+ async fn compile<'a>(
+ &'a self,
+ params: CompileParams<'a>,
+ ) -> Result<CompileResult, CompileError> {
+ let executable = params.format.executable();
+ let args = &["--interaction=batchmode", "-shell-escape", params.file_name];
+ compile(executable, args, params).await
+ }
+}
+
+impl dyn Distribution {
+ pub async fn detect() -> Box<Self> {
+ let kind = DistributionKind::detect().await;
+ let distro: Box<Self> = match kind {
+ DistributionKind::Texlive => Box::new(Texlive),
+ DistributionKind::Miktex => Box::new(Miktex),
+ DistributionKind::Tectonic => Box::new(Tectonic),
+ DistributionKind::Unknown => Box::new(Unknown),
+ };
+ distro
+ }
+}
+
+#[derive(Debug, Default)]
+pub struct Unknown;
+
+impl Distribution for Unknown {
+ fn kind(&self) -> DistributionKind {
+ DistributionKind::Unknown
+ }
+
+ fn supports_format(&self, _format: Format) -> bool {
+ false
+ }
+
+ #[boxed]
+ async fn compile<'a>(
+ &'a self,
+ _params: CompileParams<'a>,
+ ) -> Result<CompileResult, CompileError> {
+ Err(CompileError::NotInstalled)
+ }
+}
diff --git a/support/texlab/crates/tex/src/miktex.rs b/support/texlab/crates/tex/src/miktex.rs
new file mode 100644
index 0000000000..8ab399dc2b
--- /dev/null
+++ b/support/texlab/crates/tex/src/miktex.rs
@@ -0,0 +1,18 @@
+use super::compile::*;
+use super::{Distribution, DistributionKind};
+
+#[derive(Debug, Default)]
+pub struct Miktex;
+
+impl Distribution for Miktex {
+ fn kind(&self) -> DistributionKind {
+ DistributionKind::Miktex
+ }
+
+ fn supports_format(&self, format: Format) -> bool {
+ match format {
+ Format::Latex | Format::Pdflatex => true,
+ Format::Xelatex | Format::Lualatex => true,
+ }
+ }
+}
diff --git a/support/texlab/crates/tex/src/tectonic.rs b/support/texlab/crates/tex/src/tectonic.rs
new file mode 100644
index 0000000000..605e79dc92
--- /dev/null
+++ b/support/texlab/crates/tex/src/tectonic.rs
@@ -0,0 +1,32 @@
+use super::compile::*;
+use super::{Distribution, DistributionKind};
+use futures_boxed::boxed;
+
+#[derive(Debug, Default)]
+pub struct Tectonic;
+
+impl Distribution for Tectonic {
+ fn kind(&self) -> DistributionKind {
+ DistributionKind::Tectonic
+ }
+
+ fn supports_format(&self, format: Format) -> bool {
+ match format {
+ Format::Latex | Format::Pdflatex | Format::Xelatex => true,
+ Format::Lualatex => false,
+ }
+ }
+
+ fn output_kind(&self, _format: Format) -> OutputKind {
+ OutputKind::Pdf
+ }
+
+ #[boxed]
+ async fn compile<'a>(
+ &'a self,
+ params: CompileParams<'a>,
+ ) -> Result<CompileResult, CompileError> {
+ let args = [params.file_name];
+ compile("tectonic", &args, params).await
+ }
+}
diff --git a/support/texlab/crates/tex/src/texlive.rs b/support/texlab/crates/tex/src/texlive.rs
new file mode 100644
index 0000000000..4f90d07926
--- /dev/null
+++ b/support/texlab/crates/tex/src/texlive.rs
@@ -0,0 +1,18 @@
+use super::compile::*;
+use super::{Distribution, DistributionKind};
+
+#[derive(Debug, Default)]
+pub struct Texlive;
+
+impl Distribution for Texlive {
+ fn kind(&self) -> DistributionKind {
+ DistributionKind::Texlive
+ }
+
+ fn supports_format(&self, format: Format) -> bool {
+ match format {
+ Format::Latex | Format::Pdflatex => true,
+ Format::Xelatex | Format::Lualatex => true,
+ }
+ }
+}