diff --git a/prisma-fmt/src/code_actions.rs b/prisma-fmt/src/code_actions.rs index c1f52f62bd84..3e290d9b166d 100644 --- a/prisma-fmt/src/code_actions.rs +++ b/prisma-fmt/src/code_actions.rs @@ -7,39 +7,23 @@ mod relations; use log::warn; use lsp_types::{CodeActionOrCommand, CodeActionParams, Diagnostic, Range, TextEdit, Url, WorkspaceEdit}; use psl::{ - diagnostics::{FileId, Span}, + diagnostics::Span, parser_database::{ - ast, walkers::{ModelWalker, RefinedRelationWalker, ScalarFieldWalker}, - ParserDatabase, SourceFile, + SourceFile, }, - schema_ast::ast::{Attribute, IndentationType, NewlineType, WithSpan}, - Configuration, Datasource, PreviewFeature, + schema_ast::ast::{self, Attribute, IndentationType, NewlineType, WithSpan}, + PreviewFeature, }; use std::collections::HashMap; -pub(super) struct CodeActionsContext<'a> { - pub(super) db: &'a ParserDatabase, - pub(super) config: &'a Configuration, - pub(super) initiating_file_id: FileId, - pub(super) lsp_params: CodeActionParams, -} +use crate::LSPContext; -impl<'a> CodeActionsContext<'a> { - pub(super) fn initiating_file_source(&self) -> &str { - self.db.source(self.initiating_file_id) - } - - pub(super) fn initiating_file_uri(&self) -> &str { - self.db.file_name(self.initiating_file_id) - } +pub(super) type CodeActionsContext<'a> = LSPContext<'a, CodeActionParams>; +impl<'a> CodeActionsContext<'a> { pub(super) fn diagnostics(&self) -> &[Diagnostic] { - &self.lsp_params.context.diagnostics - } - - pub(super) fn datasource(&self) -> Option<&Datasource> { - self.config.datasources.first() + &self.params.context.diagnostics } /// A function to find diagnostics matching the given span. Used for @@ -54,7 +38,6 @@ impl<'a> CodeActionsContext<'a> { )) }) } - pub(super) fn diagnostics_for_span_with_message(&self, span: Span, message: &str) -> Vec { self.diagnostics_for_span(span) .filter(|diag| diag.message.contains(message)) @@ -88,7 +71,7 @@ pub(crate) fn available_actions( db: &validated_schema.db, config, initiating_file_id, - lsp_params: params, + params: ¶ms, }; let initiating_ast = validated_schema.db.ast(initiating_file_id); diff --git a/prisma-fmt/src/code_actions/block.rs b/prisma-fmt/src/code_actions/block.rs index a968b2e6ad45..7c02a400db01 100644 --- a/prisma-fmt/src/code_actions/block.rs +++ b/prisma-fmt/src/code_actions/block.rs @@ -33,7 +33,7 @@ pub(super) fn create_missing_block_for_model( diagnostics.iter().for_each(|diag| { push_missing_block( diag, - context.lsp_params.text_document.uri.clone(), + context.params.text_document.uri.clone(), range, "model", actions, @@ -41,7 +41,7 @@ pub(super) fn create_missing_block_for_model( ); push_missing_block( diag, - context.lsp_params.text_document.uri.clone(), + context.params.text_document.uri.clone(), range, "enum", actions, @@ -52,7 +52,7 @@ pub(super) fn create_missing_block_for_model( if ds.active_provider == "mongodb" { push_missing_block( diag, - context.lsp_params.text_document.uri.clone(), + context.params.text_document.uri.clone(), range, "type", actions, @@ -87,7 +87,7 @@ pub(super) fn create_missing_block_for_type( diagnostics.iter().for_each(|diag| { push_missing_block( diag, - context.lsp_params.text_document.uri.clone(), + context.params.text_document.uri.clone(), range, "type", actions, @@ -95,7 +95,7 @@ pub(super) fn create_missing_block_for_type( ); push_missing_block( diag, - context.lsp_params.text_document.uri.clone(), + context.params.text_document.uri.clone(), range, "enum", actions, diff --git a/prisma-fmt/src/code_actions/relations.rs b/prisma-fmt/src/code_actions/relations.rs index 850c1182dfe2..2803fc6d784c 100644 --- a/prisma-fmt/src/code_actions/relations.rs +++ b/prisma-fmt/src/code_actions/relations.rs @@ -75,7 +75,7 @@ pub(super) fn add_referencing_side_unique( ); let mut changes = HashMap::new(); - changes.insert(context.lsp_params.text_document.uri.clone(), vec![text]); + changes.insert(context.params.text_document.uri.clone(), vec![text]); let edit = WorkspaceEdit { changes: Some(changes), @@ -278,7 +278,7 @@ pub(super) fn add_index_for_relation_fields( }; let mut changes = HashMap::new(); - changes.insert(context.lsp_params.text_document.uri.clone(), vec![text]); + changes.insert(context.params.text_document.uri.clone(), vec![text]); let edit = WorkspaceEdit { changes: Some(changes), diff --git a/prisma-fmt/src/lib.rs b/prisma-fmt/src/lib.rs index 8f01182a33f3..9218b5efbfbe 100644 --- a/prisma-fmt/src/lib.rs +++ b/prisma-fmt/src/lib.rs @@ -12,9 +12,46 @@ mod validate; use log::*; use lsp_types::{Position, Range}; -use psl::{diagnostics::FileId, parser_database::ast}; +use psl::{ + datamodel_connector::Connector, + diagnostics::FileId, + parser_database::{ast, ParserDatabase}, + Configuration, Datasource, Generator, +}; use schema_file_input::SchemaFileInput; +#[derive(Debug, Clone, Copy)] +pub(crate) struct LSPContext<'a, T> { + pub(crate) db: &'a ParserDatabase, + pub(crate) config: &'a Configuration, + pub(crate) initiating_file_id: FileId, + pub(crate) params: &'a T, +} + +impl<'a, T> LSPContext<'a, T> { + pub(crate) fn initiating_file_source(&self) -> &str { + self.db.source(self.initiating_file_id) + } + + pub(crate) fn initiating_file_uri(&self) -> &str { + self.db.file_name(self.initiating_file_id) + } + + pub(crate) fn datasource(&self) -> Option<&Datasource> { + self.config.datasources.first() + } + + pub(crate) fn connector(&self) -> &'static dyn Connector { + self.datasource() + .map(|ds| ds.active_connector) + .unwrap_or(&psl::datamodel_connector::EmptyDatamodelConnector) + } + + pub(crate) fn generator(&self) -> Option<&'a Generator> { + self.config.generators.first() + } +} + /// The API is modelled on an LSP [completion /// request](https://github.com/microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-16.md#textDocument_completion). /// Input and output are both JSON, the request being a `CompletionParams` object and the response diff --git a/prisma-fmt/src/text_document_completion.rs b/prisma-fmt/src/text_document_completion.rs index 73db0b9f6909..28497152a85f 100644 --- a/prisma-fmt/src/text_document_completion.rs +++ b/prisma-fmt/src/text_document_completion.rs @@ -2,17 +2,36 @@ use enumflags2::BitFlags; use log::*; use lsp_types::*; use psl::{ - datamodel_connector::Connector, - diagnostics::{FileId, Span}, + diagnostics::Span, error_tolerant_parse_configuration, parser_database::{ast, ParserDatabase, SourceFile}, - Configuration, Datasource, Diagnostics, Generator, PreviewFeature, + Diagnostics, PreviewFeature, }; -use crate::position_to_offset; +use crate::{position_to_offset, LSPContext}; mod datasource; +pub(super) type CompletionContext<'a> = LSPContext<'a, CompletionParams>; + +impl<'a> CompletionContext<'a> { + pub(super) fn namespaces(&'a self) -> &'a [(String, Span)] { + self.datasource().map(|ds| ds.namespaces.as_slice()).unwrap_or(&[]) + } + pub(super) fn preview_features(&self) -> BitFlags { + self.generator() + .and_then(|gen| gen.preview_features) + .unwrap_or_default() + } + + pub(super) fn position(&self) -> Option { + let pos = self.params.text_document_position.position; + let initiating_doc = self.initiating_file_source(); + + super::position_to_offset(&pos, initiating_doc) + } +} + pub(crate) fn empty_completion_list() -> CompletionList { CompletionList { is_incomplete: true, @@ -38,20 +57,10 @@ pub(crate) fn completion(schema_files: Vec<(String, SourceFile)>, params: Comple return empty_completion_list(); }; - let initiating_doc = db.source(initiating_file_id); - let position = if let Some(pos) = super::position_to_offset(¶ms.text_document_position.position, initiating_doc) - { - pos - } else { - warn!("Received a position outside of the document boundaries in CompletionParams"); - return empty_completion_list(); - }; - let ctx = CompletionContext { config: &config, params: ¶ms, db: &db, - position, initiating_file_id, }; @@ -60,48 +69,22 @@ pub(crate) fn completion(schema_files: Vec<(String, SourceFile)>, params: Comple list } -#[derive(Debug, Clone, Copy)] -struct CompletionContext<'a> { - config: &'a Configuration, - params: &'a CompletionParams, - db: &'a ParserDatabase, - position: usize, - initiating_file_id: FileId, -} - -impl<'a> CompletionContext<'a> { - pub(crate) fn connector(self) -> &'static dyn Connector { - self.datasource() - .map(|ds| ds.active_connector) - .unwrap_or(&psl::datamodel_connector::EmptyDatamodelConnector) - } - - pub(crate) fn namespaces(self) -> &'a [(String, Span)] { - self.datasource().map(|ds| ds.namespaces.as_slice()).unwrap_or(&[]) - } - - pub(crate) fn preview_features(self) -> BitFlags { - self.generator() - .and_then(|gen| gen.preview_features) - .unwrap_or_default() - } - - fn datasource(self) -> Option<&'a Datasource> { - self.config.datasources.first() - } - - fn generator(self) -> Option<&'a Generator> { - self.config.generators.first() - } -} - fn push_ast_completions(ctx: CompletionContext<'_>, completion_list: &mut CompletionList) { + let position = match ctx.position() { + Some(pos) => pos, + None => { + warn!("Received a position outside of the document boundaries in CompletionParams"); + completion_list.is_incomplete = true; + return; + } + }; + let relation_mode = ctx .config .relation_mode() .unwrap_or_else(|| ctx.connector().default_relation_mode()); - match ctx.db.ast(ctx.initiating_file_id).find_at_position(ctx.position) { + match ctx.db.ast(ctx.initiating_file_id).find_at_position(position) { ast::SchemaPosition::Model( _model_id, ast::ModelPosition::Field(_, ast::FieldPosition::Attribute("relation", _, Some(attr_name))), @@ -132,23 +115,23 @@ fn push_ast_completions(ctx: CompletionContext<'_>, completion_list: &mut Comple } ast::SchemaPosition::DataSource(_source_id, ast::SourcePosition::Source) => { - if !ds_has_prop(ctx, "provider") { + if !ds_has_prop(&ctx, "provider") { datasource::provider_completion(completion_list); } - if !ds_has_prop(ctx, "url") { + if !ds_has_prop(&ctx, "url") { datasource::url_completion(completion_list); } - if !ds_has_prop(ctx, "shadowDatabaseUrl") { + if !ds_has_prop(&ctx, "shadowDatabaseUrl") { datasource::shadow_db_completion(completion_list); } - if !ds_has_prop(ctx, "directUrl") { + if !ds_has_prop(&ctx, "directUrl") { datasource::direct_url_completion(completion_list); } - if !ds_has_prop(ctx, "relationMode") { + if !ds_has_prop(&ctx, "relationMode") { datasource::relation_mode_completion(completion_list); } @@ -181,7 +164,7 @@ fn push_ast_completions(ctx: CompletionContext<'_>, completion_list: &mut Comple } } -fn ds_has_prop(ctx: CompletionContext<'_>, prop: &str) -> bool { +fn ds_has_prop(ctx: &CompletionContext<'_>, prop: &str) -> bool { if let Some(ds) = ctx.datasource() { match prop { "relationMode" => ds.relation_mode_defined(),