summaryrefslogtreecommitdiff
path: root/support/texlab/src/tex/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/src/tex/mod.rs')
-rw-r--r--support/texlab/src/tex/mod.rs154
1 files changed, 154 insertions, 0 deletions
diff --git a/support/texlab/src/tex/mod.rs b/support/texlab/src/tex/mod.rs
new file mode 100644
index 0000000000..6d60380e52
--- /dev/null
+++ b/support/texlab/src/tex/mod.rs
@@ -0,0 +1,154 @@
+mod compile;
+mod kpsewhich;
+mod miktex;
+mod tectonic;
+mod texlive;
+
+pub use self::{
+ compile::{Artifacts, CompileError, CompileParams, Format},
+ kpsewhich::{KpsewhichError, Resolver},
+};
+
+use self::{compile::Compiler, miktex::Miktex, tectonic::Tectonic, texlive::Texlive};
+use async_trait::async_trait;
+use std::{fmt, process::Stdio, sync::Arc};
+use tokio::process::Command;
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub enum DistributionKind {
+ Texlive,
+ Miktex,
+ Tectonic,
+ Unknown,
+}
+
+impl fmt::Display for DistributionKind {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let name = match self {
+ Self::Texlive => "TeXLive",
+ Self::Miktex => "MikTeX",
+ Self::Tectonic => "Tectonic",
+ Self::Unknown => "Unknown",
+ };
+ write!(f, "{}", name)
+ }
+}
+
+impl DistributionKind {
+ pub async fn detect() -> Self {
+ if Command::new("tectonic")
+ .arg("--version")
+ .stdout(Stdio::null())
+ .stderr(Stdio::null())
+ .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,
+ }
+ }
+}
+
+#[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_str() {
+ "tex" | "sty" | "cls" | "def" | "lco" | "aux" => Some(Language::Latex),
+ "bib" | "bibtex" => 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,
+ }
+ }
+}
+
+#[async_trait]
+pub trait Distribution: Send + Sync {
+ fn kind(&self) -> DistributionKind;
+
+ async fn compile<'a>(&'a self, params: CompileParams<'a>) -> Result<Artifacts, CompileError>;
+
+ async fn load(&self) -> Result<(), KpsewhichError>;
+
+ async fn resolver(&self) -> Arc<Resolver>;
+}
+
+impl dyn Distribution {
+ pub async fn detect() -> Arc<dyn Distribution> {
+ let kind = DistributionKind::detect().await;
+ let distro: Arc<dyn Distribution + Send + Sync> = match kind {
+ DistributionKind::Texlive => Arc::new(Texlive::default()),
+ DistributionKind::Miktex => Arc::new(Miktex::default()),
+ DistributionKind::Tectonic => Arc::new(Tectonic::default()),
+ DistributionKind::Unknown => Arc::new(UnknownDistribution::default()),
+ };
+ distro
+ }
+}
+
+impl fmt::Debug for dyn Distribution {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.kind())
+ }
+}
+
+async fn compile(params: CompileParams<'_>) -> Result<Artifacts, CompileError> {
+ let executable = params.format.executable();
+ let args = &["--interaction=batchmode", "-shell-escape", params.file_name];
+ let compiler = Compiler {
+ executable,
+ args,
+ file_name: params.file_name,
+ timeout: params.timeout,
+ };
+ compiler.compile(params.code).await
+}
+
+#[derive(Debug, Default)]
+pub struct UnknownDistribution {
+ resolver: Arc<Resolver>,
+}
+
+#[async_trait]
+impl Distribution for UnknownDistribution {
+ fn kind(&self) -> DistributionKind {
+ DistributionKind::Unknown
+ }
+
+ async fn compile<'a>(&'a self, _params: CompileParams<'a>) -> Result<Artifacts, CompileError> {
+ Err(CompileError::NotInstalled)
+ }
+
+ async fn load(&self) -> Result<(), KpsewhichError> {
+ Ok(())
+ }
+
+ async fn resolver(&self) -> Arc<Resolver> {
+ Arc::clone(&self.resolver)
+ }
+}