Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Datasource Spans & Completions #3703

Merged
merged 31 commits into from
Mar 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
01a2ea3
Added datasource completions from LT to prisma-fmt
Druue Feb 10, 2023
1856db1
Completions for DS now available at start of line
Druue Feb 10, 2023
1012933
ds completions no longer available outside of the closing curly-brace
Druue Feb 10, 2023
6ef9feb
Updated the rest of the grammar and parsing logic
Druue Feb 21, 2023
15134e0
Updated reformatter to handle inner_contents
Druue Feb 21, 2023
b9650cf
fix prisma-fmt tests to reflect span changes
Druue Feb 22, 2023
6af1430
added tests for default ds completions and multischema
Druue Feb 22, 2023
9e0d9e0
Added: sub-completions for xyz_url
Druue Feb 28, 2023
167c549
Update: config values now Optional
Druue Mar 2, 2023
d84c5b9
Added tests for url argument completions
Druue Mar 2, 2023
3a3cc52
Updated get_config test snapshots
Druue Mar 2, 2023
b991179
Added support for params to pretty_doc
Druue Mar 2, 2023
bd30177
Added XYZ_URL completion for `env()`
Druue Mar 2, 2023
92a535b
Updated completion test snapshots due to params
Druue Mar 2, 2023
20350aa
Remove engines tag from completion labels
Druue Mar 3, 2023
46e2010
removed commented line
Druue Mar 3, 2023
283bff0
update `env` doc to mention db pull
Druue Mar 6, 2023
3dc5864
extensions completion
Druue Mar 13, 2023
6b53257
validation to only offer completions for what's not already there
Druue Mar 13, 2023
4c9ec76
`add_quotes` for envar name completions
Druue Mar 13, 2023
f3dd1d6
Updated Tests:
Druue Mar 13, 2023
4a31989
missed change in merge
Druue Mar 13, 2023
02971bb
no fancy features allowed
Druue Mar 13, 2023
e2a088e
revert direct_url span back to just the arg
Druue Mar 14, 2023
c8890a7
Clippy complaining about unused inner_span for models and views
Druue Mar 14, 2023
687f0e1
formatting
Druue Mar 14, 2023
194ab42
Update ds and connector to check prop definitions
Druue Mar 14, 2023
3762e2e
cleanup unused
Druue Mar 14, 2023
7c9018d
Added connector specific completion capabilities.
Druue Mar 15, 2023
4142420
Add schemas completions to other connectors
Druue Mar 15, 2023
b8d6880
rename push_completions -> datamodel_completions
Druue Mar 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion prisma-fmt/src/get_dmmf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ mod tests {
});

let expected = expect![[
r#"{"error_code":"P1012","message":"\u001b[1;91merror\u001b[0m: \u001b[1mThe `referentialIntegrity` and `relationMode` attributes cannot be used together. Please use only `relationMode` instead.\u001b[0m\n \u001b[1;94m-->\u001b[0m \u001b[4mschema.prisma:6\u001b[0m\n\u001b[1;94m | \u001b[0m\n\u001b[1;94m 5 | \u001b[0m relationMode = \"prisma\"\n\u001b[1;94m 6 | \u001b[0m \u001b[1;91mreferentialIntegrity = \"foreignKeys\"\u001b[0m\n\u001b[1;94m 7 | \u001b[0m }\n\u001b[1;94m | \u001b[0m\n\nValidation Error Count: 1"}"#
r#"{"error_code":"P1012","message":"\u001b[1;91merror\u001b[0m: \u001b[1mThe `referentialIntegrity` and `relationMode` attributes cannot be used together. Please use only `relationMode` instead.\u001b[0m\n \u001b[1;94m-->\u001b[0m \u001b[4mschema.prisma:6\u001b[0m\n\u001b[1;94m | \u001b[0m\n\u001b[1;94m 5 | \u001b[0m relationMode = \"prisma\"\n\u001b[1;94m 6 | \u001b[0m \u001b[1;91mreferentialIntegrity = \"foreignKeys\"\u001b[0m\n\u001b[1;94m | \u001b[0m\n\nValidation Error Count: 1"}"#
]];
let response = get_dmmf(&request.to_string()).unwrap_err();
expected.assert_eq(&response);
Expand Down
67 changes: 66 additions & 1 deletion prisma-fmt/src/text_document_completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ use std::sync::Arc;

use crate::position_to_offset;

mod datasource;

pub(crate) fn empty_completion_list() -> CompletionList {
CompletionList {
is_incomplete: true,
Expand Down Expand Up @@ -119,7 +121,70 @@ fn push_ast_completions(ctx: CompletionContext<'_>, completion_list: &mut Comple
push_namespaces(ctx, completion_list);
}

position => ctx.connector().push_completions(ctx.db, position, completion_list),
ast::SchemaPosition::DataSource(_source_id, ast::SourcePosition::Source) => {
if !ds_has_prop(ctx, "provider") {
datasource::provider_completion(completion_list);
}

if !ds_has_prop(ctx, "url") {
datasource::url_completion(completion_list);
}

if !ds_has_prop(ctx, "shadowDatabaseUrl") {
datasource::shadow_db_completion(completion_list);
}

if !ds_has_prop(ctx, "directUrl") {
datasource::direct_url_completion(completion_list);
}

if !ds_has_prop(ctx, "relationMode") {
datasource::relation_mode_completion(completion_list);
}

if let Some(config) = ctx.config {
ctx.connector().datasource_completions(config, completion_list);
}
}

ast::SchemaPosition::DataSource(
_source_id,
ast::SourcePosition::Property("url", ast::PropertyPosition::FunctionValue("env")),
) => datasource::url_env_db_completion(completion_list, "url", ctx),

ast::SchemaPosition::DataSource(
_source_id,
ast::SourcePosition::Property("directUrl", ast::PropertyPosition::FunctionValue("env")),
) => datasource::url_env_db_completion(completion_list, "directUrl", ctx),

ast::SchemaPosition::DataSource(
_source_id,
ast::SourcePosition::Property("shadowDatabaseUrl", ast::PropertyPosition::FunctionValue("env")),
) => datasource::url_env_db_completion(completion_list, "shadowDatabaseUrl", ctx),

ast::SchemaPosition::DataSource(_source_id, ast::SourcePosition::Property("url", _))
| ast::SchemaPosition::DataSource(_source_id, ast::SourcePosition::Property("directUrl", _))
| ast::SchemaPosition::DataSource(_source_id, ast::SourcePosition::Property("shadowDatabaseUrl", _)) => {
datasource::url_env_completion(completion_list);
datasource::url_quotes_completion(completion_list);
}

position => ctx.connector().datamodel_completions(ctx.db, position, completion_list),
}
}

fn ds_has_prop(ctx: CompletionContext<'_>, prop: &str) -> bool {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd implement these directly in the datasource. So, we could ask from datasource ds.schemas_defined() and so on.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure do we really need this. Maybe in a follow-up PR.

if let Some(ds) = ctx.datasource() {
match prop {
"relationMode" => ds.relation_mode_defined(),
"directurl" => ds.direct_url_defined(),
"shadowDatabaseUrl" => ds.shadow_url_defined(),
"url" => ds.url_defined(),
"provider" => ds.provider_defined(),
_ => false,
}
} else {
false
}
}

Expand Down
160 changes: 160 additions & 0 deletions prisma-fmt/src/text_document_completion/datasource.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
use std::collections::HashMap;

use lsp_types::{
CompletionItem, CompletionItemKind, CompletionList, Documentation, InsertTextFormat, MarkupContent, MarkupKind,
};
use psl::datamodel_connector::format_completion_docs;

use super::{add_quotes, CompletionContext};

pub(super) fn relation_mode_completion(completion_list: &mut CompletionList) {
completion_list.items.push(CompletionItem {
label: "relationMode".to_owned(),
insert_text: Some(r#"relationmode = $0"#.to_owned()),
pimeys marked this conversation as resolved.
Show resolved Hide resolved
insert_text_format: Some(InsertTextFormat::SNIPPET),
kind: Some(CompletionItemKind::FIELD),
documentation: Some(Documentation::MarkupContent(MarkupContent {
kind: MarkupKind::Markdown,
value: format_completion_docs(
r#"relationMode = "foreignKeys" | "prisma""#,
r#"Set the global relation mode for all relations. Values can be either "foreignKeys" (Default), or "prisma". [Learn more](https://pris.ly/d/relation-mode)"#,
None,
),
})),
..Default::default()
})
}

pub(super) fn direct_url_completion(completion_list: &mut CompletionList) {
completion_list.items.push(CompletionItem {
label: "directUrl".to_owned(),
insert_text: Some(r#"directUrl = $0"#.to_owned()),
insert_text_format: Some(InsertTextFormat::SNIPPET),
kind: Some(CompletionItemKind::FIELD),
documentation: Some(Documentation::MarkupContent(MarkupContent {
kind: MarkupKind::Markdown,
value: format_completion_docs(
r#"directUrl = "String" | env("ENVIRONMENT_VARIABLE")"#,
r#"Connection URL for direct connection to the database. [Learn more](https://pris.ly/d/data-proxy-cli)."#,
None,
)
})),
..Default::default()
})
}

pub(super) fn shadow_db_completion(completion_list: &mut CompletionList) {
completion_list.items.push(CompletionItem {
label: "shadowDatabaseUrl".to_owned(),
insert_text: Some(r#"shadowDatabaseUrl = $0"#.to_owned()),
insert_text_format: Some(InsertTextFormat::SNIPPET),
kind: Some(CompletionItemKind::FIELD),
documentation: Some(Documentation::MarkupContent(MarkupContent {
kind: MarkupKind::Markdown,
value: format_completion_docs(
r#"shadowDatabaseUrl = "String" | env("ENVIRONMENT_VARIABLE")"#,
r#"Connection URL including authentication info to use for Migrate's [shadow database](https://pris.ly/d/migrate-shadow)."#,
None,
),
})),
..Default::default()
})
}

pub(super) fn url_completion(completion_list: &mut CompletionList) {
completion_list.items.push(CompletionItem {
label: "url".to_owned(),
insert_text: Some(r#"url = $0"#.to_owned()),
insert_text_format: Some(InsertTextFormat::SNIPPET),
kind: Some(CompletionItemKind::FIELD),
documentation: Some(Documentation::MarkupContent(MarkupContent {
kind: MarkupKind::Markdown,
value: format_completion_docs(
r#"url = "String" | env("ENVIRONMENT_VARIABLE")"#,
r#"Connection URL including authentication info. Each datasource provider documents the URL syntax. Most providers use the syntax provided by the database. [Learn more](https://pris.ly/d/connection-strings)."#,
None,
),
})),
..Default::default()
})
}

pub(super) fn provider_completion(completion_list: &mut CompletionList) {
completion_list.items.push(CompletionItem {
label: "provider".to_owned(),
insert_text: Some(r#"provider = $0"#.to_owned()),
insert_text_format: Some(InsertTextFormat::SNIPPET),
kind: Some(CompletionItemKind::FIELD),
documentation: Some(Documentation::MarkupContent(MarkupContent {
kind: MarkupKind::Markdown,
value: format_completion_docs(
r#"provider = "foo""#,
r#"Describes which datasource connector to use. Can be one of the following datasource providers: `postgresql`, `mysql`, `sqlserver`, `sqlite`, `mongodb` or `cockroachdb`."#,
None,
),
})),
..Default::default()
})
}

pub(super) fn url_env_completion(completion_list: &mut CompletionList) {
completion_list.items.push(CompletionItem {
label: "env()".to_owned(),
insert_text: Some(r#"env($0)"#.to_owned()),
insert_text_format: Some(InsertTextFormat::SNIPPET),
kind: Some(CompletionItemKind::PROPERTY),
documentation: Some(Documentation::MarkupContent(MarkupContent {
kind: MarkupKind::Markdown,
value: format_completion_docs(
r#"env(_ environmentVariable: string)"#,
r#"Specifies a datasource via an environment variable. When running a Prisma CLI command that needs the database connection URL (e.g. `prisma db pull`), you need to make sure that the `DATABASE_URL` environment variable is set. One way to do so is by creating a `.env` file. Note that the file must be in the same directory as your schema.prisma file to automatically be picked up by the Prisma CLI.""#,
Some(HashMap::from([(
"environmentVariable",
"The environment variable in which the database connection URL is stored.",
)])),
),
})),
..Default::default()
})
}

pub(super) fn url_quotes_completion(completion_list: &mut CompletionList) {
completion_list.items.push(CompletionItem {
label: r#""""#.to_owned(),
insert_text: Some(r#""$0""#.to_owned()),
insert_text_format: Some(InsertTextFormat::SNIPPET),
kind: Some(CompletionItemKind::PROPERTY),
documentation: Some(Documentation::MarkupContent(MarkupContent {
kind: MarkupKind::Markdown,
value: format_completion_docs(
r#""connectionString""#,
r#"Connection URL including authentication info. Each datasource provider documents the URL syntax. Most providers use the syntax provided by the database. [Learn more](https://pris.ly/d/prisma-schema)."#,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplication with line 92 avoidable?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As an update to url

Connection URL to connect to your database. [Learn more](https://pris.ly/d/connection-strings).
// instead of 
Connection URL including authentication info. Each datasource provider documents the URL syntax. Most providers use the syntax provided by the database. [Learn more](https://pris.ly/d/connection-strings).

None,
),
})),
..Default::default()
})
}

pub(super) fn url_env_db_completion(completion_list: &mut CompletionList, kind: &str, ctx: CompletionContext<'_>) {
let text = match kind {
"url" => "DATABASE_URL",
"directUrl" => "DIRECT_URL",
"shadowDatabaseUrl" => "SHADOW_DATABASE_URL",
_ => unreachable!(),
};

let insert_text = if add_quotes(ctx.params, ctx.db.source()) {
format!(r#""{text}""#)
} else {
text.to_owned()
};

completion_list.items.push(CompletionItem {
label: text.to_owned(),
insert_text: Some(insert_text),
insert_text_format: Some(InsertTextFormat::PLAIN_TEXT),
kind: Some(CompletionItemKind::CONSTANT),
..Default::default()
})
}
3 changes: 2 additions & 1 deletion prisma-fmt/src/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,9 @@ mod tests {
});

let expected = expect![[
r#"{"error_code":"P1012","message":"\u001b[1;91merror\u001b[0m: \u001b[1mThe `referentialIntegrity` and `relationMode` attributes cannot be used together. Please use only `relationMode` instead.\u001b[0m\n \u001b[1;94m-->\u001b[0m \u001b[4mschema.prisma:6\u001b[0m\n\u001b[1;94m | \u001b[0m\n\u001b[1;94m 5 | \u001b[0m relationMode = \"prisma\"\n\u001b[1;94m 6 | \u001b[0m \u001b[1;91mreferentialIntegrity = \"foreignKeys\"\u001b[0m\n\u001b[1;94m 7 | \u001b[0m }\n\u001b[1;94m | \u001b[0m\n\nValidation Error Count: 1"}"#
r#"{"error_code":"P1012","message":"\u001b[1;91merror\u001b[0m: \u001b[1mThe `referentialIntegrity` and `relationMode` attributes cannot be used together. Please use only `relationMode` instead.\u001b[0m\n \u001b[1;94m-->\u001b[0m \u001b[4mschema.prisma:6\u001b[0m\n\u001b[1;94m | \u001b[0m\n\u001b[1;94m 5 | \u001b[0m relationMode = \"prisma\"\n\u001b[1;94m 6 | \u001b[0m \u001b[1;91mreferentialIntegrity = \"foreignKeys\"\u001b[0m\n\u001b[1;94m | \u001b[0m\n\nValidation Error Count: 1"}"#
]];

let response = validate(&request.to_string()).unwrap_err();
expected.assert_eq(&response);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@
"character": 4
},
"end": {
"line": 4,
"character": 0
"line": 3,
"character": 35
Druue marked this conversation as resolved.
Show resolved Hide resolved
}
},
"severity": 2,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
{
"isIncomplete": false,
"items": [
{
"label": "provider",
"kind": 5,
"documentation": {
"kind": "markdown",
"value": "```prisma\nprovider = \"foo\"\n```\n___\nDescribes which datasource connector to use. Can be one of the following datasource providers: `postgresql`, `mysql`, `sqlserver`, `sqlite`, `mongodb` or `cockroachdb`.\n\n"
},
"insertText": "provider = $0",
"insertTextFormat": 2
},
{
"label": "url",
"kind": 5,
"documentation": {
"kind": "markdown",
"value": "```prisma\nurl = \"String\" | env(\"ENVIRONMENT_VARIABLE\")\n```\n___\nConnection URL including authentication info. Each datasource provider documents the URL syntax. Most providers use the syntax provided by the database. [Learn more](https://pris.ly/d/connection-strings).\n\n"
},
"insertText": "url = $0",
"insertTextFormat": 2
},
{
"label": "shadowDatabaseUrl",
"kind": 5,
"documentation": {
"kind": "markdown",
"value": "```prisma\nshadowDatabaseUrl = \"String\" | env(\"ENVIRONMENT_VARIABLE\")\n```\n___\nConnection URL including authentication info to use for Migrate's [shadow database](https://pris.ly/d/migrate-shadow).\n\n"
},
"insertText": "shadowDatabaseUrl = $0",
"insertTextFormat": 2
},
{
"label": "directUrl",
"kind": 5,
"documentation": {
"kind": "markdown",
"value": "```prisma\ndirectUrl = \"String\" | env(\"ENVIRONMENT_VARIABLE\")\n```\n___\nConnection URL for direct connection to the database. [Learn more](https://pris.ly/d/data-proxy-cli).\n\n"
},
"insertText": "directUrl = $0",
"insertTextFormat": 2
},
{
"label": "relationMode",
"kind": 5,
"documentation": {
"kind": "markdown",
"value": "```prisma\nrelationMode = \"foreignKeys\" | \"prisma\"\n```\n___\nSet the global relation mode for all relations. Values can be either \"foreignKeys\" (Default), or \"prisma\". [Learn more](https://pris.ly/d/relation-mode)\n\n"
},
"insertText": "relationmode = $0",
pimeys marked this conversation as resolved.
Show resolved Hide resolved
"insertTextFormat": 2
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
generator client {
provider = "prisma-client-js"
}

datasource db {
<|>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"isIncomplete": false,
"items": [
{
"label": "env()",
"kind": 10,
"documentation": {
"kind": "markdown",
"value": "```prisma\nenv(_ environmentVariable: string)\n```\n___\nSpecifies a datasource via an environment variable. When running a Prisma CLI command that needs the database connection URL (e.g. `prisma db pull`), you need to make sure that the `DATABASE_URL` environment variable is set. One way to do so is by creating a `.env` file. Note that the file must be in the same directory as your schema.prisma file to automatically be picked up by the Prisma CLI.\"\n\n_@param_ environmentVariable The environment variable in which the database connection URL is stored."
},
"insertText": "env($0)",
"insertTextFormat": 2
},
{
"label": "\"\"",
"kind": 10,
"documentation": {
"kind": "markdown",
"value": "```prisma\n\"connectionString\"\n```\n___\nConnection URL including authentication info. Each datasource provider documents the URL syntax. Most providers use the syntax provided by the database. [Learn more](https://pris.ly/d/prisma-schema).\n\n"
},
"insertText": "\"$0\"",
"insertTextFormat": 2
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
generator client {
provider = "prisma-client-js"
previewFeatures = ["multischema"]
}

datasource db {
provider = "postgresql"
url = ""
directUrl = <|>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"isIncomplete": false,
"items": [
{
"label": "DIRECT_URL",
"kind": 21,
"insertText": "DIRECT_URL",
"insertTextFormat": 1
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
generator client {
provider = "prisma-client-js"
previewFeatures = ["multischema"]
}

datasource db {
provider = "postgresql"
url = env("")
directUrl = env("<|>")
}
Loading