diff options
author | Norbert Preining <norbert@preining.info> | 2022-12-30 03:01:26 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2022-12-30 03:01:26 +0000 |
commit | 844019377157163b461e0fd4a66592e61963a530 (patch) | |
tree | 32f61991c6a5acc3a3359ffc2cdefdd2aa004268 /support/texlab/src/db/document.rs | |
parent | 55c69feeef908f49007708be194b7bb1c607f302 (diff) |
CTAN sync 202212300301
Diffstat (limited to 'support/texlab/src/db/document.rs')
-rw-r--r-- | support/texlab/src/db/document.rs | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/support/texlab/src/db/document.rs b/support/texlab/src/db/document.rs new file mode 100644 index 0000000000..186bd06ab3 --- /dev/null +++ b/support/texlab/src/db/document.rs @@ -0,0 +1,193 @@ +use std::{ + ffi::OsStr, + path::{Path, PathBuf}, +}; + +use lsp_types::Url; +use rowan::{TextRange, TextSize}; + +use crate::{ + db::{ + diagnostics::Diagnostic, + parse::{BibDocumentData, LogDocumentData, TexDocumentData}, + }, + parser::{parse_bibtex, parse_build_log, parse_latex}, + util::line_index::LineIndex, + Db, +}; + +use super::{ + analysis::TexLinkKind, + parse::{self, DocumentData}, +}; + +#[salsa::interned] +pub struct Location { + #[return_ref] + pub uri: Url, +} + +#[salsa::tracked] +impl Location { + #[salsa::tracked(return_ref)] + pub fn path(self, db: &dyn Db) -> Option<PathBuf> { + let uri = self.uri(db); + if uri.scheme() == "file" { + uri.to_file_path().ok() + } else { + None + } + } + + pub fn stem<'db>(self, db: &'db dyn Db) -> Option<&'db str> { + let name = self.uri(db).path_segments()?.last()?; + let stem = name.rsplit_once('.').map_or(name, |(stem, _)| stem); + Some(stem) + } + + pub fn join(self, db: &dyn Db, path: &str) -> Option<Location> { + let uri = self.uri(db).join(path).ok()?; + Some(Location::new(db, uri)) + } + + pub fn into_dir(self, db: &dyn Db) -> Location { + if self.uri(db).path().ends_with("/") { + self.join(db, ".").unwrap() + } else { + self + } + } +} + +#[salsa::input] +pub struct Contents { + #[return_ref] + pub text: String, +} + +#[salsa::tracked] +impl Contents { + #[salsa::tracked(return_ref)] + pub fn line_index(self, db: &dyn Db) -> LineIndex { + LineIndex::new(self.text(db)) + } +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] +pub enum Owner { + Client, + Server, +} + +#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Copy, Hash)] +pub enum Language { + Tex, + Bib, + Log, +} + +impl Language { + pub fn from_path(path: &Path) -> Option<Self> { + path.extension() + .and_then(OsStr::to_str) + .and_then(Self::from_extension) + } + + pub fn from_extension(extension: &str) -> Option<Self> { + match extension.to_lowercase().as_str() { + "tex" | "sty" | "cls" | "def" | "lco" | "aux" | "rnw" => Some(Self::Tex), + "bib" | "bibtex" => Some(Self::Bib), + "log" => Some(Self::Log), + _ => None, + } + } + + pub fn from_id(id: &str) -> Option<Self> { + match id { + "tex" | "latex" => Some(Self::Tex), + "bib" | "bibtex" => Some(Self::Bib), + _ => None, + } + } +} + +#[salsa::input] +pub struct LinterData { + #[return_ref] + pub chktex: Vec<Diagnostic>, +} + +#[salsa::input] +pub struct Document { + pub location: Location, + pub contents: Contents, + pub language: Language, + pub owner: Owner, + pub cursor: TextSize, + pub linter: LinterData, +} + +impl Document { + pub fn edit(self, db: &mut dyn Db, range: TextRange, replace_with: &str) { + let mut text = self.contents(db).set_text(db).to(String::new()); + text.replace_range(std::ops::Range::<usize>::from(range), &replace_with); + self.contents(db).set_text(db).to(text); + self.set_cursor(db).to(range.start()); + } + + pub fn directory(self, db: &dyn Db) -> Location { + self.location(db).into_dir(db) + } + + pub fn ancestor_dirs<'db>(self, db: &'db dyn Db) -> impl Iterator<Item = &'db Path> + 'db { + self.location(db) + .path(db) + .as_deref() + .into_iter() + .flat_map(|path| path.ancestors()) + .skip(1) + } +} + +#[salsa::tracked] +impl Document { + #[salsa::tracked] + pub fn parse(self, db: &dyn Db) -> DocumentData { + let text = self.contents(db).text(db); + match self.language(db) { + Language::Tex => { + let data = TexDocumentData::new(db, parse_latex(text)); + parse::DocumentData::Tex(data) + } + Language::Bib => { + let data = BibDocumentData::new(db, parse_bibtex(text)); + DocumentData::Bib(data) + } + Language::Log => { + let data = LogDocumentData::new(db, parse_build_log(text)); + DocumentData::Log(data) + } + } + } + + #[salsa::tracked] + pub fn can_be_index(self, db: &dyn Db) -> bool { + self.parse(db).as_tex().map_or(false, |data| { + data.analyze(db) + .links(db) + .iter() + .filter(|link| link.kind(db) == TexLinkKind::Cls) + .any(|link| link.path(db).text(db) != "subfiles") + }) + } + + #[salsa::tracked] + pub fn can_be_built(self, db: &dyn Db) -> bool { + self.parse(db).as_tex().map_or(false, |data| { + data.analyze(db) + .links(db) + .iter() + .any(|link| link.kind(db) == TexLinkKind::Cls) + }) + } +} |