use super::{Document, DocumentView, Workspace, WorkspaceBuilder};
use futures_boxed::boxed;
use lsp_types::*;
use std::sync::Arc;
pub struct FeatureRequest
{
pub params: P,
pub view: DocumentView,
pub client_capabilities: Arc,
pub distribution: Arc>,
}
impl FeatureRequest
{
pub fn workspace(&self) -> &Workspace {
&self.view.workspace
}
pub fn document(&self) -> &Document {
&self.view.document
}
pub fn related_documents(&self) -> &[Arc] {
&self.view.related_documents
}
}
pub trait FeatureProvider {
type Params;
type Output;
#[boxed]
async fn execute<'a>(&'a self, request: &'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 }
}
}
impl FeatureProvider for ConcatProvider
where
P: Send + Sync,
O: Send + Sync,
{
type Params = P;
type Output = Vec;
#[boxed]
async fn execute<'a>(&'a self, request: &'a FeatureRequest) -> Vec {
let mut items = Vec::new();
for provider in &self.providers {
items.append(&mut provider.execute(request).await);
}
items
}
}
type OptionProvider = Box> + Send + Sync>;
#[derive(Default)]
pub struct ChoiceProvider {
providers: Vec>,
}
impl ChoiceProvider
{
pub fn new(providers: Vec>) -> Self {
Self { providers }
}
}
impl FeatureProvider for ChoiceProvider
where
P: Send + Sync,
O: Send + Sync,
{
type Params = P;
type Output = Option;
#[boxed]
async fn execute<'a>(&'a self, request: &'a FeatureRequest) -> Option {
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,
pub main_file: &'static str,
pub position: Position,
pub new_name: &'static str,
pub include_declaration: bool,
pub client_capabilities: ClientCapabilities,
pub distribution: Box,
}
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(self, params: T) -> FeatureRequest {
FeatureRequest {
params,
view: self.view(),
client_capabilities: Arc::new(self.client_capabilities),
distribution: Arc::new(self.distribution),
}
}
}
impl Into> for FeatureSpec {
fn into(self) -> FeatureRequest {
let params = TextDocumentPositionParams::new(self.identifier(), self.position);
self.request(params)
}
}
impl Into> for FeatureSpec {
fn into(self) -> FeatureRequest {
let params = CompletionParams {
text_document_position: TextDocumentPositionParams::new(
self.identifier(),
self.position,
),
context: None,
};
self.request(params)
}
}
impl Into> for FeatureSpec {
fn into(self) -> FeatureRequest {
let params = FoldingRangeParams {
text_document: self.identifier(),
};
self.request(params)
}
}
impl Into> for FeatureSpec {
fn into(self) -> FeatureRequest {
let params = DocumentLinkParams {
text_document: self.identifier(),
};
self.request(params)
}
}
impl Into> for FeatureSpec {
fn into(self) -> FeatureRequest {
let params = ReferenceParams {
text_document_position: TextDocumentPositionParams::new(
self.identifier(),
self.position,
),
context: ReferenceContext {
include_declaration: self.include_declaration,
},
};
self.request(params)
}
}
impl Into> for FeatureSpec {
fn into(self) -> FeatureRequest {
let params = RenameParams {
text_document_position: TextDocumentPositionParams::new(
self.identifier(),
self.position,
),
new_name: self.new_name.to_owned(),
};
self.request(params)
}
}
impl Into> for FeatureSpec {
fn into(self) -> FeatureRequest {
let params = DocumentSymbolParams {
text_document: self.identifier(),
};
self.request(params)
}
}
pub fn test_feature(provider: F, spec: S) -> O
where
F: FeatureProvider,
S: Into>,
{
futures::executor::block_on(provider.execute(&spec.into()))
}