From 745892fbddea56040139108277e728b53fd8fc11 Mon Sep 17 00:00:00 2001 From: Norbert Preining Date: Thu, 28 May 2020 03:03:21 +0000 Subject: CTAN sync 202005280303 --- support/texlab/src/feature.rs | 405 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 405 insertions(+) create mode 100644 support/texlab/src/feature.rs (limited to 'support/texlab/src/feature.rs') diff --git a/support/texlab/src/feature.rs b/support/texlab/src/feature.rs new file mode 100644 index 0000000000..2b32b64188 --- /dev/null +++ b/support/texlab/src/feature.rs @@ -0,0 +1,405 @@ +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 + } +} -- cgit v1.2.3