diff options
Diffstat (limited to 'support/texlab/crates/base-db')
-rw-r--r-- | support/texlab/crates/base-db/Cargo.toml | 11 | ||||
-rw-r--r-- | support/texlab/crates/base-db/src/document.rs | 40 | ||||
-rw-r--r-- | support/texlab/crates/base-db/src/graph.rs | 5 | ||||
-rw-r--r-- | support/texlab/crates/base-db/src/util.rs | 2 | ||||
-rw-r--r-- | support/texlab/crates/base-db/src/util/line_index.rs | 217 | ||||
-rw-r--r-- | support/texlab/crates/base-db/src/workspace.rs | 72 |
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; } |