summaryrefslogtreecommitdiff
path: root/support/texlab/src/workspace/feature.rs
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/src/workspace/feature.rs')
-rw-r--r--support/texlab/src/workspace/feature.rs246
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()))
+}