summaryrefslogtreecommitdiff
path: root/support/texlab/crates/base-db
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/crates/base-db')
-rw-r--r--support/texlab/crates/base-db/Cargo.toml11
-rw-r--r--support/texlab/crates/base-db/src/document.rs40
-rw-r--r--support/texlab/crates/base-db/src/graph.rs5
-rw-r--r--support/texlab/crates/base-db/src/util.rs2
-rw-r--r--support/texlab/crates/base-db/src/util/line_index.rs217
-rw-r--r--support/texlab/crates/base-db/src/workspace.rs72
6 files changed, 91 insertions, 256 deletions
diff --git a/support/texlab/crates/base-db/Cargo.toml b/support/texlab/crates/base-db/Cargo.toml
index b440ff5c61..f5e2c07829 100644
--- a/support/texlab/crates/base-db/Cargo.toml
+++ b/support/texlab/crates/base-db/Cargo.toml
@@ -7,20 +7,21 @@ edition.workspace = true
rust-version.workspace = true
[dependencies]
+bibtex-utils = { path = "../bibtex-utils" }
dirs = "5.0.1"
distro = { path = "../distro" }
-itertools = "0.11.0"
+itertools = "0.12.0"
+line-index = { path = "../line-index" }
log = "0.4.20"
notify = "6.0.1"
once_cell = "1.18.0"
parser = { path = "../parser" }
percent-encoding = "2.3.0"
-regex = "1.9.5"
-rowan = "0.15.11"
+regex = "1.10.2"
+rowan = "0.15.13"
rustc-hash = "1.1.0"
syntax = { path = "../syntax" }
-url = "=2.3.1"
-bibtex-utils = { path = "../bibtex-utils" }
+url = "2.5.0"
[lib]
doctest = false
diff --git a/support/texlab/crates/base-db/src/document.rs b/support/texlab/crates/base-db/src/document.rs
index 8642694811..9f45876d7e 100644
--- a/support/texlab/crates/base-db/src/document.rs
+++ b/support/texlab/crates/base-db/src/document.rs
@@ -1,15 +1,12 @@
use std::path::PathBuf;
use distro::Language;
+use line_index::{LineCol, LineIndex};
use rowan::TextRange;
-use syntax::{bibtex, latex, BuildError};
+use syntax::{bibtex, latex, latexmkrc::LatexmkrcData, BuildError};
use url::Url;
-use crate::{
- semantics,
- util::{LineCol, LineIndex},
- Config,
-};
+use crate::{semantics, Config};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)]
pub enum Owner {
@@ -78,6 +75,10 @@ impl Document {
DocumentData::Log(LogDocumentData { errors })
}
Language::Root => DocumentData::Root,
+ Language::Latexmkrc => {
+ let data = parser::parse_latexmkrc(&text).unwrap_or_default();
+ DocumentData::Latexmkrc(data)
+ }
Language::Tectonic => DocumentData::Tectonic,
};
@@ -134,6 +135,7 @@ pub enum DocumentData {
Aux(AuxDocumentData),
Log(LogDocumentData),
Root,
+ Latexmkrc(LatexmkrcData),
Tectonic,
}
@@ -169,6 +171,14 @@ impl DocumentData {
None
}
}
+
+ pub fn as_latexmkrc(&self) -> Option<&LatexmkrcData> {
+ if let DocumentData::Latexmkrc(data) = self {
+ Some(data)
+ } else {
+ None
+ }
+ }
}
#[derive(Debug, Clone)]
@@ -216,4 +226,22 @@ impl<'a> DocumentLocation<'a> {
pub fn new(document: &'a Document, range: TextRange) -> Self {
Self { document, range }
}
+
+ fn eq_key(&self) -> (&Url, TextRange) {
+ (&self.document.uri, self.range)
+ }
+}
+
+impl<'a> PartialEq for DocumentLocation<'a> {
+ fn eq(&self, other: &Self) -> bool {
+ self.eq_key() == other.eq_key()
+ }
+}
+
+impl<'a> Eq for DocumentLocation<'a> {}
+
+impl<'a> std::hash::Hash for DocumentLocation<'a> {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ self.eq_key().hash(state)
+ }
}
diff --git a/support/texlab/crates/base-db/src/graph.rs b/support/texlab/crates/base-db/src/graph.rs
index 46b9038de8..088e0828b7 100644
--- a/support/texlab/crates/base-db/src/graph.rs
+++ b/support/texlab/crates/base-db/src/graph.rs
@@ -138,9 +138,8 @@ impl<'a> Graph<'a> {
fn add_implicit_edges(&mut self, source: &'a Document, base_dir: &Url) {
if source.language == Language::Tex {
- let config = &self.workspace.config().build;
- let aux_dir = self.workspace.output_dir(base_dir, config.aux_dir.clone());
- let log_dir = self.workspace.output_dir(base_dir, config.log_dir.clone());
+ let aux_dir = self.workspace.aux_dir(base_dir);
+ let log_dir = self.workspace.log_dir(base_dir);
let relative_path = base_dir.make_relative(&source.uri).unwrap();
diff --git a/support/texlab/crates/base-db/src/util.rs b/support/texlab/crates/base-db/src/util.rs
index 6d6d6897a6..068a0c9b2a 100644
--- a/support/texlab/crates/base-db/src/util.rs
+++ b/support/texlab/crates/base-db/src/util.rs
@@ -1,10 +1,8 @@
mod label;
-mod line_index;
pub mod queries;
mod regex_filter;
pub use self::{
label::{render_label, FloatKind, RenderedLabel, RenderedObject},
- line_index::{LineCol, LineColUtf16, LineIndex},
regex_filter::filter_regex_patterns,
};
diff --git a/support/texlab/crates/base-db/src/util/line_index.rs b/support/texlab/crates/base-db/src/util/line_index.rs
deleted file mode 100644
index 3c5907782a..0000000000
--- a/support/texlab/crates/base-db/src/util/line_index.rs
+++ /dev/null
@@ -1,217 +0,0 @@
-// The following code has been copied from rust-analyzer.
-
-//! `LineIndex` maps flat `TextSize` offsets into `(Line, Column)`
-//! representation.
-use std::iter;
-
-use rowan::{TextRange, TextSize};
-use rustc_hash::FxHashMap;
-
-#[derive(Clone, Debug, PartialEq, Eq)]
-pub struct LineIndex {
- /// Offset the the beginning of each line, zero-based
- pub newlines: Vec<TextSize>,
- /// List of non-ASCII characters on each line
- pub(crate) utf16_lines: FxHashMap<u32, Vec<Utf16Char>>,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct LineColUtf16 {
- /// Zero-based
- pub line: u32,
- /// Zero-based
- pub col: u32,
-}
-
-#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
-pub struct LineCol {
- /// Zero-based
- pub line: u32,
- /// Zero-based utf8 offset
- pub col: u32,
-}
-
-#[derive(Clone, Debug, Hash, PartialEq, Eq)]
-pub(crate) struct Utf16Char {
- /// Start offset of a character inside a line, zero-based
- pub(crate) start: TextSize,
- /// End offset of a character inside a line, zero-based
- pub(crate) end: TextSize,
-}
-
-impl Utf16Char {
- /// Returns the length in 8-bit UTF-8 code units.
- fn len(&self) -> TextSize {
- self.end - self.start
- }
-
- /// Returns the length in 16-bit UTF-16 code units.
- fn len_utf16(&self) -> usize {
- if self.len() == TextSize::from(4) {
- 2
- } else {
- 1
- }
- }
-}
-
-impl LineIndex {
- pub fn new(text: &str) -> LineIndex {
- let mut utf16_lines = FxHashMap::default();
- let mut utf16_chars = Vec::new();
-
- let mut newlines = vec![0.into()];
- let mut curr_row = 0.into();
- let mut curr_col = 0.into();
- let mut line = 0;
- for c in text.chars() {
- let c_len = TextSize::of(c);
- curr_row += c_len;
- if c == '\n' {
- newlines.push(curr_row);
-
- // Save any utf-16 characters seen in the previous line
- if !utf16_chars.is_empty() {
- utf16_lines.insert(line, utf16_chars);
- utf16_chars = Vec::new();
- }
-
- // Prepare for processing the next line
- curr_col = 0.into();
- line += 1;
- continue;
- }
-
- if !c.is_ascii() {
- utf16_chars.push(Utf16Char {
- start: curr_col,
- end: curr_col + c_len,
- });
- }
-
- curr_col += c_len;
- }
-
- // Save any utf-16 characters seen in the last line
- if !utf16_chars.is_empty() {
- utf16_lines.insert(line, utf16_chars);
- }
-
- LineIndex {
- newlines,
- utf16_lines,
- }
- }
-
- pub fn line_col(&self, offset: TextSize) -> LineCol {
- let line = partition_point(&self.newlines, |&it| it <= offset) - 1;
- let line_start_offset = self.newlines[line];
- let col = offset - line_start_offset;
- LineCol {
- line: line as u32,
- col: col.into(),
- }
- }
-
- pub fn offset(&self, line_col: LineCol) -> TextSize {
- self.newlines[line_col.line as usize] + TextSize::from(line_col.col)
- }
-
- pub fn to_utf16(&self, line_col: LineCol) -> LineColUtf16 {
- let col = self.utf8_to_utf16_col(line_col.line, line_col.col.into());
- LineColUtf16 {
- line: line_col.line,
- col: col as u32,
- }
- }
-
- pub fn to_utf8(&self, line_col: LineColUtf16) -> LineCol {
- let col = self.utf16_to_utf8_col(line_col.line, line_col.col);
- LineCol {
- line: line_col.line,
- col: col.into(),
- }
- }
-
- pub fn lines(&self, range: TextRange) -> impl Iterator<Item = TextRange> + '_ {
- let lo = partition_point(&self.newlines, |&it| it < range.start());
- let hi = partition_point(&self.newlines, |&it| it <= range.end());
- let all = iter::once(range.start())
- .chain(self.newlines[lo..hi].iter().copied())
- .chain(iter::once(range.end()));
-
- all.clone()
- .zip(all.skip(1))
- .map(|(lo, hi)| TextRange::new(lo, hi))
- .filter(|it| !it.is_empty())
- }
-
- fn utf8_to_utf16_col(&self, line: u32, col: TextSize) -> usize {
- let mut res: usize = col.into();
- if let Some(utf16_chars) = self.utf16_lines.get(&line) {
- for c in utf16_chars {
- if c.end <= col {
- res -= usize::from(c.len()) - c.len_utf16();
- } else {
- // From here on, all utf16 characters come *after* the character we are mapping,
- // so we don't need to take them into account
- break;
- }
- }
- }
- res
- }
-
- fn utf16_to_utf8_col(&self, line: u32, mut col: u32) -> TextSize {
- if let Some(utf16_chars) = self.utf16_lines.get(&line) {
- for c in utf16_chars {
- if col > u32::from(c.start) {
- col += u32::from(c.len()) - c.len_utf16() as u32;
- } else {
- // From here on, all utf16 characters come *after* the character we are mapping,
- // so we don't need to take them into account
- break;
- }
- }
- }
-
- col.into()
- }
-}
-
-/// Returns `idx` such that:
-///
-/// ```text
-/// ∀ x in slice[..idx]: pred(x)
-/// && ∀ x in slice[idx..]: !pred(x)
-/// ```
-///
-/// https://github.com/rust-lang/rust/issues/73831
-fn partition_point<T, P>(slice: &[T], mut pred: P) -> usize
-where
- P: FnMut(&T) -> bool,
-{
- let mut left = 0;
- let mut right = slice.len();
-
- while left != right {
- let mid = left + (right - left) / 2;
- // SAFETY:
- // When left < right, left <= mid < right.
- // Therefore left always increases and right always decreases,
- // and either of them is selected.
- // In both cases left <= right is satisfied.
- // Therefore if left < right in a step,
- // left <= right is satisfied in the next step.
- // Therefore as long as left != right, 0 <= left < right <= len is satisfied
- // and if this case 0 <= mid < len is satisfied too.
- let value = unsafe { slice.get_unchecked(mid) };
- if pred(value) {
- left = mid + 1;
- } else {
- right = mid;
- }
- }
-
- left
-}
diff --git a/support/texlab/crates/base-db/src/workspace.rs b/support/texlab/crates/base-db/src/workspace.rs
index b4a524ad4a..330ed5a493 100644
--- a/support/texlab/crates/base-db/src/workspace.rs
+++ b/support/texlab/crates/base-db/src/workspace.rs
@@ -5,11 +5,13 @@ use std::{
use distro::{Distro, Language};
use itertools::Itertools;
+use line_index::LineCol;
use rowan::{TextLen, TextRange};
use rustc_hash::FxHashSet;
+use syntax::latexmkrc::LatexmkrcData;
use url::Url;
-use crate::{graph, util::LineCol, Config, Document, DocumentData, DocumentParams, Owner};
+use crate::{graph, Config, Document, DocumentData, DocumentParams, Owner};
#[derive(Debug, Default)]
pub struct Workspace {
@@ -114,22 +116,11 @@ impl Workspace {
self.iter()
.filter(|document| document.uri.scheme() == "file")
.flat_map(|document| {
- let dir1 = self.output_dir(
- &self.current_dir(&document.dir),
- self.config.build.aux_dir.clone(),
- );
-
- let dir2 = self.output_dir(
- &self.current_dir(&document.dir),
- self.config.build.log_dir.clone(),
- );
-
- let dir3 = &document.dir;
- [
- dir1.to_file_path(),
- dir2.to_file_path(),
- dir3.to_file_path(),
- ]
+ let current_dir = &self.current_dir(&document.dir);
+ let doc_dir = document.dir.to_file_path();
+ let aux_dir = self.aux_dir(current_dir).to_file_path();
+ let log_dir = self.log_dir(current_dir).to_file_path();
+ [aux_dir, log_dir, doc_dir]
})
.flatten()
.for_each(|path| {
@@ -153,13 +144,45 @@ impl Workspace {
.unwrap_or_else(|| base_dir.clone())
}
- pub fn output_dir(&self, base_dir: &Url, relative_path: String) -> Url {
- let mut path = relative_path;
- if !path.ends_with('/') {
- path.push('/');
+ pub fn aux_dir(&self, base_dir: &Url) -> Url {
+ self.output_dir(base_dir, &self.config.build.aux_dir, |data| {
+ data.aux_dir.as_deref()
+ })
+ }
+
+ pub fn log_dir(&self, base_dir: &Url) -> Url {
+ self.output_dir(base_dir, &self.config.build.log_dir, |_| None)
+ }
+
+ pub fn pdf_dir(&self, base_dir: &Url) -> Url {
+ self.output_dir(base_dir, &self.config.build.pdf_dir, |_| None)
+ }
+
+ fn current_latexmkrc(&self, base_dir: &Url) -> Option<&LatexmkrcData> {
+ self.documents
+ .iter()
+ .filter(|document| document.language == Language::Latexmkrc)
+ .find(|document| document.uri.join(".").as_ref() == Ok(base_dir))
+ .and_then(|document| document.data.as_latexmkrc())
+ }
+
+ fn output_dir(
+ &self,
+ base_dir: &Url,
+ config: &str,
+ extract_latexmkrc: impl FnOnce(&LatexmkrcData) -> Option<&str>,
+ ) -> Url {
+ let mut dir: String = self
+ .current_latexmkrc(base_dir)
+ .and_then(|data| extract_latexmkrc(data).or_else(|| data.out_dir.as_deref()))
+ .unwrap_or(config)
+ .into();
+
+ if !dir.ends_with('/') {
+ dir.push('/');
}
- base_dir.join(&path).unwrap_or_else(|_| base_dir.clone())
+ base_dir.join(&dir).unwrap_or_else(|_| base_dir.clone())
}
pub fn contains(&self, path: &Path) -> bool {
@@ -298,7 +321,10 @@ impl Workspace {
continue;
};
- if !matches!(lang, Language::Tex | Language::Root | Language::Tectonic) {
+ if !matches!(
+ lang,
+ Language::Tex | Language::Root | Language::Tectonic | Language::Latexmkrc
+ ) {
continue;
}