use crate::{ components::{Component, COMPONENT_DATABASE}, protocol::*, tex::{Distribution, Language, UnknownDistribution}, workspace::{Document, DocumentContent, DocumentParams, Snapshot}, }; use async_trait::async_trait; use itertools::Itertools; use std::{ env, path::{Path, PathBuf}, sync::Arc, }; #[derive(Debug, PartialEq, Eq, Clone)] pub struct DocumentView { pub snapshot: Arc, pub current: Arc, pub related: Vec>, } impl DocumentView { pub fn analyze( snapshot: Arc, current: Arc, options: &Options, current_dir: &Path, ) -> Self { let related = snapshot.relations(¤t.uri, options, current_dir); Self { snapshot, current, related, } } pub fn components(&self) -> Vec<&'static Component> { let mut start_components = vec![COMPONENT_DATABASE.kernel()]; for doc in &self.related { if let DocumentContent::Latex(table) = &doc.content { table .components .iter() .flat_map(|file| COMPONENT_DATABASE.find(file)) .for_each(|component| start_components.push(component)) } } let mut all_components = Vec::new(); for component in start_components { all_components.push(component); component .references .iter() .flat_map(|file| COMPONENT_DATABASE.find(&file)) .for_each(|component| all_components.push(component)) } all_components .into_iter() .unique_by(|component| &component.file_names) .collect() } } #[derive(Clone)] pub struct FeatureRequest

{ pub params: P, pub view: DocumentView, pub distro: Arc, pub client_capabilities: Arc, pub options: Options, pub current_dir: Arc, } impl

FeatureRequest

{ pub fn snapshot(&self) -> &Snapshot { &self.view.snapshot } pub fn current(&self) -> &Document { &self.view.current } pub fn related(&self) -> &[Arc] { &self.view.related } } #[async_trait] pub trait FeatureProvider { type Params; type Output; async fn execute<'a>(&'a self, req: &'a FeatureRequest) -> Self::Output; } type ListProvider = Box> + Send + Sync>; #[derive(Default)] pub struct ConcatProvider { providers: Vec>, } impl ConcatProvider { pub fn new(providers: Vec>) -> Self { Self { providers } } } #[async_trait] impl FeatureProvider for ConcatProvider where P: Send + Sync, O: Send + Sync, { type Params = P; type Output = Vec; async fn execute<'a>(&'a self, req: &'a FeatureRequest

) -> Vec { let mut items = Vec::new(); for provider in &self.providers { items.append(&mut provider.execute(req).await); } items } } type OptionProvider = Box> + Send + Sync>; #[derive(Default)] pub struct ChoiceProvider { providers: Vec>, } impl ChoiceProvider { pub fn new(providers: Vec>) -> Self { Self { providers } } } #[async_trait] impl FeatureProvider for ChoiceProvider where P: Send + Sync, O: Send + Sync, { type Params = P; type Output = Option; async fn execute<'a>(&'a self, req: &'a FeatureRequest

) -> Option { for provider in &self.providers { let item = provider.execute(req).await; if item.is_some() { return item; } } None } } #[derive(Debug)] pub struct FeatureTester { main: String, files: Vec<(String, String)>, distro: Arc, position: Position, new_name: String, include_declaration: bool, client_capabilities: Arc, current_dir: Arc, root_dir: Option, output_dir: Option, } impl Default for FeatureTester { fn default() -> Self { Self::new() } } impl FeatureTester { pub fn new() -> Self { Self { main: String::new(), files: Vec::new(), distro: Arc::new(UnknownDistribution::default()), position: Position::default(), new_name: String::new(), include_declaration: false, client_capabilities: Arc::default(), current_dir: Arc::new(env::temp_dir()), root_dir: None, output_dir: None, } } pub fn main>(&mut self, name: S) -> &mut Self { self.main = name.into(); self } pub fn file(&mut self, name: S, text: T) -> &mut Self where S: Into, T: Into, { self.files.push((name.into(), text.into())); self } pub fn position(&mut self, line: u64, character: u64) -> &mut Self { self.position = Position::new(line, character); self } pub fn new_name>(&mut self, value: S) -> &mut Self { self.new_name = value.into(); self } pub fn include_declaration(&mut self) -> &mut Self { self.include_declaration = true; self } pub fn root_directory>(&mut self, path: P) -> &mut Self { self.root_dir = Some(path.into()); self } pub fn output_directory>(&mut self, path: P) -> &mut Self { self.output_dir = Some(path.into()); self } pub fn uri(name: &str) -> Uri { let path = env::temp_dir().join(name); Uri::from_file_path(path).unwrap() } fn identifier(&self) -> TextDocumentIdentifier { let uri = Self::uri(&self.main); TextDocumentIdentifier::new(uri.into()) } fn options(&self) -> Options { Options { latex: Some(LatexOptions { build: Some(LatexBuildOptions { output_directory: self.output_dir.clone(), ..LatexBuildOptions::default() }), root_directory: self.root_dir.clone(), ..LatexOptions::default() }), ..Options::default() } } async fn view(&self) -> DocumentView { let mut snapshot = Snapshot::new(); let resolver = self.distro.resolver().await; let options = self.options(); for (name, text) in &self.files { let uri = Self::uri(name); let path = uri.to_file_path().unwrap(); let language = path .extension() .and_then(|ext| ext.to_str()) .and_then(Language::by_extension) .unwrap(); let doc = Document::open(DocumentParams { uri, text: text.trim().into(), language, resolver: &resolver, options: &options, current_dir: &self.current_dir, }); snapshot.push(doc); } let current = snapshot.find(&Self::uri(&self.main)).unwrap(); DocumentView::analyze(Arc::new(snapshot), current, &options, &self.current_dir) } async fn request

(&self, params: P) -> FeatureRequest

{ FeatureRequest { params, view: self.view().await, client_capabilities: Arc::clone(&self.client_capabilities), distro: self.distro.clone(), options: self.options(), current_dir: Arc::clone(&self.current_dir), } } pub async fn test_position(&self, provider: F) -> O where F: FeatureProvider, { let text_document = self.identifier(); let params = TextDocumentPositionParams::new(text_document, self.position); let req = self.request(params).await; provider.execute(&req).await } pub async fn test_completion(&self, provider: F) -> O where F: FeatureProvider, { let req = self.test_completion_request().await; provider.execute(&req).await } pub async fn test_completion_request(&self) -> FeatureRequest { let params = CompletionParams { text_document_position: TextDocumentPositionParams::new( self.identifier(), self.position, ), context: None, work_done_progress_params: WorkDoneProgressParams::default(), partial_result_params: PartialResultParams::default(), }; self.request(params).await } pub async fn test_folding(&self, provider: F) -> O where F: FeatureProvider, { let text_document = self.identifier(); let params = FoldingRangeParams { text_document, work_done_progress_params: WorkDoneProgressParams::default(), partial_result_params: PartialResultParams::default(), }; let req = self.request(params).await; provider.execute(&req).await } pub async fn test_link(&self, provider: F) -> O where F: FeatureProvider, { let text_document = self.identifier(); let params = DocumentLinkParams { text_document, work_done_progress_params: WorkDoneProgressParams::default(), partial_result_params: PartialResultParams::default(), }; let req = self.request(params).await; provider.execute(&req).await } pub async fn test_reference(&self, provider: F) -> O where F: FeatureProvider, { let params = ReferenceParams { text_document_position: TextDocumentPositionParams::new( self.identifier(), self.position, ), context: ReferenceContext { include_declaration: self.include_declaration, }, work_done_progress_params: WorkDoneProgressParams::default(), partial_result_params: PartialResultParams::default(), }; let req = self.request(params).await; provider.execute(&req).await } pub async fn test_rename(&self, provider: F) -> O where F: FeatureProvider, { let params = RenameParams { text_document_position: TextDocumentPositionParams::new( self.identifier(), self.position, ), new_name: self.new_name.clone(), work_done_progress_params: WorkDoneProgressParams::default(), }; let req = self.request(params).await; provider.execute(&req).await } pub async fn test_symbol(&self, provider: F) -> O where F: FeatureProvider, { let text_document = self.identifier(); let params = DocumentSymbolParams { text_document, work_done_progress_params: WorkDoneProgressParams::default(), partial_result_params: PartialResultParams::default(), }; let req = self.request(params).await; provider.execute(&req).await } }