From 9bd8194de30ab72c7b72c691b100794b4592ad10 Mon Sep 17 00:00:00 2001 From: Sophie Atkins Date: Thu, 4 Jul 2024 01:22:01 +0200 Subject: [PATCH] spruce up relation field doc with `@relation(...)` info --- prisma-fmt/src/hover.rs | 53 +++++++++++++++---- .../model_from_view_type/result.json | 2 +- .../one_to_many_self_relation/result.json | 2 +- psl/parser-database/src/walkers/enum.rs | 2 +- .../src/walkers/relation_field.rs | 4 +- .../src/walkers/scalar_field.rs | 8 +++ 6 files changed, 56 insertions(+), 15 deletions(-) diff --git a/prisma-fmt/src/hover.rs b/prisma-fmt/src/hover.rs index 8061fff4a31..f259b3fbc03 100644 --- a/prisma-fmt/src/hover.rs +++ b/prisma-fmt/src/hover.rs @@ -2,7 +2,10 @@ use log::{info, warn}; use lsp_types::{Hover, HoverContents, HoverParams, MarkupContent, MarkupKind}; use psl::{ error_tolerant_parse_configuration, - parser_database::{walkers, ParserDatabase, ScalarFieldType}, + parser_database::{ + walkers::{self, Walker}, + ParserDatabase, RelationFieldId, ScalarFieldType, + }, schema_ast::ast::{ self, CompositeTypePosition, EnumPosition, EnumValuePosition, Field, FieldPosition, ModelPosition, SchemaPosition, WithDocumentation, WithName, @@ -140,22 +143,20 @@ fn hover(ctx: HoverContext<'_>) -> Option { }, walkers::RefinedFieldWalker::Relation(rf) => { - let relation = rf.relation(); let opposite_model = rf.related_model(); - let opposite_field = rf.opposite_relation_field().unwrap().ast_field(); + let opposite_rf = rf.opposite_relation_field().unwrap(); + let opposite_field = opposite_rf.ast_field(); let related_model_type = if opposite_model.ast_model().is_view() { "view" } else { "model" }; - let self_relation = if relation.is_self_relation() { " on self" } else { " " }; - let relation_kind = format!("{}{}", relation.relation_kind(), self_relation); Some(format_hover_content( opposite_model.ast_model().documentation().unwrap_or_default(), related_model_type, name, - Some((relation_kind, opposite_field)), + Some((opposite_rf, opposite_field)), )) } } @@ -193,12 +194,12 @@ fn format_hover_content( documentation: &str, variant: &str, name: &str, - relation: Option<(String, &Field)>, + relation: Option<(Walker, &Field)>, ) -> HoverContents { let fancy_line_break = String::from("\n___\n"); - let (field, relation_kind) = relation.map_or((Default::default(), Default::default()), |(rk, field)| { - (format!("\n\t...\n\t{field}\n"), format!("{rk}{fancy_line_break}")) - }); + + let (field, relation_kind) = format_relation_info(relation, &fancy_line_break); + let prisma_display = match variant { "model" | "enum" | "view" | "type" => { format!("```prisma\n{variant} {name} {{{field}}}\n```{fancy_line_break}{relation_kind}") @@ -213,3 +214,35 @@ fn format_hover_content( value: full_signature, }) } + +fn format_relation_info( + relation: Option<(Walker, &Field)>, + fancy_line_break: &String, +) -> (String, String) { + if let Some((rf, field)) = relation { + let relation = rf.relation(); + + let fields = rf + .referencing_fields() + .map(|fields| fields.map(|f| f.to_string()).collect::>().join(", ")) + .map_or_else(String::new, |fields| format!(", fields: [{fields}]")); + + let references = rf + .referenced_fields() + .map(|fields| fields.map(|f| f.to_string()).collect::>().join(", ")) + .map_or_else(String::new, |fields| format!(", references: [{fields}]")); + + let self_relation = if relation.is_self_relation() { " on self" } else { " " }; + let relation_kind = format!("{}{}", relation.relation_kind(), self_relation); + + let relation_name = relation.relation_name(); + let relation_inner = format!("name: \"{relation_name}\"{fields}{references}"); + + ( + format!("\n\t...\n\t{field} @relation({relation_inner})\n"), + format!("{relation_kind}{fancy_line_break}"), + ) + } else { + ("".to_owned(), "".to_owned()) + } +} diff --git a/prisma-fmt/tests/hover/scenarios/model_from_view_type/result.json b/prisma-fmt/tests/hover/scenarios/model_from_view_type/result.json index 877fe41d921..8b1a29b07e1 100644 --- a/prisma-fmt/tests/hover/scenarios/model_from_view_type/result.json +++ b/prisma-fmt/tests/hover/scenarios/model_from_view_type/result.json @@ -1,6 +1,6 @@ { "contents": { "kind": "markdown", - "value": "```prisma\nmodel ModelNameA {\n\t...\n\tval ModelNameB\n}\n```\n___\none-to-many \n___\nThis is doc for A" + "value": "```prisma\nmodel ModelNameA {\n\t...\n\tval ModelNameB @relation(name: \"ModelNameAToModelNameB\", fields: [bId], references: [id])\n}\n```\n___\none-to-many \n___\nThis is doc for A" } } \ No newline at end of file diff --git a/prisma-fmt/tests/hover/scenarios/one_to_many_self_relation/result.json b/prisma-fmt/tests/hover/scenarios/one_to_many_self_relation/result.json index dd6404b2cc7..5663663e427 100644 --- a/prisma-fmt/tests/hover/scenarios/one_to_many_self_relation/result.json +++ b/prisma-fmt/tests/hover/scenarios/one_to_many_self_relation/result.json @@ -1,6 +1,6 @@ { "contents": { "kind": "markdown", - "value": "```prisma\nmodel Bee {\n\t...\n\tB Bee?\n}\n```\n___\none-to-many on self\n___\n" + "value": "```prisma\nmodel Bee {\n\t...\n\tB Bee? @relation(name: \"bees\", fields: [bId], references: [id])\n}\n```\n___\none-to-many on self\n___\n" } } \ No newline at end of file diff --git a/psl/parser-database/src/walkers/enum.rs b/psl/parser-database/src/walkers/enum.rs index 3ddc3005cf4..659a713f1ed 100644 --- a/psl/parser-database/src/walkers/enum.rs +++ b/psl/parser-database/src/walkers/enum.rs @@ -47,7 +47,7 @@ impl<'db> EnumWalker<'db> { /// Returns the specific value from the model. pub fn value(self, value_id: ast::EnumValueId) -> EnumValueWalker<'db> { - self.walk((self.id, value_id.into())) + self.walk((self.id, value_id)) } /// The values of the enum. diff --git a/psl/parser-database/src/walkers/relation_field.rs b/psl/parser-database/src/walkers/relation_field.rs index 8fa24d5163e..e2ddde53e0b 100644 --- a/psl/parser-database/src/walkers/relation_field.rs +++ b/psl/parser-database/src/walkers/relation_field.rs @@ -97,7 +97,7 @@ impl<'db> RelationFieldWalker<'db> { self.db.walk(self.attributes().referenced_model) } - /// The fields in the `@relation(references: ...)` argument. + /// The fields in the `@relation(references: [...])` argument. pub fn referenced_fields(self) -> Option>> { self.attributes() .references @@ -154,7 +154,7 @@ impl<'db> RelationFieldWalker<'db> { self.fields() } - /// The fields in the `fields: [...]` argument in the forward relation field. + /// The fields in the `@relation(fields: [...])` argument in the forward relation field. pub fn fields(self) -> Option> + Clone> { let attributes = &self.db.types[self.id]; attributes diff --git a/psl/parser-database/src/walkers/scalar_field.rs b/psl/parser-database/src/walkers/scalar_field.rs index e36fd9d38db..a81edea8eee 100644 --- a/psl/parser-database/src/walkers/scalar_field.rs +++ b/psl/parser-database/src/walkers/scalar_field.rs @@ -1,3 +1,5 @@ +use std::fmt; + use crate::{ ast::{self, WithName}, types::{DefaultAttribute, FieldWithArgs, OperatorClassStore, ScalarField, ScalarType, SortOrder}, @@ -163,6 +165,12 @@ impl<'db> ScalarFieldWalker<'db> { } } +impl<'db> fmt::Display for ScalarFieldWalker<'db> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.name()) + } +} + /// An `@default()` attribute on a field. #[derive(Clone, Copy)] pub struct DefaultValueWalker<'db> {