diff --git a/prisma-fmt/src/code_actions.rs b/prisma-fmt/src/code_actions.rs index 6b96e6f8c7c7..bacad1ac79b6 100644 --- a/prisma-fmt/src/code_actions.rs +++ b/prisma-fmt/src/code_actions.rs @@ -4,19 +4,72 @@ mod relation_mode; mod relations; use log::warn; -use lsp_types::{CodeActionOrCommand, CodeActionParams, Diagnostic, Range, TextEdit, WorkspaceEdit}; +use lsp_types::{CodeActionOrCommand, CodeActionParams, Diagnostic, Range, TextEdit, Url, WorkspaceEdit}; use psl::{ - diagnostics::Span, + diagnostics::{FileId, Span}, parser_database::{ ast, walkers::{ModelWalker, RefinedRelationWalker, ScalarFieldWalker}, - SourceFile, + ParserDatabase, SourceFile, }, schema_ast::ast::{Attribute, IndentationType, NewlineType, WithSpan}, - PreviewFeature, + Configuration, Datasource, 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, +} + +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) fn diagnostics(&self) -> &[Diagnostic] { + &self.lsp_params.context.diagnostics + } + + pub(super) fn datasource(&self) -> Option<&Datasource> { + self.config.datasources.first() + } + + /// A function to find diagnostics matching the given span. Used for + /// copying the diagnostics to a code action quick fix. + #[track_caller] + pub(super) fn diagnostics_for_span(&self, span: ast::Span) -> Option> { + if span.file_id != self.initiating_file_id { + return None; + } + + let res: Vec<_> = self + .diagnostics() + .iter() + .filter(|diag| { + span.overlaps(crate::range_to_span( + diag.range, + self.initiating_file_source(), + self.initiating_file_id, + )) + }) + .cloned() + .collect(); + + if res.is_empty() { + None + } else { + Some(res) + } + } +} + pub(crate) fn empty_code_actions() -> Vec { Vec::new() } @@ -38,14 +91,16 @@ pub(crate) fn available_actions( return vec![]; }; + let context = CodeActionsContext { + db: &validated_schema.db, + config, + initiating_file_id, + lsp_params: params, + }; + let initiating_ast = validated_schema.db.ast(initiating_file_id); for source in initiating_ast.sources() { - relation_mode::edit_referential_integrity( - &mut actions, - ¶ms, - validated_schema.db.source(initiating_file_id), - source, - ) + relation_mode::edit_referential_integrity(&mut actions, &context, source) } // models AND views @@ -55,45 +110,21 @@ pub(crate) fn available_actions( .chain(validated_schema.db.walk_views_in_file(initiating_file_id)) { if config.preview_features().contains(PreviewFeature::MultiSchema) { - multi_schema::add_schema_block_attribute_model( - &mut actions, - ¶ms, - validated_schema.db.source(initiating_file_id), - config, - model, - ); - - multi_schema::add_schema_to_schemas( - &mut actions, - ¶ms, - validated_schema.db.source(initiating_file_id), - config, - model, - ); + multi_schema::add_schema_block_attribute_model(&mut actions, &context, model); + + multi_schema::add_schema_to_schemas(&mut actions, &context, model); } if matches!(datasource, Some(ds) if ds.active_provider == "mongodb") { - mongodb::add_at_map_for_id(&mut actions, ¶ms, validated_schema.db.source_assert_single(), model); - - mongodb::add_native_for_auto_id( - &mut actions, - ¶ms, - validated_schema.db.source(initiating_file_id), - model, - datasource.unwrap(), - ); + mongodb::add_at_map_for_id(&mut actions, &context, model); + + mongodb::add_native_for_auto_id(&mut actions, &context, model, datasource.unwrap()); } } for enumerator in validated_schema.db.walk_enums_in_file(initiating_file_id) { if config.preview_features().contains(PreviewFeature::MultiSchema) { - multi_schema::add_schema_block_attribute_enum( - &mut actions, - ¶ms, - validated_schema.db.source(initiating_file_id), - config, - enumerator, - ) + multi_schema::add_schema_block_attribute_enum(&mut actions, &context, enumerator) } } @@ -104,44 +135,24 @@ pub(crate) fn available_actions( None => continue, }; - if relation.referenced_model().is_defined_in_file(initiating_file_id) { - relations::add_referenced_side_unique( - &mut actions, - ¶ms, - validated_schema.db.source(initiating_file_id), - complete_relation, - ); + relations::add_referenced_side_unique(&mut actions, &context, complete_relation); + + if relation.is_one_to_one() { + relations::add_referencing_side_unique(&mut actions, &context, complete_relation); } if relation.referencing_model().is_defined_in_file(initiating_file_id) { - if relation.is_one_to_one() { - relations::add_referencing_side_unique( - &mut actions, - ¶ms, - validated_schema.db.source(initiating_file_id), - complete_relation, - ); - } - if validated_schema.relation_mode().is_prisma() { relations::add_index_for_relation_fields( &mut actions, - ¶ms, - validated_schema.db.source(initiating_file_id), + &context, complete_relation.referencing_field(), ); } } if validated_schema.relation_mode().uses_foreign_keys() { - relation_mode::replace_set_default_mysql( - &mut actions, - ¶ms, - initiating_file_id, - validated_schema.db.source(initiating_file_id), - complete_relation, - config, - ) + relation_mode::replace_set_default_mysql(&mut actions, &context, complete_relation) } } } @@ -149,27 +160,6 @@ pub(crate) fn available_actions( actions } -/// A function to find diagnostics matching the given span. Used for -/// copying the diagnostics to a code action quick fix. -#[track_caller] -pub(super) fn diagnostics_for_span( - schema: &str, - diagnostics: &[Diagnostic], - span: ast::Span, -) -> Option> { - let res: Vec<_> = diagnostics - .iter() - .filter(|diag| span.overlaps(crate::range_to_span(diag.range, schema))) - .cloned() - .collect(); - - if res.is_empty() { - None - } else { - Some(res) - } -} - fn filter_diagnostics(span_diagnostics: Vec, diagnostic_message: &str) -> Option> { let diagnostics = span_diagnostics .into_iter() @@ -272,15 +262,15 @@ fn format_block_attribute( } fn create_text_edit( - schema: &str, + target_file_uri: &str, + target_file_content: &str, formatted_attribute: String, append: bool, span: Span, - params: &CodeActionParams, -) -> WorkspaceEdit { +) -> Result> { let range = match append { - true => range_after_span(schema, span), - false => span_to_range(schema, span), + true => range_after_span(target_file_content, span), + false => span_to_range(target_file_content, span), }; let text = TextEdit { @@ -289,10 +279,19 @@ fn create_text_edit( }; let mut changes = HashMap::new(); - changes.insert(params.text_document.uri.clone(), vec![text]); + let url = parse_url(target_file_uri)?; + changes.insert(url, vec![text]); - WorkspaceEdit { + Ok(WorkspaceEdit { changes: Some(changes), ..Default::default() + }) +} + +pub(crate) fn parse_url(url: &str) -> Result> { + let result = Url::parse(url); + if result.is_err() { + warn!("Could not parse url {url}") } + return Ok(result?); } diff --git a/prisma-fmt/src/code_actions/mongodb.rs b/prisma-fmt/src/code_actions/mongodb.rs index 3da4ef911a32..f78d2bae4726 100644 --- a/prisma-fmt/src/code_actions/mongodb.rs +++ b/prisma-fmt/src/code_actions/mongodb.rs @@ -1,10 +1,11 @@ use lsp_types::{CodeAction, CodeActionKind, CodeActionOrCommand}; use psl::{parser_database::walkers::ModelWalker, schema_ast::ast::WithSpan, Datasource}; +use super::CodeActionsContext; + pub(super) fn add_at_map_for_id( actions: &mut Vec, - params: &lsp_types::CodeActionParams, - schema: &str, + context: &CodeActionsContext<'_>, model: ModelWalker<'_>, ) { let pk = match model.primary_key() { @@ -21,11 +22,13 @@ pub(super) fn add_at_map_for_id( None => return, }; - let span_diagnostics = - match super::diagnostics_for_span(schema, ¶ms.context.diagnostics, model.ast_model().span()) { - Some(sd) => sd, - None => return, - }; + let file_id = model.ast_model().span().file_id; + let file_uri = model.db.file_name(file_id); + let file_content = model.db.source(file_id); + let span_diagnostics = match context.diagnostics_for_span(model.ast_model().span()) { + Some(sd) => sd, + None => return, + }; let diagnostics = match super::filter_diagnostics( span_diagnostics, @@ -37,7 +40,15 @@ pub(super) fn add_at_map_for_id( let formatted_attribute = super::format_field_attribute(r#"@map("_id")"#); - let edit = super::create_text_edit(schema, formatted_attribute, true, field.ast_field().span(), params); + let Ok(edit) = super::create_text_edit( + file_uri, + file_content, + formatted_attribute, + true, + field.ast_field().span(), + ) else { + return; + }; let action = CodeAction { title: r#"Add @map("_id")"#.to_owned(), @@ -52,8 +63,7 @@ pub(super) fn add_at_map_for_id( pub(super) fn add_native_for_auto_id( actions: &mut Vec, - params: &lsp_types::CodeActionParams, - schema: &str, + context: &CodeActionsContext<'_>, model: ModelWalker<'_>, source: &Datasource, ) { @@ -71,11 +81,14 @@ pub(super) fn add_native_for_auto_id( None => return, }; - let span_diagnostics = - match super::diagnostics_for_span(schema, ¶ms.context.diagnostics, model.ast_model().span()) { - Some(sd) => sd, - None => return, - }; + let file_id = model.ast_model().span().file_id; + let file_uri = model.db.file_name(file_id); + let file_content = model.db.source(file_id); + + let span_diagnostics = match context.diagnostics_for_span(model.ast_model().span()) { + Some(sd) => sd, + None => return, + }; let diagnostics = match super::filter_diagnostics( span_diagnostics, @@ -87,7 +100,15 @@ pub(super) fn add_native_for_auto_id( let formatted_attribute = super::format_field_attribute(format!("@{}.ObjectId", source.name).as_str()); - let edit = super::create_text_edit(schema, formatted_attribute, true, field.ast_field().span(), params); + let Ok(edit) = super::create_text_edit( + file_uri, + file_content, + formatted_attribute, + true, + field.ast_field().span(), + ) else { + return; + }; let action = CodeAction { title: r#"Add @db.ObjectId"#.to_owned(), diff --git a/prisma-fmt/src/code_actions/multi_schema.rs b/prisma-fmt/src/code_actions/multi_schema.rs index aa5aaad05175..8198c1bf7c9c 100644 --- a/prisma-fmt/src/code_actions/multi_schema.rs +++ b/prisma-fmt/src/code_actions/multi_schema.rs @@ -1,19 +1,18 @@ -use lsp_types::{CodeAction, CodeActionKind, CodeActionOrCommand, CodeActionParams}; +use lsp_types::{CodeAction, CodeActionKind, CodeActionOrCommand}; use psl::{ diagnostics::Span, parser_database::walkers::{EnumWalker, ModelWalker}, schema_ast::ast::WithSpan, - Configuration, }; +use super::CodeActionsContext; + pub(super) fn add_schema_block_attribute_model( actions: &mut Vec, - params: &CodeActionParams, - schema: &str, - config: &Configuration, + context: &CodeActionsContext<'_>, model: ModelWalker<'_>, ) { - let datasource = match config.datasources.first() { + let datasource = match context.datasource() { Some(ds) => ds, None => return, }; @@ -26,11 +25,14 @@ pub(super) fn add_schema_block_attribute_model( return; } - let span_diagnostics = - match super::diagnostics_for_span(schema, ¶ms.context.diagnostics, model.ast_model().span()) { - Some(sd) => sd, - None => return, - }; + let file_id = model.ast_model().span().file_id; + let file_uri = model.db.file_name(file_id); + let file_content = model.db.source(file_id); + + let span_diagnostics = match context.diagnostics_for_span(model.ast_model().span()) { + Some(sd) => sd, + None => return, + }; let diagnostics = match super::filter_diagnostics(span_diagnostics, "This model is missing an `@@schema` attribute.") { @@ -45,7 +47,15 @@ pub(super) fn add_schema_block_attribute_model( &model.ast_model().attributes, ); - let edit = super::create_text_edit(schema, formatted_attribute, true, model.ast_model().span(), params); + let Ok(edit) = super::create_text_edit( + file_uri, + file_content, + formatted_attribute, + true, + model.ast_model().span(), + ) else { + return; + }; let action = CodeAction { title: String::from("Add `@@schema` attribute"), @@ -60,12 +70,10 @@ pub(super) fn add_schema_block_attribute_model( pub(super) fn add_schema_block_attribute_enum( actions: &mut Vec, - params: &CodeActionParams, - schema: &str, - config: &Configuration, + context: &CodeActionsContext<'_>, enumerator: EnumWalker<'_>, ) { - let datasource = match config.datasources.first() { + let datasource = match context.datasource() { Some(ds) => ds, None => return, }; @@ -78,11 +86,14 @@ pub(super) fn add_schema_block_attribute_enum( return; } - let span_diagnostics = - match super::diagnostics_for_span(schema, ¶ms.context.diagnostics, enumerator.ast_enum().span()) { - Some(sd) => sd, - None => return, - }; + let file_id = enumerator.ast_enum().span().file_id; + let file_uri = enumerator.db.file_name(file_id); + let file_content = enumerator.db.source(file_id); + + let span_diagnostics = match context.diagnostics_for_span(enumerator.ast_enum().span()) { + Some(sd) => sd, + None => return, + }; let diagnostics = match super::filter_diagnostics(span_diagnostics, "This enum is missing an `@@schema` attribute.") { @@ -97,7 +108,15 @@ pub(super) fn add_schema_block_attribute_enum( &enumerator.ast_enum().attributes, ); - let edit = super::create_text_edit(schema, formatted_attribute, true, enumerator.ast_enum().span(), params); + let Ok(edit) = super::create_text_edit( + file_uri, + file_content, + formatted_attribute, + true, + enumerator.ast_enum().span(), + ) else { + return; + }; let action = CodeAction { title: String::from("Add `@@schema` attribute"), @@ -112,21 +131,18 @@ pub(super) fn add_schema_block_attribute_enum( pub(super) fn add_schema_to_schemas( actions: &mut Vec, - params: &CodeActionParams, - schema: &str, - config: &Configuration, + context: &CodeActionsContext<'_>, model: ModelWalker<'_>, ) { - let datasource = match config.datasources.first() { + let datasource = match context.datasource() { Some(ds) => ds, None => return, }; - let span_diagnostics = - match super::diagnostics_for_span(schema, ¶ms.context.diagnostics, model.ast_model().span()) { - Some(sd) => sd, - None => return, - }; + let span_diagnostics = match context.diagnostics_for_span(model.ast_model().span()) { + Some(sd) => sd, + None => return, + }; let diagnostics = match super::filter_diagnostics(span_diagnostics, "This schema is not defined in the datasource.") { @@ -134,16 +150,20 @@ pub(super) fn add_schema_to_schemas( None => return, }; + let datasource_file_id = datasource.span.file_id; + let datasource_file_uri = context.db.file_name(datasource_file_id); + let datasource_content = context.db.source(datasource_file_id); + let edit = match datasource.schemas_span { Some(span) => { let formatted_attribute = format!(r#"", "{}""#, model.schema_name().unwrap()); super::create_text_edit( - schema, + datasource_file_uri, + datasource_content, formatted_attribute, true, // todo: update spans so that we can just append to the end of the _inside_ of the array. Instead of needing to re-append the `]` or taking the span end -1 Span::new(span.start, span.end - 1, psl::parser_database::FileId::ZERO), - params, ) } None => { @@ -161,10 +181,20 @@ pub(super) fn add_schema_to_schemas( has_properties, ); - super::create_text_edit(schema, formatted_attribute, true, datasource.url_span, params) + super::create_text_edit( + datasource_file_uri, + datasource_content, + formatted_attribute, + true, + datasource.url_span, + ) } }; + let Ok(edit) = edit else { + return; + }; + let action = CodeAction { title: String::from("Add schema to schemas"), kind: Some(CodeActionKind::QUICKFIX), diff --git a/prisma-fmt/src/code_actions/relation_mode.rs b/prisma-fmt/src/code_actions/relation_mode.rs index 8c45c36c8b4f..982a7f86eefb 100644 --- a/prisma-fmt/src/code_actions/relation_mode.rs +++ b/prisma-fmt/src/code_actions/relation_mode.rs @@ -1,13 +1,11 @@ use lsp_types::{CodeAction, CodeActionKind, CodeActionOrCommand}; -use psl::{ - diagnostics::FileId, parser_database::walkers::CompleteInlineRelationWalker, schema_ast::ast::SourceConfig, - Configuration, -}; +use psl::{parser_database::walkers::CompleteInlineRelationWalker, schema_ast::ast::SourceConfig}; + +use super::CodeActionsContext; pub(crate) fn edit_referential_integrity( actions: &mut Vec, - params: &lsp_types::CodeActionParams, - schema: &str, + context: &CodeActionsContext<'_>, source: &SourceConfig, ) { let prop = match source.properties.iter().find(|p| p.name.name == "referentialIntegrity") { @@ -15,7 +13,7 @@ pub(crate) fn edit_referential_integrity( None => return, }; - let span_diagnostics = match super::diagnostics_for_span(schema, ¶ms.context.diagnostics, source.span) { + let span_diagnostics = match context.diagnostics_for_span(source.span) { Some(sd) => sd, None => return, }; @@ -26,7 +24,15 @@ pub(crate) fn edit_referential_integrity( None => return, }; - let edit = super::create_text_edit(schema, "relationMode".to_owned(), false, prop.name.span, params); + let Ok(edit) = super::create_text_edit( + context.initiating_file_uri(), + context.initiating_file_source(), + "relationMode".to_owned(), + false, + prop.name.span, + ) else { + return; + }; let action = CodeAction { title: String::from("Rename property to relationMode"), @@ -41,13 +47,10 @@ pub(crate) fn edit_referential_integrity( pub(crate) fn replace_set_default_mysql( actions: &mut Vec, - params: &lsp_types::CodeActionParams, - file_id: FileId, - schema: &str, + context: &CodeActionsContext<'_>, relation: CompleteInlineRelationWalker<'_>, - config: &Configuration, ) { - let datasource = match config.datasources.first() { + let datasource = match context.datasource() { Some(ds) => ds, None => return, }; @@ -61,11 +64,14 @@ pub(crate) fn replace_set_default_mysql( None => return, }; - if span.file_id != file_id { + if span.file_id != context.initiating_file_id { return; } - let span_diagnostics = match super::diagnostics_for_span(schema, ¶ms.context.diagnostics, span) { + let file_name = context.initiating_file_uri(); + let file_content = context.initiating_file_source(); + + let span_diagnostics = match context.diagnostics_for_span(span) { Some(sd) => sd, None => return, }; @@ -78,7 +84,9 @@ pub(crate) fn replace_set_default_mysql( None => return, }; - let edit = super::create_text_edit(schema, "NoAction".to_owned(), false, span, params); + let Ok(edit) = super::create_text_edit(file_name, file_content, "NoAction".to_owned(), false, span) else { + return; + }; let action = CodeAction { title: r#"Replace SetDefault with NoAction"#.to_owned(), diff --git a/prisma-fmt/src/code_actions/relations.rs b/prisma-fmt/src/code_actions/relations.rs index 3f84e9ddb7ce..dac291c51222 100644 --- a/prisma-fmt/src/code_actions/relations.rs +++ b/prisma-fmt/src/code_actions/relations.rs @@ -1,11 +1,11 @@ -use lsp_types::{CodeAction, CodeActionKind, CodeActionOrCommand, CodeActionParams, TextEdit, WorkspaceEdit}; +use lsp_types::{CodeAction, CodeActionKind, CodeActionOrCommand, TextEdit, Url, WorkspaceEdit}; use psl::parser_database::{ ast::WithSpan, walkers::{CompleteInlineRelationWalker, RelationFieldWalker}, }; use std::collections::HashMap; -use super::format_block_attribute; +use super::{format_block_attribute, CodeActionsContext}; /// If the referencing side of the one-to-one relation does not point /// to a unique constraint, the action adds the attribute. @@ -49,8 +49,7 @@ use super::format_block_attribute; /// ``` pub(super) fn add_referencing_side_unique( actions: &mut Vec, - params: &CodeActionParams, - schema: &str, + context: &CodeActionsContext<'_>, relation: CompleteInlineRelationWalker<'_>, ) { if relation @@ -69,14 +68,14 @@ pub(super) fn add_referencing_side_unique( let attribute_name = "unique"; let text = super::create_missing_attribute( - schema, + context.initiating_file_source(), relation.referencing_model(), relation.referencing_fields(), attribute_name, ); let mut changes = HashMap::new(); - changes.insert(params.text_document.uri.clone(), vec![text]); + changes.insert(context.lsp_params.text_document.uri.clone(), vec![text]); let edit = WorkspaceEdit { changes: Some(changes), @@ -85,11 +84,7 @@ pub(super) fn add_referencing_side_unique( // The returned diagnostics are the ones we promise to fix with // the code action. - let diagnostics = super::diagnostics_for_span( - schema, - ¶ms.context.diagnostics, - relation.referencing_field().ast_field().span(), - ); + let diagnostics = context.diagnostics_for_span(relation.referencing_field().ast_field().span()); let action = CodeAction { title: String::from("Make referencing fields unique"), @@ -141,8 +136,7 @@ pub(super) fn add_referencing_side_unique( /// ``` pub(super) fn add_referenced_side_unique( actions: &mut Vec, - params: &CodeActionParams, - schema: &str, + context: &CodeActionsContext<'_>, relation: CompleteInlineRelationWalker<'_>, ) { if relation @@ -153,6 +147,10 @@ pub(super) fn add_referenced_side_unique( return; } + let file_id = relation.referenced_model().ast_model().span().file_id; + let file_uri = relation.db.file_name(file_id); + let file_content = relation.db.source(file_id); + match (relation.referencing_fields().len(), relation.referenced_fields().len()) { (0, 0) => return, (a, b) if a != b => return, @@ -161,14 +159,15 @@ pub(super) fn add_referenced_side_unique( let attribute_name = "unique"; let text = super::create_missing_attribute( - schema, + file_content, relation.referenced_model(), relation.referenced_fields(), attribute_name, ); let mut changes = HashMap::new(); - changes.insert(params.text_document.uri.clone(), vec![text]); + // TODO: don't unwrap + changes.insert(Url::parse(file_uri).unwrap(), vec![text]); let edit = WorkspaceEdit { changes: Some(changes), @@ -177,11 +176,7 @@ pub(super) fn add_referenced_side_unique( // The returned diagnostics are the ones we promise to fix with // the code action. - let diagnostics = super::diagnostics_for_span( - schema, - ¶ms.context.diagnostics, - relation.referencing_field().ast_field().span(), - ); + let diagnostics = context.diagnostics_for_span(relation.referencing_field().ast_field().span()); let action = CodeAction { title: String::from("Make referenced field(s) unique"), @@ -241,8 +236,7 @@ pub(super) fn add_referenced_side_unique( /// ``` pub(super) fn add_index_for_relation_fields( actions: &mut Vec, - params: &CodeActionParams, - schema: &str, + context: &CodeActionsContext<'_>, relation: RelationFieldWalker<'_>, ) { let fields = match relation.fields() { @@ -269,25 +263,21 @@ pub(super) fn add_index_for_relation_fields( &relation.model().ast_model().attributes, ); - let range = super::range_after_span(schema, relation.model().ast_model().span()); + let range = super::range_after_span(context.initiating_file_source(), relation.model().ast_model().span()); let text = TextEdit { range, new_text: formatted_attribute, }; let mut changes = HashMap::new(); - changes.insert(params.text_document.uri.clone(), vec![text]); + changes.insert(context.lsp_params.text_document.uri.clone(), vec![text]); let edit = WorkspaceEdit { changes: Some(changes), ..Default::default() }; - let span_diagnostics = match super::diagnostics_for_span( - schema, - ¶ms.context.diagnostics, - relation.relation_attribute().unwrap().span(), - ) { + let span_diagnostics = match context.diagnostics_for_span(relation.relation_attribute().unwrap().span()) { Some(sd) => sd, None => return, }; diff --git a/prisma-fmt/src/lib.rs b/prisma-fmt/src/lib.rs index 149ca5767ff7..79979c4ffe47 100644 --- a/prisma-fmt/src/lib.rs +++ b/prisma-fmt/src/lib.rs @@ -12,14 +12,14 @@ mod validate; use log::*; use lsp_types::{Position, Range}; -use psl::parser_database::ast; +use psl::{diagnostics::FileId, parser_database::ast}; use schema_file_input::SchemaFileInput; /// 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 /// being a `CompletionList` object. -pub fn text_document_completion(schema_files: String, initiating_file_name: String, params: &str) -> String { +pub fn text_document_completion(schema_files: String, initiating_file_name: &str, params: &str) -> String { let params = if let Ok(params) = serde_json::from_str::(params) { params } else { @@ -38,7 +38,7 @@ pub fn text_document_completion(schema_files: String, initiating_file_name: Stri } /// This API is modelled on an LSP [code action request](https://github.com/microsoft/language-server-protocol/blob/gh-pages/_specifications/specification-3-16.md#textDocument_codeAction=). Input and output are both JSON, the request being a `CodeActionParams` object and the response being a list of `CodeActionOrCommand` objects. -pub fn code_actions(schema_files: String, initiating_file_name: String, params: &str) -> String { +pub fn code_actions(schema_files: String, initiating_file_name: &str, params: &str) -> String { let params = if let Ok(params) = serde_json::from_str::(params) { params } else { @@ -264,11 +264,11 @@ pub(crate) fn position_to_offset(position: &Position, document: &str) -> Option< #[track_caller] /// Converts an LSP range to a span. -pub(crate) fn range_to_span(range: Range, document: &str) -> ast::Span { +pub(crate) fn range_to_span(range: Range, document: &str, file_id: FileId) -> ast::Span { let start = position_to_offset(&range.start, document).unwrap(); let end = position_to_offset(&range.end, document).unwrap(); - ast::Span::new(start, end, psl::parser_database::FileId::ZERO) + ast::Span::new(start, end, file_id) } /// Gives the LSP position right after the given span. diff --git a/prisma-fmt/tests/code_actions/scenarios/mongodb_at_map_multifile/_target.prisma b/prisma-fmt/tests/code_actions/scenarios/mongodb_at_map_multifile/_target.prisma new file mode 100644 index 000000000000..6ca9071e74e3 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/mongodb_at_map_multifile/_target.prisma @@ -0,0 +1,3 @@ +model Kattbjorn { + id String @id +} diff --git a/prisma-fmt/tests/code_actions/scenarios/mongodb_at_map_multifile/config.prisma b/prisma-fmt/tests/code_actions/scenarios/mongodb_at_map_multifile/config.prisma new file mode 100644 index 000000000000..179437b854fa --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/mongodb_at_map_multifile/config.prisma @@ -0,0 +1,8 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "mongodb" + url = env("DATABASE_URL") +} diff --git a/prisma-fmt/tests/code_actions/scenarios/mongodb_at_map_multifile/result.json b/prisma-fmt/tests/code_actions/scenarios/mongodb_at_map_multifile/result.json new file mode 100644 index 000000000000..fcfe7e97d7e5 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/mongodb_at_map_multifile/result.json @@ -0,0 +1,41 @@ +[ + { + "title": "Add @map(\"_id\")", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 1, + "character": 4 + }, + "end": { + "line": 2, + "character": 0 + } + }, + "severity": 1, + "message": "Error validating field `id` in model `Kattbjorn`: MongoDB model IDs must have an @map(\"_id\") annotation." + } + ], + "edit": { + "changes": { + "file:///path/to/_target.prisma": [ + { + "range": { + "start": { + "line": 1, + "character": 17 + }, + "end": { + "line": 2, + "character": 0 + } + }, + "newText": " @map(\"_id\")\n" + } + ] + } + } + } +] \ No newline at end of file diff --git a/prisma-fmt/tests/code_actions/scenarios/multi_schema_add_to_existing_schemas_multifile/_target.prisma b/prisma-fmt/tests/code_actions/scenarios/multi_schema_add_to_existing_schemas_multifile/_target.prisma new file mode 100644 index 000000000000..39ea6e2c70a9 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/multi_schema_add_to_existing_schemas_multifile/_target.prisma @@ -0,0 +1,5 @@ +model A { + id Int @id + + @@schema("base") +} diff --git a/prisma-fmt/tests/code_actions/scenarios/multi_schema_add_to_existing_schemas_multifile/datasource.prisma b/prisma-fmt/tests/code_actions/scenarios/multi_schema_add_to_existing_schemas_multifile/datasource.prisma new file mode 100644 index 000000000000..ad9034e8e487 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/multi_schema_add_to_existing_schemas_multifile/datasource.prisma @@ -0,0 +1,6 @@ +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + schemas = ["a", "b"] + relationMode = "prisma" +} diff --git a/prisma-fmt/tests/code_actions/scenarios/multi_schema_add_to_existing_schemas_multifile/generator.prisma b/prisma-fmt/tests/code_actions/scenarios/multi_schema_add_to_existing_schemas_multifile/generator.prisma new file mode 100644 index 000000000000..6fc2e1264aca --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/multi_schema_add_to_existing_schemas_multifile/generator.prisma @@ -0,0 +1,4 @@ +generator client { + provider = "prisma-client-js" + previewFeatures = ["multiSchema"] +} diff --git a/prisma-fmt/tests/code_actions/scenarios/multi_schema_add_to_existing_schemas_multifile/result.json b/prisma-fmt/tests/code_actions/scenarios/multi_schema_add_to_existing_schemas_multifile/result.json new file mode 100644 index 000000000000..181c63dd4785 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/multi_schema_add_to_existing_schemas_multifile/result.json @@ -0,0 +1,41 @@ +[ + { + "title": "Add schema to schemas", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 3, + "character": 13 + }, + "end": { + "line": 3, + "character": 19 + } + }, + "severity": 1, + "message": "This schema is not defined in the datasource. Read more on `@@schema` at https://pris.ly/d/multi-schema" + } + ], + "edit": { + "changes": { + "file:///path/to/datasource.prisma": [ + { + "range": { + "start": { + "line": 3, + "character": 27 + }, + "end": { + "line": 3, + "character": 28 + } + }, + "newText": "\", \"base\"" + } + ] + } + } + } +] \ No newline at end of file diff --git a/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_multifile/_target.prisma b/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_multifile/_target.prisma new file mode 100644 index 000000000000..edc69dac2950 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_multifile/_target.prisma @@ -0,0 +1,3 @@ +model User { + id Int @id +} diff --git a/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_multifile/datasource.prisma b/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_multifile/datasource.prisma new file mode 100644 index 000000000000..9ae54b6ef5ab --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_multifile/datasource.prisma @@ -0,0 +1,5 @@ +datasource db { + provider = "postgresql" + url = env("TEST_DATABASE_URL") + schemas = ["one", "two"] +} diff --git a/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_multifile/generator.prisma b/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_multifile/generator.prisma new file mode 100644 index 000000000000..6fc2e1264aca --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_multifile/generator.prisma @@ -0,0 +1,4 @@ +generator client { + provider = "prisma-client-js" + previewFeatures = ["multiSchema"] +} diff --git a/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_multifile/result.json b/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_multifile/result.json new file mode 100644 index 000000000000..a719b5b15b31 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_multifile/result.json @@ -0,0 +1,41 @@ +[ + { + "title": "Add `@@schema` attribute", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 2, + "character": 1 + } + }, + "severity": 1, + "message": "Error validating model \"User\": This model is missing an `@@schema` attribute." + } + ], + "edit": { + "changes": { + "file:///path/to/_target.prisma": [ + { + "range": { + "start": { + "line": 2, + "character": 0 + }, + "end": { + "line": 2, + "character": 1 + } + }, + "newText": "\n @@schema()\n}" + } + ] + } + } + } +] \ No newline at end of file diff --git a/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_one_enum_multifile/_target.prisma b/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_one_enum_multifile/_target.prisma new file mode 100644 index 000000000000..9f874a6f3cb8 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_one_enum_multifile/_target.prisma @@ -0,0 +1,7 @@ +model User { + id Int @id +} + +enum Colour { + Red +} diff --git a/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_one_enum_multifile/config.prisma b/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_one_enum_multifile/config.prisma new file mode 100644 index 000000000000..091f806ed3e9 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_one_enum_multifile/config.prisma @@ -0,0 +1,10 @@ +generator client { + provider = "prisma-client-js" + previewFeatures = ["multiSchema"] +} + +datasource db { + provider = "postgresql" + url = env("TEST_DATABASE_URL") + schemas = ["one", "two"] +} diff --git a/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_one_enum_multifile/result.json b/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_one_enum_multifile/result.json new file mode 100644 index 000000000000..af6834985b98 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/multi_schema_one_model_one_enum_multifile/result.json @@ -0,0 +1,80 @@ +[ + { + "title": "Add `@@schema` attribute", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 2, + "character": 1 + } + }, + "severity": 1, + "message": "Error validating model \"User\": This model is missing an `@@schema` attribute." + } + ], + "edit": { + "changes": { + "file:///path/to/_target.prisma": [ + { + "range": { + "start": { + "line": 2, + "character": 0 + }, + "end": { + "line": 2, + "character": 1 + } + }, + "newText": "\n @@schema()\n}" + } + ] + } + } + }, + { + "title": "Add `@@schema` attribute", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 4, + "character": 0 + }, + "end": { + "line": 6, + "character": 1 + } + }, + "severity": 1, + "message": "This enum is missing an `@@schema` attribute." + } + ], + "edit": { + "changes": { + "file:///path/to/_target.prisma": [ + { + "range": { + "start": { + "line": 6, + "character": 0 + }, + "end": { + "line": 6, + "character": 1 + } + }, + "newText": "\n @@schema()\n}" + } + ] + } + } + } +] \ No newline at end of file diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_compound_field_multifile/A.prisma b/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_compound_field_multifile/A.prisma new file mode 100644 index 000000000000..3b8fd5027039 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_compound_field_multifile/A.prisma @@ -0,0 +1,6 @@ +model A { + id Int @id + field1 Int + field2 Int + B B[] +} diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_compound_field_multifile/_target.prisma b/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_compound_field_multifile/_target.prisma new file mode 100644 index 000000000000..637a2fafd05a --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_compound_field_multifile/_target.prisma @@ -0,0 +1,8 @@ +model B { + id Int @id + bId1 Int + bId2 Int + A A @relation(fields: [bId1, bId2], references: [field1, field2]) + + @@unique([bId1, bId2]) +} diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_compound_field_multifile/datasource.prisma b/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_compound_field_multifile/datasource.prisma new file mode 100644 index 000000000000..8f0b81f14329 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_compound_field_multifile/datasource.prisma @@ -0,0 +1,4 @@ +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_compound_field_multifile/result.json b/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_compound_field_multifile/result.json new file mode 100644 index 000000000000..82863e96a923 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_compound_field_multifile/result.json @@ -0,0 +1,41 @@ +[ + { + "title": "Make referenced field(s) unique", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 4, + "character": 2 + }, + "end": { + "line": 5, + "character": 0 + } + }, + "severity": 1, + "message": "Error parsing attribute \"@relation\": The argument `references` must refer to a unique criterion in the related model. Consider adding an `@@unique([field1, field2])` attribute to the model `A`." + } + ], + "edit": { + "changes": { + "file:///path/to/A.prisma": [ + { + "range": { + "start": { + "line": 5, + "character": 0 + }, + "end": { + "line": 5, + "character": 1 + } + }, + "newText": "\n @@unique([field1, field2])\n}" + } + ] + } + } + } +] \ No newline at end of file diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_single_field_multifile/A.prisma b/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_single_field_multifile/A.prisma new file mode 100644 index 000000000000..62b017574614 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_single_field_multifile/A.prisma @@ -0,0 +1,5 @@ +model A { + id Int @id + field Int + B B[] +} diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_single_field_multifile/_target.prisma b/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_single_field_multifile/_target.prisma new file mode 100644 index 000000000000..d4b0adb57089 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_single_field_multifile/_target.prisma @@ -0,0 +1,5 @@ +model B { + id Int @id + bId Int + A A @relation(fields: [bId], references: [field]) +} diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_single_field_multifile/datasource.prisma b/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_single_field_multifile/datasource.prisma new file mode 100644 index 000000000000..c26ac8e3a79e --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_single_field_multifile/datasource.prisma @@ -0,0 +1,4 @@ +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_single_field_multifile/result.json b/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_single_field_multifile/result.json new file mode 100644 index 000000000000..956353f0c9b8 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_many_referenced_side_misses_unique_single_field_multifile/result.json @@ -0,0 +1,41 @@ +[ + { + "title": "Make referenced field(s) unique", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 3, + "character": 2 + }, + "end": { + "line": 4, + "character": 0 + } + }, + "severity": 1, + "message": "Error parsing attribute \"@relation\": The argument `references` must refer to a unique criterion in the related model. Consider adding an `@unique` attribute to the field `field` in the model `A`." + } + ], + "edit": { + "changes": { + "file:///path/to/A.prisma": [ + { + "range": { + "start": { + "line": 2, + "character": 13 + }, + "end": { + "line": 2, + "character": 13 + } + }, + "newText": " @unique" + } + ] + } + } + } +] \ No newline at end of file diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_compound_field_multifile/A.prisma b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_compound_field_multifile/A.prisma new file mode 100644 index 000000000000..ec824442c022 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_compound_field_multifile/A.prisma @@ -0,0 +1,6 @@ +model A { + id Int @id + field1 Int + field2 Int + B B? +} diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_compound_field_multifile/_target.prisma b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_compound_field_multifile/_target.prisma new file mode 100644 index 000000000000..637a2fafd05a --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_compound_field_multifile/_target.prisma @@ -0,0 +1,8 @@ +model B { + id Int @id + bId1 Int + bId2 Int + A A @relation(fields: [bId1, bId2], references: [field1, field2]) + + @@unique([bId1, bId2]) +} diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_compound_field_multifile/datasource.prisma b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_compound_field_multifile/datasource.prisma new file mode 100644 index 000000000000..c26ac8e3a79e --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_compound_field_multifile/datasource.prisma @@ -0,0 +1,4 @@ +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_compound_field_multifile/result.json b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_compound_field_multifile/result.json new file mode 100644 index 000000000000..4bb25efd7bc6 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_compound_field_multifile/result.json @@ -0,0 +1,41 @@ +[ + { + "title": "Make referenced field(s) unique", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 4, + "character": 2 + }, + "end": { + "line": 5, + "character": 0 + } + }, + "severity": 1, + "message": "Error parsing attribute \"@relation\": The argument `references` must refer to a unique criterion in the related model. Consider adding an `@@unique([field1, field2])` attribute to the model `A`." + } + ], + "edit": { + "changes": { + "file:///path/to/A.prisma": [ + { + "range": { + "start": { + "line": 5, + "character": 0 + }, + "end": { + "line": 5, + "character": 1 + } + }, + "newText": "\n @@unique([field1, field2])\n}" + } + ] + } + } + } +] \ No newline at end of file diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_single_field_multifile/A.prisma b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_single_field_multifile/A.prisma new file mode 100644 index 000000000000..22a6b353d34e --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_single_field_multifile/A.prisma @@ -0,0 +1,5 @@ +model A { + id Int @id + field Int + B B? +} diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_single_field_multifile/_target.prisma b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_single_field_multifile/_target.prisma new file mode 100644 index 000000000000..2877fa449b3d --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_single_field_multifile/_target.prisma @@ -0,0 +1,5 @@ +model B { + id Int @id + bId Int @unique + A A @relation(fields: [bId], references: [field]) +} diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_single_field_multifile/datasource.prisma b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_single_field_multifile/datasource.prisma new file mode 100644 index 000000000000..c26ac8e3a79e --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_single_field_multifile/datasource.prisma @@ -0,0 +1,4 @@ +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_single_field_multifile/result.json b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_single_field_multifile/result.json new file mode 100644 index 000000000000..b488d8e75fff --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referenced_side_misses_unique_single_field_multifile/result.json @@ -0,0 +1,41 @@ +[ + { + "title": "Make referenced field(s) unique", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 3, + "character": 2 + }, + "end": { + "line": 4, + "character": 0 + } + }, + "severity": 1, + "message": "Error parsing attribute \"@relation\": The argument `references` must refer to a unique criterion in the related model. Consider adding an `@unique` attribute to the field `field` in the model `A`." + } + ], + "edit": { + "changes": { + "file:///path/to/A.prisma": [ + { + "range": { + "start": { + "line": 2, + "character": 11 + }, + "end": { + "line": 2, + "character": 11 + } + }, + "newText": " @unique" + } + ] + } + } + } +] \ No newline at end of file diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_compound_field_multifile/A.prisma b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_compound_field_multifile/A.prisma new file mode 100644 index 000000000000..a73233194368 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_compound_field_multifile/A.prisma @@ -0,0 +1,8 @@ +model A { + id Int @id + field1 Int + field2 Int + B B? + + @@unique([field1, field2]) +} diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_compound_field_multifile/_target.prisma b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_compound_field_multifile/_target.prisma new file mode 100644 index 000000000000..1ce1e30dbe53 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_compound_field_multifile/_target.prisma @@ -0,0 +1,6 @@ +model B { + id Int @id + bId1 Int + bId2 Int + A A @relation(fields: [bId1, bId2], references: [field1, field2]) +} diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_compound_field_multifile/datasource.prisma b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_compound_field_multifile/datasource.prisma new file mode 100644 index 000000000000..c26ac8e3a79e --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_compound_field_multifile/datasource.prisma @@ -0,0 +1,4 @@ +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_compound_field_multifile/result.json b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_compound_field_multifile/result.json new file mode 100644 index 000000000000..8f2d42bd3ce1 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_compound_field_multifile/result.json @@ -0,0 +1,41 @@ +[ + { + "title": "Make referencing fields unique", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 4, + "character": 2 + }, + "end": { + "line": 5, + "character": 0 + } + }, + "severity": 1, + "message": "Error parsing attribute \"@relation\": A one-to-one relation must use unique fields on the defining side. Either add an `@@unique([bId1, bId2])` attribute to the model, or change the relation to one-to-many." + } + ], + "edit": { + "changes": { + "file:///path/to/_target.prisma": [ + { + "range": { + "start": { + "line": 5, + "character": 0 + }, + "end": { + "line": 5, + "character": 1 + } + }, + "newText": "\n @@unique([bId1, bId2])\n}" + } + ] + } + } + } +] \ No newline at end of file diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_single_field_multifile/A.prisma b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_single_field_multifile/A.prisma new file mode 100644 index 000000000000..4c8ef27b3e8c --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_single_field_multifile/A.prisma @@ -0,0 +1,5 @@ +model A { + id Int @id + field Int @unique + B B? +} diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_single_field_multifile/_target.prisma b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_single_field_multifile/_target.prisma new file mode 100644 index 000000000000..d4b0adb57089 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_single_field_multifile/_target.prisma @@ -0,0 +1,5 @@ +model B { + id Int @id + bId Int + A A @relation(fields: [bId], references: [field]) +} diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_single_field_multifile/datasource.prisma b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_single_field_multifile/datasource.prisma new file mode 100644 index 000000000000..c26ac8e3a79e --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_single_field_multifile/datasource.prisma @@ -0,0 +1,4 @@ +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} diff --git a/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_single_field_multifile/result.json b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_single_field_multifile/result.json new file mode 100644 index 000000000000..c0ebbc5ec6ab --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/one_to_one_referencing_side_misses_unique_single_field_multifile/result.json @@ -0,0 +1,41 @@ +[ + { + "title": "Make referencing fields unique", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 3, + "character": 2 + }, + "end": { + "line": 4, + "character": 0 + } + }, + "severity": 1, + "message": "Error parsing attribute \"@relation\": A one-to-one relation must use unique fields on the defining side. Either add an `@unique` attribute to the field `bId`, or change the relation to one-to-many." + } + ], + "edit": { + "changes": { + "file:///path/to/_target.prisma": [ + { + "range": { + "start": { + "line": 2, + "character": 9 + }, + "end": { + "line": 2, + "character": 9 + } + }, + "newText": " @unique" + } + ] + } + } + } +] \ No newline at end of file diff --git a/prisma-fmt/tests/code_actions/scenarios/relation_mode_mysql_foreign_keys_set_default_multifile/Bar.prisma b/prisma-fmt/tests/code_actions/scenarios/relation_mode_mysql_foreign_keys_set_default_multifile/Bar.prisma new file mode 100644 index 000000000000..950d86686923 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/relation_mode_mysql_foreign_keys_set_default_multifile/Bar.prisma @@ -0,0 +1,4 @@ +model Bar { + id Int @id + foo Foo? +} diff --git a/prisma-fmt/tests/code_actions/scenarios/relation_mode_mysql_foreign_keys_set_default_multifile/Test.prisma b/prisma-fmt/tests/code_actions/scenarios/relation_mode_mysql_foreign_keys_set_default_multifile/Test.prisma new file mode 100644 index 000000000000..48e6b86bc1bc --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/relation_mode_mysql_foreign_keys_set_default_multifile/Test.prisma @@ -0,0 +1,5 @@ +// This is a test enum. +enum Test { + TestUno + TestDue +} diff --git a/prisma-fmt/tests/code_actions/scenarios/relation_mode_mysql_foreign_keys_set_default_multifile/_target.prisma b/prisma-fmt/tests/code_actions/scenarios/relation_mode_mysql_foreign_keys_set_default_multifile/_target.prisma new file mode 100644 index 000000000000..b57ffc80b959 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/relation_mode_mysql_foreign_keys_set_default_multifile/_target.prisma @@ -0,0 +1,8 @@ +/// multi line +/// commennttt +model Foo { + id Int @id + bar Bar @relation(fields: [bar_id], references: [id], onUpdate: SetDefault) + bar_id Int @unique + t Test +} diff --git a/prisma-fmt/tests/code_actions/scenarios/relation_mode_mysql_foreign_keys_set_default_multifile/config.prisma b/prisma-fmt/tests/code_actions/scenarios/relation_mode_mysql_foreign_keys_set_default_multifile/config.prisma new file mode 100644 index 000000000000..b1dfaf6bd75b --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/relation_mode_mysql_foreign_keys_set_default_multifile/config.prisma @@ -0,0 +1,9 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "mysql" + url = env("DATABASE_URL") + relationMode = "foreignKeys" +} diff --git a/prisma-fmt/tests/code_actions/scenarios/relation_mode_mysql_foreign_keys_set_default_multifile/result.json b/prisma-fmt/tests/code_actions/scenarios/relation_mode_mysql_foreign_keys_set_default_multifile/result.json new file mode 100644 index 000000000000..bf5b524d5243 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/relation_mode_mysql_foreign_keys_set_default_multifile/result.json @@ -0,0 +1,41 @@ +[ + { + "title": "Replace SetDefault with NoAction", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 4, + "character": 62 + }, + "end": { + "line": 4, + "character": 82 + } + }, + "severity": 2, + "message": "MySQL does not actually support the `SetDefault` referential action, so using it may result in unexpected errors. Read more at https://pris.ly/d/mysql-set-default " + } + ], + "edit": { + "changes": { + "file:///path/to/_target.prisma": [ + { + "range": { + "start": { + "line": 4, + "character": 72 + }, + "end": { + "line": 4, + "character": 82 + } + }, + "newText": "NoAction" + } + ] + } + } + } +] \ No newline at end of file diff --git a/prisma-fmt/tests/code_actions/scenarios/relation_mode_prisma_missing_index_multifile/SomeUser.prisma b/prisma-fmt/tests/code_actions/scenarios/relation_mode_prisma_missing_index_multifile/SomeUser.prisma new file mode 100644 index 000000000000..b410777b6990 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/relation_mode_prisma_missing_index_multifile/SomeUser.prisma @@ -0,0 +1,4 @@ +model SomeUser { + id Int @id + posts Post[] +} diff --git a/prisma-fmt/tests/code_actions/scenarios/relation_mode_prisma_missing_index_multifile/_target.prisma b/prisma-fmt/tests/code_actions/scenarios/relation_mode_prisma_missing_index_multifile/_target.prisma new file mode 100644 index 000000000000..475a7884814a --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/relation_mode_prisma_missing_index_multifile/_target.prisma @@ -0,0 +1,5 @@ +model Post { + id Int @id + userId Int + user SomeUser @relation(fields: [userId], references: [id]) +} diff --git a/prisma-fmt/tests/code_actions/scenarios/relation_mode_prisma_missing_index_multifile/config.prisma b/prisma-fmt/tests/code_actions/scenarios/relation_mode_prisma_missing_index_multifile/config.prisma new file mode 100644 index 000000000000..09be286622d3 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/relation_mode_prisma_missing_index_multifile/config.prisma @@ -0,0 +1,9 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "mysql" + url = env("TEST_DATABASE_URL") + relationMode = "prisma" +} diff --git a/prisma-fmt/tests/code_actions/scenarios/relation_mode_prisma_missing_index_multifile/result.json b/prisma-fmt/tests/code_actions/scenarios/relation_mode_prisma_missing_index_multifile/result.json new file mode 100644 index 000000000000..ec9be9472974 --- /dev/null +++ b/prisma-fmt/tests/code_actions/scenarios/relation_mode_prisma_missing_index_multifile/result.json @@ -0,0 +1,41 @@ +[ + { + "title": "Add an index for the relation's field(s)", + "kind": "quickfix", + "diagnostics": [ + { + "range": { + "start": { + "line": 3, + "character": 20 + }, + "end": { + "line": 3, + "character": 65 + } + }, + "severity": 2, + "message": "With `relationMode = \"prisma\"`, no foreign keys are used, so relation fields will not benefit from the index usually created by the relational database under the hood. This can lead to poor performance when querying these fields. We recommend adding an index manually. Learn more at https://pris.ly/d/relation-mode-prisma-indexes\" " + } + ], + "edit": { + "changes": { + "file:///path/to/_target.prisma": [ + { + "range": { + "start": { + "line": 4, + "character": 0 + }, + "end": { + "line": 4, + "character": 1 + } + }, + "newText": "\n @@index([userId])\n}" + } + ] + } + } + } +] \ No newline at end of file diff --git a/prisma-fmt/tests/code_actions/test_api.rs b/prisma-fmt/tests/code_actions/test_api.rs index f10763fadb67..f8b19057b5d4 100644 --- a/prisma-fmt/tests/code_actions/test_api.rs +++ b/prisma-fmt/tests/code_actions/test_api.rs @@ -2,40 +2,58 @@ use lsp_types::{Diagnostic, DiagnosticSeverity}; use once_cell::sync::Lazy; use prisma_fmt::offset_to_position; use psl::SourceFile; -use std::{fmt::Write as _, io::Write as _, sync::Arc}; +use std::{fmt::Write as _, io::Write as _, path::PathBuf}; + +use crate::helpers::load_schema_files; const SCENARIOS_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/code_actions/scenarios"); +/** + * File used as a target for code-actions in multi-file + * tests + */ +const TARGET_SCHEMA_FILE: &str = "_target.prisma"; static UPDATE_EXPECT: Lazy = Lazy::new(|| std::env::var("UPDATE_EXPECT").is_ok()); -fn parse_schema_diagnostics(file: impl Into) -> Option> { - let schema = psl::validate(file.into()); +fn parse_schema_diagnostics(files: &[(String, String)], initiating_file_name: &str) -> Option> { + let schema = psl::validate_multi_file( + files + .iter() + .map(|(name, content)| (name.to_owned(), SourceFile::from(content))) + .collect(), + ); + let file_id = schema.db.file_id(initiating_file_name).unwrap(); + let source = schema.db.source(file_id); match (schema.diagnostics.warnings(), schema.diagnostics.errors()) { ([], []) => None, (warnings, errors) => { let mut diagnostics = Vec::new(); for warn in warnings.iter() { - diagnostics.push(Diagnostic { - severity: Some(DiagnosticSeverity::WARNING), - message: warn.message().to_owned(), - range: lsp_types::Range { - start: offset_to_position(warn.span().start, schema.db.source_assert_single()), - end: offset_to_position(warn.span().end, schema.db.source_assert_single()), - }, - ..Default::default() - }); + if warn.span().file_id == file_id { + diagnostics.push(Diagnostic { + severity: Some(DiagnosticSeverity::WARNING), + message: warn.message().to_owned(), + range: lsp_types::Range { + start: offset_to_position(warn.span().start, source), + end: offset_to_position(warn.span().end, source), + }, + ..Default::default() + }); + } } for error in errors.iter() { - diagnostics.push(Diagnostic { - severity: Some(DiagnosticSeverity::ERROR), - message: error.message().to_owned(), - range: lsp_types::Range { - start: offset_to_position(error.span().start, schema.db.source_assert_single()), - end: offset_to_position(error.span().end, schema.db.source_assert_single()), - }, - ..Default::default() - }); + if error.span().file_id == file_id { + diagnostics.push(Diagnostic { + severity: Some(DiagnosticSeverity::ERROR), + message: error.message().to_owned(), + range: lsp_types::Range { + start: offset_to_position(error.span().start, source), + end: offset_to_position(error.span().end, source), + }, + ..Default::default() + }); + } } Some(diagnostics) @@ -46,14 +64,30 @@ fn parse_schema_diagnostics(file: impl Into) -> Option diagnostics, None => Vec::new(), }; @@ -64,7 +98,7 @@ pub(crate) fn test_scenario(scenario_name: &str) { let params = lsp_types::CodeActionParams { text_document: lsp_types::TextDocumentIdentifier { - uri: "file:/path/to/schema.prisma".parse().unwrap(), + uri: initiating_file_name.parse().unwrap(), }, range: lsp_types::Range::default(), context: lsp_types::CodeActionContext { @@ -77,10 +111,9 @@ pub(crate) fn test_scenario(scenario_name: &str) { }, }; - let schema_files = serde_json::to_string_pretty(&[("schema.prisma", schema)]).unwrap(); let result = prisma_fmt::code_actions( - schema_files, - "schema.prisma".into(), + serde_json::to_string_pretty(&schema_files).unwrap(), + initiating_file_name, &serde_json::to_string_pretty(¶ms).unwrap(), ); // Prettify the JSON diff --git a/prisma-fmt/tests/code_actions/tests.rs b/prisma-fmt/tests/code_actions/tests.rs index 7bb3d1024ec4..00c65fd9003f 100644 --- a/prisma-fmt/tests/code_actions/tests.rs +++ b/prisma-fmt/tests/code_actions/tests.rs @@ -13,25 +13,37 @@ macro_rules! scenarios { scenarios! { one_to_many_referenced_side_misses_unique_single_field + one_to_many_referenced_side_misses_unique_single_field_multifile one_to_many_referenced_side_misses_unique_single_field_broken_relation one_to_many_referenced_side_misses_unique_compound_field + one_to_many_referenced_side_misses_unique_compound_field_multifile one_to_many_referenced_side_misses_unique_compound_field_existing_arguments one_to_many_referenced_side_misses_unique_compound_field_indentation_four_spaces one_to_many_referenced_side_misses_unique_compound_field_broken_relation one_to_one_referenced_side_misses_unique_single_field + one_to_one_referenced_side_misses_unique_single_field_multifile one_to_one_referenced_side_misses_unique_compound_field + one_to_one_referenced_side_misses_unique_compound_field_multifile one_to_one_referencing_side_misses_unique_single_field + one_to_one_referencing_side_misses_unique_single_field_multifile one_to_one_referencing_side_misses_unique_compound_field + one_to_one_referencing_side_misses_unique_compound_field_multifile one_to_one_referencing_side_misses_unique_compound_field_indentation_four_spaces relation_mode_prisma_missing_index + relation_mode_prisma_missing_index_multifile relation_mode_referential_integrity relation_mode_mysql_foreign_keys_set_default + relation_mode_mysql_foreign_keys_set_default_multifile multi_schema_one_model + multi_schema_one_model_multifile multi_schema_one_model_one_enum + multi_schema_one_model_one_enum_multifile multi_schema_two_models multi_schema_add_to_existing_schemas + multi_schema_add_to_existing_schemas_multifile multi_schema_add_to_nonexisting_schemas mongodb_at_map + mongodb_at_map_multifile mongodb_at_map_with_validation_errors mongodb_auto_native } diff --git a/prisma-fmt/tests/code_actions_tests.rs b/prisma-fmt/tests/code_actions_tests.rs index 1c460f038970..ce58517a78a0 100644 --- a/prisma-fmt/tests/code_actions_tests.rs +++ b/prisma-fmt/tests/code_actions_tests.rs @@ -1 +1,2 @@ mod code_actions; +mod helpers; diff --git a/prisma-fmt/tests/helpers.rs b/prisma-fmt/tests/helpers.rs new file mode 100644 index 000000000000..b5b5bfaaaccd --- /dev/null +++ b/prisma-fmt/tests/helpers.rs @@ -0,0 +1,31 @@ +use std::path::Path; + +pub fn load_schema_files(dir: impl AsRef) -> Vec<(String, String)> { + let schema_files = { + std::fs::read_dir(dir.as_ref()) + .unwrap() + .into_iter() + .map(Result::unwrap) + .filter_map(|entry| { + let ft = entry.file_type().ok()?; + if ft.is_dir() { + return None; + } + let path = entry.path(); + let name = path.file_name()?.to_str()?; + let ext = path.extension()?; + if ext != "prisma" { + return None; + } + + Some(( + format!("file:///path/to/{name}"), + std::fs::read_to_string(&path).unwrap(), + )) + }) + .collect::>() + }; + assert!(schema_files.len() > 0); + + schema_files +} diff --git a/prisma-fmt/tests/text_document_completion/test_api.rs b/prisma-fmt/tests/text_document_completion/test_api.rs index af14dc81e1fa..8db074e42ea1 100644 --- a/prisma-fmt/tests/text_document_completion/test_api.rs +++ b/prisma-fmt/tests/text_document_completion/test_api.rs @@ -1,3 +1,4 @@ +use crate::helpers::load_schema_files; use once_cell::sync::Lazy; use std::{fmt::Write as _, io::Write as _}; @@ -10,36 +11,14 @@ pub(crate) fn test_scenario(scenario_name: &str) { let schema_files = { write!(path, "{SCENARIOS_PATH}/{scenario_name}").unwrap(); - std::fs::read_dir(&path) - .unwrap() - .into_iter() - .map(Result::unwrap) - .filter_map(|entry| { - let ft = entry.file_type().ok()?; - if ft.is_dir() { - return None; - } - let path = entry.path(); - let ext = path.extension()?; - if ext == "prisma" { - Some(( - path.to_str().unwrap().to_owned(), - std::fs::read_to_string(&path).unwrap(), - )) - } else { - None - } - }) - .collect::>() + load_schema_files(&path) }; - assert!(schema_files.len() > 0); - path.clear(); write!(path, "{SCENARIOS_PATH}/{scenario_name}/result.json").unwrap(); let expected_result = std::fs::read_to_string(&path).unwrap_or_else(|_| String::new()); - let (initiating_file, cursor_position, schema_files) = take_cursor(schema_files); + let (initiating_file_name, cursor_position, schema_files) = take_cursor(schema_files); let params = lsp_types::CompletionParams { text_document_position: lsp_types::TextDocumentPositionParams { text_document: lsp_types::TextDocumentIdentifier { @@ -56,7 +35,7 @@ pub(crate) fn test_scenario(scenario_name: &str) { let result = prisma_fmt::text_document_completion( serde_json::to_string_pretty(&schema_files).unwrap(), - initiating_file, + &initiating_file_name, &serde_json::to_string_pretty(¶ms).unwrap(), ); // Prettify the JSON diff --git a/prisma-fmt/tests/text_document_completion_tests.rs b/prisma-fmt/tests/text_document_completion_tests.rs index 644e44d4e912..0a077044e6b2 100644 --- a/prisma-fmt/tests/text_document_completion_tests.rs +++ b/prisma-fmt/tests/text_document_completion_tests.rs @@ -1 +1,2 @@ +mod helpers; mod text_document_completion; diff --git a/psl/diagnostics/src/span.rs b/psl/diagnostics/src/span.rs index 42110aa29792..8b476e118303 100644 --- a/psl/diagnostics/src/span.rs +++ b/psl/diagnostics/src/span.rs @@ -38,7 +38,7 @@ impl Span { /// Is the given span overlapping with the current span. pub fn overlaps(self, other: Span) -> bool { - self.contains(other.start) || self.contains(other.end) + self.file_id == other.file_id && (self.contains(other.start) || self.contains(other.end)) } } diff --git a/psl/parser-database/src/walkers/relation/inline/complete.rs b/psl/parser-database/src/walkers/relation/inline/complete.rs index 3f7b1b67dc60..2fab404fe7a5 100644 --- a/psl/parser-database/src/walkers/relation/inline/complete.rs +++ b/psl/parser-database/src/walkers/relation/inline/complete.rs @@ -11,7 +11,8 @@ use schema_ast::ast; pub struct CompleteInlineRelationWalker<'db> { pub(crate) side_a: RelationFieldId, pub(crate) side_b: RelationFieldId, - pub(crate) db: &'db ParserDatabase, + /// The parser database being traversed. + pub db: &'db ParserDatabase, } #[allow(missing_docs)] diff --git a/psl/psl-core/src/configuration/datasource.rs b/psl/psl-core/src/configuration/datasource.rs index 2f81674ce745..4b79391e9a9b 100644 --- a/psl/psl-core/src/configuration/datasource.rs +++ b/psl/psl-core/src/configuration/datasource.rs @@ -9,6 +9,7 @@ use std::{any::Any, borrow::Cow, path::Path}; /// a `datasource` from the prisma schema. pub struct Datasource { pub name: String, + pub span: Span, /// The provider string pub provider: String, /// The provider that was selected as active from all specified providers diff --git a/psl/psl-core/src/validate/datasource_loader.rs b/psl/psl-core/src/validate/datasource_loader.rs index dcdf49cb9d05..9f95c04230ae 100644 --- a/psl/psl-core/src/validate/datasource_loader.rs +++ b/psl/psl-core/src/validate/datasource_loader.rs @@ -10,6 +10,7 @@ use parser_database::{ ast::{Expression, WithDocumentation}, coerce, coerce_array, coerce_opt, }; +use schema_ast::ast::WithSpan; use std::{borrow::Cow, collections::HashMap}; const PREVIEW_FEATURES_KEY: &str = "previewFeatures"; @@ -219,6 +220,7 @@ fn lift_datasource( Some(Datasource { namespaces: schemas.into_iter().map(|(s, span)| (s.to_owned(), span)).collect(), + span: ast_source.span(), schemas_span, name: source_name.to_owned(), provider: provider.to_owned(),