diff options
Diffstat (limited to 'support/texlab/src/workspace/feature.rs')
-rw-r--r-- | support/texlab/src/workspace/feature.rs | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/support/texlab/src/workspace/feature.rs b/support/texlab/src/workspace/feature.rs new file mode 100644 index 0000000000..815f5e1724 --- /dev/null +++ b/support/texlab/src/workspace/feature.rs @@ -0,0 +1,246 @@ +use super::{Document, DocumentView, Workspace, WorkspaceBuilder}; +use futures_boxed::boxed; +use lsp_types::*; +use std::sync::Arc; + +pub struct FeatureRequest<P> { + pub params: P, + pub view: DocumentView, + pub client_capabilities: Arc<ClientCapabilities>, + pub distribution: Arc<Box<dyn tex::Distribution>>, +} + +impl<P> FeatureRequest<P> { + pub fn workspace(&self) -> &Workspace { + &self.view.workspace + } + + pub fn document(&self) -> &Document { + &self.view.document + } + + pub fn related_documents(&self) -> &[Arc<Document>] { + &self.view.related_documents + } +} + +pub trait FeatureProvider { + type Params; + type Output; + + #[boxed] + async fn execute<'a>(&'a self, request: &'a FeatureRequest<Self::Params>) -> Self::Output; +} + +type ListProvider<P, O> = Box<dyn FeatureProvider<Params = P, Output = Vec<O>> + Send + Sync>; + +#[derive(Default)] +pub struct ConcatProvider<P, O> { + providers: Vec<ListProvider<P, O>>, +} + +impl<P, O> ConcatProvider<P, O> { + pub fn new(providers: Vec<ListProvider<P, O>>) -> Self { + Self { providers } + } +} + +impl<P, O> FeatureProvider for ConcatProvider<P, O> +where + P: Send + Sync, + O: Send + Sync, +{ + type Params = P; + type Output = Vec<O>; + + #[boxed] + async fn execute<'a>(&'a self, request: &'a FeatureRequest<P>) -> Vec<O> { + let mut items = Vec::new(); + for provider in &self.providers { + items.append(&mut provider.execute(request).await); + } + items + } +} + +type OptionProvider<P, O> = Box<dyn FeatureProvider<Params = P, Output = Option<O>> + Send + Sync>; + +#[derive(Default)] +pub struct ChoiceProvider<P, O> { + providers: Vec<OptionProvider<P, O>>, +} + +impl<P, O> ChoiceProvider<P, O> { + pub fn new(providers: Vec<OptionProvider<P, O>>) -> Self { + Self { providers } + } +} + +impl<P, O> FeatureProvider for ChoiceProvider<P, O> +where + P: Send + Sync, + O: Send + Sync, +{ + type Params = P; + type Output = Option<O>; + + #[boxed] + async fn execute<'a>(&'a self, request: &'a FeatureRequest<P>) -> Option<O> { + for provider in &self.providers { + let item = provider.execute(request).await; + if item.is_some() { + return item; + } + } + None + } +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct FeatureSpecFile { + name: &'static str, + text: &'static str, +} + +pub struct FeatureSpec { + pub files: Vec<FeatureSpecFile>, + pub main_file: &'static str, + pub position: Position, + pub new_name: &'static str, + pub include_declaration: bool, + pub client_capabilities: ClientCapabilities, + pub distribution: Box<dyn tex::Distribution>, +} + +impl Default for FeatureSpec { + fn default() -> Self { + Self { + files: Vec::new(), + main_file: "", + position: Position::new(0, 0), + new_name: "", + include_declaration: false, + client_capabilities: ClientCapabilities::default(), + distribution: Box::new(tex::Unknown::default()), + } + } +} + +impl FeatureSpec { + pub fn file(name: &'static str, text: &'static str) -> FeatureSpecFile { + FeatureSpecFile { name, text } + } + + pub fn uri(name: &str) -> Url { + let path = std::env::temp_dir().join(name); + Url::from_file_path(path).unwrap() + } + + fn identifier(&self) -> TextDocumentIdentifier { + let uri = Self::uri(self.main_file); + TextDocumentIdentifier::new(uri) + } + + fn view(&self) -> DocumentView { + let mut builder = WorkspaceBuilder::new(); + for file in &self.files { + builder.document(file.name, file.text); + } + let workspace = builder.workspace; + let main_uri = Self::uri(self.main_file); + let main_document = workspace.find(&main_uri.into()).unwrap(); + DocumentView::new(Arc::new(workspace), main_document) + } + + fn request<T>(self, params: T) -> FeatureRequest<T> { + FeatureRequest { + params, + view: self.view(), + client_capabilities: Arc::new(self.client_capabilities), + distribution: Arc::new(self.distribution), + } + } +} + +impl Into<FeatureRequest<TextDocumentPositionParams>> for FeatureSpec { + fn into(self) -> FeatureRequest<TextDocumentPositionParams> { + let params = TextDocumentPositionParams::new(self.identifier(), self.position); + self.request(params) + } +} + +impl Into<FeatureRequest<CompletionParams>> for FeatureSpec { + fn into(self) -> FeatureRequest<CompletionParams> { + let params = CompletionParams { + text_document_position: TextDocumentPositionParams::new( + self.identifier(), + self.position, + ), + context: None, + }; + self.request(params) + } +} + +impl Into<FeatureRequest<FoldingRangeParams>> for FeatureSpec { + fn into(self) -> FeatureRequest<FoldingRangeParams> { + let params = FoldingRangeParams { + text_document: self.identifier(), + }; + self.request(params) + } +} + +impl Into<FeatureRequest<DocumentLinkParams>> for FeatureSpec { + fn into(self) -> FeatureRequest<DocumentLinkParams> { + let params = DocumentLinkParams { + text_document: self.identifier(), + }; + self.request(params) + } +} + +impl Into<FeatureRequest<ReferenceParams>> for FeatureSpec { + fn into(self) -> FeatureRequest<ReferenceParams> { + let params = ReferenceParams { + text_document_position: TextDocumentPositionParams::new( + self.identifier(), + self.position, + ), + context: ReferenceContext { + include_declaration: self.include_declaration, + }, + }; + self.request(params) + } +} + +impl Into<FeatureRequest<RenameParams>> for FeatureSpec { + fn into(self) -> FeatureRequest<RenameParams> { + let params = RenameParams { + text_document_position: TextDocumentPositionParams::new( + self.identifier(), + self.position, + ), + new_name: self.new_name.to_owned(), + }; + self.request(params) + } +} + +impl Into<FeatureRequest<DocumentSymbolParams>> for FeatureSpec { + fn into(self) -> FeatureRequest<DocumentSymbolParams> { + let params = DocumentSymbolParams { + text_document: self.identifier(), + }; + self.request(params) + } +} + +pub fn test_feature<F, P, O, S>(provider: F, spec: S) -> O +where + F: FeatureProvider<Params = P, Output = O>, + S: Into<FeatureRequest<P>>, +{ + futures::executor::block_on(provider.execute(&spec.into())) +} |