diff options
author | Norbert Preining <norbert@preining.info> | 2021-05-23 03:00:39 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2021-05-23 03:00:39 +0000 |
commit | f1261b349e875b842745b63258c3e338cb1fe3bf (patch) | |
tree | b5d402b3e80818cde2c079a42249f3dcb9732247 /support/texlab/src/workspace.rs | |
parent | 58aa1ac09b1d9e4769d0a0661cf12e2b2db41b14 (diff) |
CTAN sync 202105230300
Diffstat (limited to 'support/texlab/src/workspace.rs')
-rw-r--r-- | support/texlab/src/workspace.rs | 980 |
1 files changed, 24 insertions, 956 deletions
diff --git a/support/texlab/src/workspace.rs b/support/texlab/src/workspace.rs index c3c2551325..12924e2cf4 100644 --- a/support/texlab/src/workspace.rs +++ b/support/texlab/src/workspace.rs @@ -1,964 +1,32 @@ -use crate::{ - components::COMPONENT_DATABASE, - protocol::{Options, TextDocumentItem, Uri}, - syntax::{bibtex, latex, LatexIncludeKind}, - tex::{Distribution, Language, Resolver}, -}; -use futures::lock::Mutex; -use log::{debug, error, warn}; -use petgraph::{graph::Graph, visit::Dfs}; -use std::{ - collections::HashMap, - ffi::OsStr, - hash::{Hash, Hasher}, - io, - path::{Path, PathBuf}, - sync::Arc, - time::SystemTime, -}; -use thiserror::Error; -use tokio::fs; - -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct DocumentParams<'a> { - pub uri: Uri, - pub text: String, - pub language: Language, - pub resolver: &'a Resolver, - pub options: &'a Options, - pub current_dir: &'a Path, -} - -#[derive(Debug, Clone)] -pub enum DocumentContent { - Latex(Box<latex::SymbolTable>), - Bibtex(Box<bibtex::Tree>), -} - -impl DocumentContent { - pub fn as_latex(&self) -> Option<&latex::SymbolTable> { - if let Self::Latex(table) = self { - Some(&table) - } else { - None - } - } - - pub fn as_bibtex(&self) -> Option<&bibtex::Tree> { - if let Self::Bibtex(tree) = self { - Some(&tree) - } else { - None - } - } -} - -#[derive(Debug, Clone)] -pub struct Document { - pub uri: Uri, - pub text: String, - pub content: DocumentContent, - pub modified: SystemTime, -} - -impl Document { - pub fn is_file(&self) -> bool { - self.uri.scheme() == "file" - } - - pub fn open(params: DocumentParams) -> Self { - let DocumentParams { - uri, - text, - language, - resolver, - options, - current_dir, - } = params; - - let content = match language { - Language::Latex => { - let table = latex::open(latex::OpenParams { - uri: &uri, - text: &text, - resolver, - options, - current_dir, - }); - DocumentContent::Latex(Box::new(table)) - } - Language::Bibtex => { - let tree = bibtex::open(&text); - DocumentContent::Bibtex(Box::new(tree)) - } - }; - - Self { - uri, - text, - content, - modified: SystemTime::now(), - } - } -} - -impl PartialEq for Document { - fn eq(&self, other: &Self) -> bool { - self.uri == other.uri - } -} - -impl Eq for Document {} - -impl Hash for Document { - fn hash<H: Hasher>(&self, state: &mut H) { - self.uri.hash(state); - } -} - -#[derive(Debug, PartialEq, Eq, Clone, Default)] -pub struct Snapshot(pub Vec<Arc<Document>>); - -impl Snapshot { - pub fn new() -> Self { - Self(Vec::new()) - } - - pub fn push(&mut self, doc: Document) { - self.0.push(Arc::new(doc)); - } - - pub fn find(&self, uri: &Uri) -> Option<Arc<Document>> { - self.0.iter().find(|doc| doc.uri == *uri).map(Arc::clone) - } - - pub fn relations( - &self, - uri: &Uri, - options: &Options, - current_dir: &Path, - ) -> Vec<Arc<Document>> { - let mut graph = Graph::new_undirected(); - let mut indices_by_uri = HashMap::new(); - for document in &self.0 { - indices_by_uri.insert(&document.uri, graph.add_node(document)); - } - - for parent in &self.0 { - if let DocumentContent::Latex(table) = &parent.content { - table - .includes - .iter() - .flat_map(|include| include.all_targets.iter()) - .filter_map(|targets| targets.iter().find_map(|target| self.find(target))) - .for_each(|child| { - graph.add_edge(indices_by_uri[&parent.uri], indices_by_uri[&child.uri], ()); - }); - - table - .imports - .iter() - .flat_map(|import| import.targets.iter()) - .find_map(|target| self.find(target)) - .into_iter() - .for_each(|child| { - graph.add_edge(indices_by_uri[&parent.uri], indices_by_uri[&child.uri], ()); - }); - - self.resolve_aux_targets(&parent.uri, options, current_dir, "aux") - .into_iter() - .flatten() - .find_map(|target| self.find(&target)) - .into_iter() - .for_each(|child| { - graph.add_edge(indices_by_uri[&parent.uri], indices_by_uri[&child.uri], ()); - }); - } - } - - let mut documents = Vec::new(); - if self.find(uri).is_some() { - let mut dfs = Dfs::new(&graph, indices_by_uri[uri]); - while let Some(index) = dfs.next(&graph) { - documents.push(Arc::clone(&graph[index])); - } - } - documents - } - - pub fn parent( - &self, - uri: &Uri, - options: &Options, - current_dir: &Path, - ) -> Option<Arc<Document>> { - for doc in self.relations(uri, options, current_dir) { - if let DocumentContent::Latex(table) = &doc.content { - if table.is_standalone { - return Some(doc); - } - } - } - None - } - - pub fn parent_subfile( - &self, - uri: &Uri, - options: &Options, - current_dir: &Path, - ) -> Option<Arc<Document>> { - for doc in self.relations(uri, options, current_dir) { - if let DocumentContent::Latex(table) = &doc.content { - if table.is_standalone - && !table.components.iter().any(|comp| comp == "subfiles.cls") - { - return Some(doc); - } - } - } - None - } - - pub fn expand(&self, options: &Options, current_dir: &Path) -> Vec<Uri> { - let mut unknown_targets = Vec::new(); - for parent in &self.0 { - if let DocumentContent::Latex(table) = &parent.content { - table - .includes - .iter() - .filter(|include| Self::should_expand_include(&table, include)) - .flat_map(|include| include.all_targets.iter()) - .filter(|targets| targets.iter().all(|target| self.find(target).is_none())) - .flatten() - .for_each(|target| unknown_targets.push(target.clone())); - - table - .imports - .iter() - .filter(|import| { - import - .targets - .iter() - .all(|target| self.find(target).is_none()) - }) - .flat_map(|import| import.targets.iter()) - .for_each(|target| unknown_targets.push(target.clone())); - - self.resolve_aux_targets(&parent.uri, options, current_dir, "aux") - .into_iter() - .filter(|targets| targets.iter().all(|target| self.find(target).is_none())) - .flatten() - .for_each(|target| unknown_targets.push(target)); - } - } - unknown_targets - } - - fn should_expand_include(tree: &latex::Tree, include: &latex::Include) -> bool { - match include.kind { - LatexIncludeKind::Bibliography | LatexIncludeKind::Latex => true, - LatexIncludeKind::Everything - | LatexIncludeKind::Image - | LatexIncludeKind::Pdf - | LatexIncludeKind::Svg => false, - LatexIncludeKind::Package | LatexIncludeKind::Class => !include - .paths(tree) - .into_iter() - .all(|name| COMPONENT_DATABASE.contains(name.text())), - } - } - - pub fn resolve_aux_targets( - &self, - tex_uri: &Uri, - options: &Options, - current_dir: &Path, - extension: &str, - ) -> Option<Vec<Uri>> { - let mut targets = Vec::new(); - targets.push(tex_uri.with_extension(extension)?); - if tex_uri.scheme() == "file" { - let tex_path = tex_uri.to_file_path().ok()?; - let file_stem = tex_path.file_stem()?; - let aux_name = format!("{}.{}", file_stem.to_str()?, extension); - - if let Some(root_dir) = options - .latex - .as_ref() - .and_then(|opts| opts.root_directory.as_ref()) - { - let path = current_dir.join(root_dir).join(&aux_name); - targets.push(Uri::from_file_path(path).ok()?); - } - - if let Some(build_dir) = options - .latex - .as_ref() - .and_then(|opts| opts.build.as_ref()) - .and_then(|opts| opts.output_directory.as_ref()) - { - let path = current_dir.join(build_dir).join(&aux_name); - targets.push(Uri::from_file_path(path).ok()?); - } - } - Some(targets) - } -} - -#[derive(Debug, Error)] -pub enum WorkspaceLoadError { - #[error("invalid language id")] - UnknownLanguage, - #[error("invalid file path")] - InvalidPath, - #[error("an I/O error occurred: `{0}`")] - IO(#[from] io::Error), -} - -pub struct Workspace { - distro: Arc<dyn Distribution>, - current_dir: Arc<PathBuf>, - snapshot: Mutex<Arc<Snapshot>>, -} - -impl Workspace { - pub fn new(distro: Arc<dyn Distribution>, current_dir: Arc<PathBuf>) -> Self { - Self { - distro, - current_dir, - snapshot: Mutex::default(), - } - } - - pub async fn get(&self) -> Arc<Snapshot> { - let snapshot = self.snapshot.lock().await; - Arc::clone(&snapshot) - } - - pub async fn add(&self, document: TextDocumentItem, options: &Options) { - let language = match Language::by_language_id(&document.language_id) { - Some(language) => language, - None => { - error!( - "Invalid language id: {} ({})", - &document.language_id, &document.uri, - ); - return; - } - }; - - debug!("Adding document: {}", document.uri); - let mut snapshot = self.snapshot.lock().await; - *snapshot = self - .add_or_update( - &snapshot, - document.uri.into(), - document.text, - language, - options, - ) - .await; - } - - pub async fn load(&self, path: &Path, options: &Options) -> Result<(), WorkspaceLoadError> { - let language = match path - .extension() - .and_then(OsStr::to_str) - .and_then(Language::by_extension) - { - Some(language) => language, - None => { - warn!("Could not determine language: {}", path.to_string_lossy()); - return Err(WorkspaceLoadError::UnknownLanguage); - } - }; - - let uri = match Uri::from_file_path(path) { - Ok(uri) => uri, - Err(_) => { - error!("Invalid path: {}", path.to_string_lossy()); - return Err(WorkspaceLoadError::InvalidPath); - } - }; - - let text = match fs::read_to_string(path).await { - Ok(text) => text, - Err(why) => { - warn!("Could not open file: {}", uri); - return Err(WorkspaceLoadError::IO(why)); - } - }; +mod api; +mod children_expand; +mod document; +mod parent_expand; +mod storage; +mod watch; - debug!("Loading document: {}", uri); - let mut snapshot = self.snapshot.lock().await; - *snapshot = self - .add_or_update(&snapshot, uri, text, language, options) - .await; - Ok(()) - } +use std::sync::Arc; - pub async fn update(&self, uri: Uri, text: String, options: &Options) { - let mut snapshot = self.snapshot.lock().await; +use anyhow::Result; - let old_document = match snapshot.0.iter().find(|x| x.uri == uri) { - Some(document) => document, - None => { - warn!("Document not found: {}", uri); - return; - } - }; +use crate::ServerContext; - let language = match old_document.content { - DocumentContent::Latex(_) => Language::Latex, - DocumentContent::Bibtex(_) => Language::Bibtex, - }; - - *snapshot = self - .add_or_update(&snapshot, uri, text, language, options) - .await; - } - - pub async fn reparse(&self, options: &Options) { - let snapshot = self.get().await; - for doc in &snapshot.0 { - let language = match doc.content { - DocumentContent::Latex(_) => Language::Latex, - DocumentContent::Bibtex(_) => Language::Bibtex, - }; - - let mut snapshot = self.snapshot.lock().await; - debug!("Reparsing document: {}", doc.uri); - *snapshot = self - .add_or_update( - &snapshot, - doc.uri.clone(), - doc.text.clone(), - language, - options, - ) - .await; - } - } - - pub async fn detect_root(&self, uri: &Uri, options: &Options) -> io::Result<()> { - if uri.scheme() != "file" { - return Ok(()); - } - - if let Ok(mut path) = uri.to_file_path() { - while path.pop() { - let snapshot = self.get().await; - if snapshot - .parent_subfile(&uri, &options, &self.current_dir) - .is_some() - { - break; - } - - let mut entries = fs::read_dir(&path).await?; - while let Some(entry) = entries.next_entry().await? { - if entry.file_type().await?.is_file() { - let path = entry.path(); - if path - .extension() - .and_then(OsStr::to_str) - .and_then(Language::by_extension) - .is_some() - { - if let Ok(parent_uri) = Uri::from_file_path(&path) { - if snapshot.find(&parent_uri).is_none() { - let _ = self.load(&path, options).await; - } - } - } - } - } - } - } - Ok(()) - } - - pub async fn detect_children(&self, options: &Options) { - loop { - let mut changed = false; - - let snapshot = self.get().await; - for path in snapshot - .expand(&options, &self.current_dir) - .into_iter() - .filter(|uri| uri.scheme() == "file" && uri.fragment().is_none()) - .filter_map(|uri| uri.to_file_path().ok()) - { - if path.exists() { - changed |= self.load(&path, &options).await.is_ok(); - } - } - - if !changed { - break; - } - } - } - - pub async fn reparse_all_if_newer(&self, options: &Options) { - let snapshot = self.get().await; - for doc in &snapshot.0 { - if let Err(WorkspaceLoadError::IO(why)) = self.reparse_if_newer(doc, options).await { - warn!("Reparsing document {} failed: {}", doc.uri, why); - } - } - } - - async fn reparse_if_newer( - &self, - doc: &Document, - options: &Options, - ) -> Result<(), WorkspaceLoadError> { - if !doc.is_file() { - return Ok(()); - } - - if let Ok(path) = doc.uri.to_file_path() { - let data = fs::metadata(&path).await?; - if data.modified()? > doc.modified { - self.load(&path, options).await?; - } - } - Ok(()) - } - - async fn add_or_update( - &self, - snapshot: &Snapshot, - uri: Uri, - text: String, - language: Language, - options: &Options, - ) -> Arc<Snapshot> { - let resolver = self.distro.resolver().await; - let document = Document::open(DocumentParams { - uri, - text, - language, - resolver: &resolver, - options, - current_dir: &self.current_dir, - }); - - let mut documents: Vec<Arc<Document>> = snapshot - .0 - .iter() - .filter(|x| x.uri != document.uri) - .cloned() - .collect(); +pub use self::{api::*, document::*}; +use self::{ + children_expand::ChildrenExpander, parent_expand::ParentExpander, storage::Storage, + watch::DocumentWatcher, +}; - documents.push(Arc::new(document)); - Arc::new(Snapshot(documents)) - } +pub fn create_workspace_fast(context: Arc<ServerContext>) -> Result<impl Workspace> { + let workspace = Storage::new(context); + let workspace = ChildrenExpander::new(Arc::new(workspace)); + Ok(workspace) } -#[cfg(test)] -mod tests { - use super::*; - use crate::protocol::{LatexBuildOptions, LatexOptions}; - use itertools::Itertools; - use std::env; - - fn create_simple_document(uri: &Uri, language: Language, text: &str) -> Arc<Document> { - Arc::new(Document::open(DocumentParams { - uri: uri.clone(), - text: text.into(), - language, - resolver: &Resolver::default(), - options: &Options::default(), - current_dir: &env::current_dir().unwrap(), - })) - } - - #[test] - fn relations_append_missing_extension() { - let uri1 = Uri::parse("http://www.example.com/foo.tex").unwrap(); - let uri2 = Uri::parse("http://www.example.com/bar/baz.tex").unwrap(); - let mut snapshot = Snapshot::new(); - snapshot.0 = vec![ - create_simple_document(&uri1, Language::Latex, r#"\include{bar/baz}"#), - create_simple_document(&uri2, Language::Latex, r#""#), - ]; - let actual_uris: Vec<_> = snapshot - .relations(&uri1, &Options::default(), &env::current_dir().unwrap()) - .into_iter() - .map(|doc| doc.uri.clone()) - .collect(); - - assert_eq!(actual_uris, vec![uri1, uri2]); - } - - #[test] - fn relations_parent_directory() { - let uri1 = Uri::parse("http://www.example.com/foo.tex").unwrap(); - let uri2 = Uri::parse("http://www.example.com/bar/baz.tex").unwrap(); - let mut snapshot = Snapshot::new(); - snapshot.0 = vec![ - create_simple_document(&uri1, Language::Latex, r#""#), - create_simple_document(&uri2, Language::Latex, r#"\input{../foo.tex}"#), - ]; - let actual_uris: Vec<_> = snapshot - .relations(&uri1, &Options::default(), &env::current_dir().unwrap()) - .into_iter() - .map(|doc| doc.uri.clone()) - .collect(); - - assert_eq!(actual_uris, vec![uri1, uri2]); - } - - #[test] - fn relations_invalid_include() { - let uri = Uri::parse("http://www.example.com/foo.tex").unwrap(); - let mut snapshot = Snapshot::new(); - snapshot.0 = vec![create_simple_document( - &uri, - Language::Latex, - r#"\include{<foo>?|bar|:}"#, - )]; - let actual_uris: Vec<_> = snapshot - .relations(&uri, &Options::default(), &env::current_dir().unwrap()) - .into_iter() - .map(|doc| doc.uri.clone()) - .collect(); - - assert_eq!(actual_uris, vec![uri]); - } - - #[test] - fn relations_bibliography() { - let uri1 = Uri::parse("http://www.example.com/foo.tex").unwrap(); - let uri2 = Uri::parse("http://www.example.com/bar.bib").unwrap(); - let mut snapshot = Snapshot::new(); - snapshot.0 = vec![ - create_simple_document(&uri1, Language::Latex, r#"\addbibresource{bar.bib}"#), - create_simple_document(&uri2, Language::Bibtex, r#""#), - ]; - let actual_uris: Vec<_> = snapshot - .relations(&uri2, &Options::default(), &env::current_dir().unwrap()) - .into_iter() - .map(|doc| doc.uri.clone()) - .collect(); - - assert_eq!(actual_uris, vec![uri2, uri1]); - } - - #[test] - fn relations_unknown_include() { - let uri = Uri::parse("http://www.example.com/foo.tex").unwrap(); - let mut snapshot = Snapshot::new(); - snapshot.0 = vec![create_simple_document( - &uri, - Language::Latex, - r#"\input{bar.tex}"#, - )]; - let actual_uris: Vec<_> = snapshot - .relations(&uri, &Options::default(), &env::current_dir().unwrap()) - .into_iter() - .map(|doc| doc.uri.clone()) - .collect(); - - assert_eq!(actual_uris, vec![uri]); - } - - #[test] - fn relations_include_cycle() { - let uri1 = Uri::parse("http://www.example.com/foo.tex").unwrap(); - let uri2 = Uri::parse("http://www.example.com/bar.tex").unwrap(); - let mut snapshot = Snapshot::new(); - snapshot.0 = vec![ - create_simple_document(&uri1, Language::Latex, r#"\include{bar}"#), - create_simple_document(&uri2, Language::Latex, r#"\input{foo.tex}"#), - ]; - let actual_uris: Vec<_> = snapshot - .relations(&uri1, &Options::default(), &env::current_dir().unwrap()) - .into_iter() - .map(|doc| doc.uri.clone()) - .collect(); - - assert_eq!(actual_uris, vec![uri1, uri2]); - } - - #[test] - fn relations_same_parent() { - let uri1 = Uri::parse("http://www.example.com/foo.tex").unwrap(); - let uri2 = Uri::parse("http://www.example.com/bar.tex").unwrap(); - let uri3 = Uri::parse("http://www.example.com/baz.tex").unwrap(); - let mut snapshot = Snapshot::new(); - snapshot.0 = vec![ - create_simple_document(&uri1, Language::Latex, r#"\input{bar.tex}\input{baz.tex}"#), - create_simple_document(&uri2, Language::Latex, r#""#), - create_simple_document(&uri3, Language::Latex, r#""#), - ]; - let actual_uris: Vec<_> = snapshot - .relations(&uri3, &Options::default(), &env::current_dir().unwrap()) - .into_iter() - .map(|doc| doc.uri.clone()) - .collect(); - - assert_eq!(actual_uris, vec![uri3, uri1, uri2]); - } - - #[test] - fn relations_aux_default_options() { - let uri1 = Uri::parse("http://www.example.com/foo.tex").unwrap(); - let uri2 = Uri::parse("http://www.example.com/foo.aux").unwrap(); - let mut snapshot = Snapshot::new(); - snapshot.0 = vec![ - create_simple_document(&uri1, Language::Latex, r#""#), - create_simple_document(&uri2, Language::Latex, r#""#), - ]; - let actual_uris: Vec<_> = snapshot - .relations(&uri1, &Options::default(), &env::current_dir().unwrap()) - .into_iter() - .map(|doc| doc.uri.clone()) - .collect(); - - assert_eq!(actual_uris, vec![uri1, uri2]); - } - - #[test] - fn relations_aux_output_directory() { - let cwd = env::current_dir().unwrap(); - let options = Options { - latex: Some(LatexOptions { - build: Some(LatexBuildOptions { - output_directory: Some(PathBuf::from("build")), - ..LatexBuildOptions::default() - }), - ..LatexOptions::default() - }), - ..Options::default() - }; - - let uri1 = Uri::from_file_path(cwd.join("foo.tex")).unwrap(); - let uri2 = Uri::from_file_path(cwd.join("build/foo.aux")).unwrap(); - let mut snapshot = Snapshot::new(); - snapshot.0 = vec![ - Arc::new(Document::open(DocumentParams { - uri: uri1.clone(), - text: String::new(), - language: Language::Latex, - resolver: &Resolver::default(), - options: &options, - current_dir: &cwd, - })), - Arc::new(Document::open(DocumentParams { - uri: uri2.clone(), - text: String::new(), - language: Language::Latex, - resolver: &Resolver::default(), - options: &options, - current_dir: &cwd, - })), - ]; - let actual_uris: Vec<_> = snapshot - .relations(&uri1, &options, &cwd) - .into_iter() - .map(|doc| doc.uri.clone()) - .collect(); - - assert_eq!(actual_uris, vec![uri1, uri2]); - } - - #[test] - fn relations_aux_root_directory() { - let cwd = env::current_dir().unwrap(); - let options = Options { - latex: Some(LatexOptions { - root_directory: Some(PathBuf::from(".")), - ..LatexOptions::default() - }), - ..Options::default() - }; - - let uri1 = Uri::from_file_path(cwd.join("src/foo.tex")).unwrap(); - let uri2 = Uri::from_file_path(cwd.join("foo.aux")).unwrap(); - let mut snapshot = Snapshot::new(); - snapshot.0 = vec![ - Arc::new(Document::open(DocumentParams { - uri: uri1.clone(), - text: String::new(), - language: Language::Latex, - resolver: &Resolver::default(), - options: &options, - current_dir: &cwd, - })), - Arc::new(Document::open(DocumentParams { - uri: uri2.clone(), - text: String::new(), - language: Language::Latex, - resolver: &Resolver::default(), - options: &options, - current_dir: &cwd, - })), - ]; - let actual_uris: Vec<_> = snapshot - .relations(&uri1, &options, &cwd) - .into_iter() - .map(|doc| doc.uri.clone()) - .collect(); - - assert_eq!(actual_uris, vec![uri1, uri2]); - } - - #[test] - fn relations_import() { - let uri1 = Uri::parse("http://www.example.com/foo.tex").unwrap(); - let uri2 = Uri::parse("http://www.example.com/bar/baz.tex").unwrap(); - let uri3 = Uri::parse("http://www.example.com/bar/qux/foo-bar.tex").unwrap(); - let mut snapshot = Snapshot::new(); - snapshot.0 = vec![ - create_simple_document(&uri1, Language::Latex, r#"\import{bar/}{baz.tex}"#), - create_simple_document(&uri2, Language::Latex, r#"\subimport{qux/}{foo-bar}"#), - create_simple_document(&uri3, Language::Latex, r#""#), - ]; - - let actual_uris: Vec<_> = snapshot - .relations(&uri1, &Options::default(), &env::current_dir().unwrap()) - .into_iter() - .map(|doc| doc.uri.clone()) - .collect(); - - assert_eq!(actual_uris, vec![uri1, uri2, uri3]); - } - - #[test] - fn parent() { - let uri1 = Uri::parse("http://www.example.com/foo.tex").unwrap(); - let uri2 = Uri::parse("http://www.example.com/bar.tex").unwrap(); - let mut snapshot = Snapshot::new(); - snapshot.0 = vec![ - create_simple_document(&uri1, Language::Latex, r#""#), - create_simple_document( - &uri2, - Language::Latex, - r#"\begin{document}\include{foo}\end{document}"#, - ), - ]; - let doc = snapshot - .parent(&uri1, &Options::default(), &env::current_dir().unwrap()) - .unwrap(); - assert_eq!(doc.uri, uri2); - } - - #[test] - fn parent_nothing_found() { - let uri1 = Uri::parse("http://www.example.com/foo.tex").unwrap(); - let uri2 = Uri::parse("http://www.example.com/bar.tex").unwrap(); - let mut snapshot = Snapshot::new(); - snapshot.0 = vec![ - create_simple_document(&uri1, Language::Latex, r#""#), - create_simple_document(&uri2, Language::Latex, r#"\begin{document}\end{document}"#), - ]; - let doc = snapshot.parent(&uri1, &Options::default(), &env::current_dir().unwrap()); - assert_eq!(doc, None); - } - - #[test] - fn expand_aux_file() { - let uri = Uri::parse("http://www.example.com/foo.tex").unwrap(); - let mut snapshot = Snapshot::new(); - snapshot.0 = vec![create_simple_document(&uri, Language::Latex, r#""#)]; - let expansion = snapshot.expand(&Options::default(), &env::current_dir().unwrap()); - assert_eq!( - expansion - .iter() - .map(|uri| uri.as_str()) - .filter(|uri| uri.ends_with(".aux")) - .collect_vec(), - vec!["http://www.example.com/foo.aux"] - ); - } - - #[test] - fn expand_local_package() { - let uri = Uri::parse("http://www.example.com/foo.tex").unwrap(); - let mut snapshot = Snapshot::new(); - snapshot.0 = vec![create_simple_document( - &uri, - Language::Latex, - r#"\usepackage{foo-bar-baz}"#, - )]; - let expansion = snapshot.expand(&Options::default(), &env::current_dir().unwrap()); - - assert_eq!( - expansion - .iter() - .map(|uri| uri.as_str()) - .filter(|uri| uri.ends_with(".sty")) - .collect_vec(), - vec!["http://www.example.com/foo-bar-baz.sty"] - ); - } - - #[test] - fn expand_system_package() { - let uri = Uri::parse("http://www.example.com/foo.tex").unwrap(); - let mut snapshot = Snapshot::new(); - snapshot.0 = vec![create_simple_document( - &uri, - Language::Latex, - r#"\usepackage{amsmath}"#, - )]; - let expansion = snapshot.expand(&Options::default(), &env::current_dir().unwrap()); - - assert_eq!( - expansion - .iter() - .map(|uri| uri.as_str()) - .filter(|uri| uri.ends_with(".sty")) - .collect_vec(), - Vec::<&str>::new() - ); - } - - #[test] - fn expand_subdirectory() { - let uri = Uri::parse("http://www.example.com/foo.tex").unwrap(); - let mut snapshot = Snapshot::new(); - snapshot.0 = vec![create_simple_document( - &uri, - Language::Latex, - r#"\include{bar/baz}"#, - )]; - let expansion = snapshot.expand(&Options::default(), &env::current_dir().unwrap()); - assert_eq!( - expansion - .iter() - .map(|uri| uri.as_str()) - .filter(|uri| uri.ends_with(".tex")) - .collect_vec(), - vec!["http://www.example.com/bar/baz.tex"] - ); - } - - #[test] - fn expand_import() { - let uri1 = Uri::parse("http://www.example.com/qux/foo.tex").unwrap(); - let uri2 = Uri::parse("http://www.example.com/qux/baz/bar.tex").unwrap(); - let mut snapshot = Snapshot::new(); - snapshot.0 = vec![ - create_simple_document( - &uri1, - Language::Latex, - r#"\import{.}{foo}\import{baz/}{bar}\import{baz/foo-bar/}{qux}"#, - ), - create_simple_document(&uri2, Language::Latex, r#""#), - ]; - let expansion = snapshot.expand(&Options::default(), &env::current_dir().unwrap()); - assert_eq!( - expansion - .iter() - .map(|uri| uri.as_str()) - .filter(|uri| uri.ends_with(".tex")) - .collect_vec(), - vec!["http://www.example.com/qux/baz/foo-bar/qux.tex"] - ) - } +pub fn create_workspace_full(context: Arc<ServerContext>) -> Result<impl Workspace> { + let workspace = Storage::new(context); + let workspace = DocumentWatcher::new(Arc::new(workspace))?; + let workspace = ParentExpander::new(workspace); + let workspace = ChildrenExpander::new(Arc::new(workspace)); + Ok(workspace) } |