diff --git a/packages/language-server/src/completion/completionUtil.ts b/packages/language-server/src/completion/completionUtil.ts index 29c722bac7..f026ce7240 100644 --- a/packages/language-server/src/completion/completionUtil.ts +++ b/packages/language-server/src/completion/completionUtil.ts @@ -44,7 +44,9 @@ function convertAttributesToCompletionItems( itemKind: CompletionItemKind, insertTextFunc: (label: string) => string, ): CompletionItem[] { + // https://code.visualstudio.com/api/references/vscode-api#CompletionItem const result: CompletionItem[] = [] + for (const item of completionItems) { const docComment = [ '```prisma', diff --git a/packages/language-server/src/completion/completions.json b/packages/language-server/src/completion/completions.json index 07936ae75b..5e3e972926 100644 --- a/packages/language-server/src/completion/completions.json +++ b/packages/language-server/src/completion/completions.json @@ -240,19 +240,67 @@ }, { "label": "onDelete: ", - "fullSignature": "onDelete: ", - "documentation": "Specifies the action to perform when a referenced entry in the referenced model is being deleted.", - "params": [] + "fullSignature": "onDelete: Cascade | Restrict | NoAction | SetNull | SetDefault", + "documentation": "Specifies the action to perform when a referenced entry in the referenced model is being deleted, [learn more](https://www.prisma.io/docs/concepts/components/prisma-schema/relations/referential-actions).", + "params": [ + { + "label": "Cascade", + "documentation": "Deleting a referenced record will trigger the deletion of referencing record." + }, + { + "label": "Restrict", + "documentation": "Prevents the deletion if any referencing records exist." + }, + { + "label": "NoAction", + "documentation": "Is similar to Restrict, the difference between the two is dependant on the database being used, [learn more](https://www.prisma.io/docs/concepts/components/prisma-schema/relations/referential-actions)." + }, + { + "label": "SetNull", + "documentation": "The scalar field of the referenced object will be set to NULL." + }, + { + "label": "SetDefault", + "documentation": "The scalar field of the referencing object will be set to the fields default value." + } + ] }, { "label": "onUpdate: ", - "fullSignature": "onUpdate: ", - "documentation": "Specifies the action to perform when a referenced field in the referenced model is being updated to a new value.", - "params": [] + "fullSignature": "onUpdate: Cascade | Restrict | NoAction | SetNull | SetDefault", + "documentation": "Specifies the action to perform when a referenced field in the referenced model is being updated to a new value, [learn more](https://www.prisma.io/docs/concepts/components/prisma-schema/relations/referential-actions).", + "params": [ + { + "label": "Cascade", + "documentation": "Updates the relation scalar fields if the referenced scalar fields of the dependant record are updated." + }, + { + "label": "Restrict", + "documentation": "Prevents the identifier of a referenced record from being changed." + }, + { + "label": "NoAction", + "documentation": "Is similar to Restrict, the difference between the two is dependant on the database being used, [learn more](https://www.prisma.io/docs/concepts/components/prisma-schema/relations/referential-actions)." + }, + { + "label": "SetNull", + "documentation": "When updating the identifier of a referenced object, the scalar fields of the referencing objects will be set to NULL." + }, + { + "label": "SetDefault", + "documentation": "The scalar field of the referencing object will be set to the fields default value." + } + ] }, { "label": "\"\"", - "fullSignature": "\"\"", + "fullSignature": "String", + "documentation": "Defines the name of the relationship. In an m-n-relation, it also determines the name of the underlying relation table.", + "params": [] + }, + { + "label": "name: ", + "fullSignature": "name: String", "documentation": "Defines the name of the relationship. In an m-n-relation, it also determines the name of the underlying relation table.", "params": [] } diff --git a/packages/language-server/src/completion/completions.ts b/packages/language-server/src/completion/completions.ts index 5f1b2194cc..163472cec6 100644 --- a/packages/language-server/src/completion/completions.ts +++ b/packages/language-server/src/completion/completions.ts @@ -933,25 +933,58 @@ function getSuggestionsForRelationDirective( isIncomplete: false, } } + if (stringTilPosition.endsWith(',')) { - const referencesExist = wordsBeforePosition.some((a) => - a.includes('references'), + // Check which attributes are already present + // so we can filter them out from the suggestions + const attributesFound: Set = new Set() + + for (const word of wordsBeforePosition) { + if (word.includes('references')) { + attributesFound.add('references') + } + if (word.includes('fields')) { + attributesFound.add('fields') + } + if (word.includes('onUpdate')) { + attributesFound.add('onUpdate') + } + if (word.includes('onDelete')) { + attributesFound.add('onDelete') + } + if (word.includes('name') || /".*"/.exec(word)) { + attributesFound.add('name') + attributesFound.add('""') + } + } + + // now filter them out of the suggestions as they are already present + const filteredSuggestions: CompletionItem[] = suggestions.reduce( + (accumulator: CompletionItem[] & unknown[], sugg) => { + let suggestionMatch = false + for (const attribute of attributesFound) { + if (sugg.label.includes(attribute)) { + suggestionMatch = true + } + } + + if (!suggestionMatch) { + accumulator.push(sugg) + } + + return accumulator + }, + [], ) - const fieldsExist = wordsBeforePosition.some((a) => a.includes('fields')) - if (referencesExist && fieldsExist) { + + // nothing to present any more, return + if (filteredSuggestions.length === 0) { return } - if (referencesExist) { - return { - items: suggestions.filter((sugg) => !sugg.label.includes('references')), - isIncomplete: false, - } - } - if (fieldsExist) { - return { - items: suggestions.filter((sugg) => !sugg.label.includes('fields')), - isIncomplete: false, - } + + return { + items: filteredSuggestions, + isIncomplete: false, } } } diff --git a/packages/language-server/src/test/completion.test.ts b/packages/language-server/src/test/completion.test.ts index 180c100916..4767f68bf2 100644 --- a/packages/language-server/src/test/completion.test.ts +++ b/packages/language-server/src/test/completion.test.ts @@ -65,41 +65,7 @@ suite('Completions', () => { const emptyBlocksUri = 'completions/emptyBlocks.prisma' const modelBlocksUri = 'completions/modelBlocks.prisma' const enumCommentUri = 'completions/enumWithComments.prisma' - - // ALL BLOCKS - - test('Diagnoses block type suggestions with sqlite as provider', () => { - assertCompletion( - sqliteDocUri, - { line: 4, character: 0 }, - { - isIncomplete: false, - items: [ - { label: 'datasource', kind: CompletionItemKind.Class }, - { label: 'generator', kind: CompletionItemKind.Class }, - { label: 'model', kind: CompletionItemKind.Class }, - ], - }, - ) - }) - - test('Diagnoses block type suggestions for empty file', () => { - assertCompletion( - emptyDocUri, - { line: 0, character: 0 }, - { - isIncomplete: false, - items: [ - { label: 'datasource', kind: CompletionItemKind.Class }, - { label: 'generator', kind: CompletionItemKind.Class }, - { label: 'model', kind: CompletionItemKind.Class }, - { label: 'enum', kind: CompletionItemKind.Class }, - ], - }, - ) - }) - - // DATASOURCE BLOCK + const relationDirectiveUri = 'completions/relationDirective.prisma' const fieldPreviewFeatures = { label: 'previewFeatures', @@ -109,612 +75,680 @@ suite('Completions', () => { label: 'provider', kind: CompletionItemKind.Field, } - const fieldUrl = { label: 'url', kind: CompletionItemKind.Field } - const fieldShadowDatabaseUrl = { - label: 'shadowDatabaseUrl', - kind: CompletionItemKind.Field, - } - const sqlite = { label: 'sqlite', kind: CompletionItemKind.Constant } - const mysql = { label: 'mysql', kind: CompletionItemKind.Constant } - const postgresql = { - label: 'postgresql', - kind: CompletionItemKind.Constant, - } - const mssql = { - label: 'sqlserver', - kind: CompletionItemKind.Constant, - } - const array = { label: '[]', kind: CompletionItemKind.Property } - const quotationMarks = { - label: '""', - kind: CompletionItemKind.Property, - } - const envArgument = { - label: 'DATABASE_URL', - kind: CompletionItemKind.Constant, - } - const env = { label: 'env()', kind: CompletionItemKind.Property } - - test('Diagnoses datasource field suggestions in empty block', () => { - assertCompletion( - emptyBlocksUri, - { line: 1, character: 0 }, - { - isIncomplete: false, - items: [fieldProvider, fieldUrl, fieldShadowDatabaseUrl], - }, - ) - }) - test('Diagnoses datasource field suggestions with existing field', () => { - assertCompletion( - sqliteDocUri, - { line: 2, character: 0 }, - { - isIncomplete: false, - items: [fieldUrl, fieldShadowDatabaseUrl], - }, - ) - assertCompletion( - dataSourceWithUri, - { line: 2, character: 0 }, - { - isIncomplete: false, - items: [fieldProvider, fieldShadowDatabaseUrl], - }, - ) - }) + suite('BASE BLOCKS', () => { + test('Diagnoses block type suggestions with sqlite as provider', () => { + assertCompletion( + sqliteDocUri, + { line: 4, character: 0 }, + { + isIncomplete: false, + items: [ + { label: 'datasource', kind: CompletionItemKind.Class }, + { label: 'generator', kind: CompletionItemKind.Class }, + { label: 'model', kind: CompletionItemKind.Class }, + ], + }, + ) + }) - test('Diagnoses url argument suggestions for datasource block', () => { - assertCompletion( - dataSourceWithUri, - { line: 7, character: 10 }, - { - isIncomplete: true, - items: [quotationMarks, env], - }, - ), + test('Diagnoses block type suggestions for empty file', () => { assertCompletion( - dataSourceWithUri, - { line: 11, character: 15 }, + emptyDocUri, + { line: 0, character: 0 }, { isIncomplete: false, - items: [envArgument], + items: [ + { label: 'datasource', kind: CompletionItemKind.Class }, + { label: 'generator', kind: CompletionItemKind.Class }, + { label: 'model', kind: CompletionItemKind.Class }, + { label: 'enum', kind: CompletionItemKind.Class }, + ], }, ) + }) }) - test('Diagnoses single provider suggestions for datasource block', () => { - assertCompletion( - sqliteDocUri, - { line: 14, character: 14 }, - { - isIncomplete: true, - items: [mysql, postgresql, sqlite, mssql], - }, - ), + suite('DATABASE BLOCK', () => { + const fieldUrl = { label: 'url', kind: CompletionItemKind.Field } + const fieldShadowDatabaseUrl = { + label: 'shadowDatabaseUrl', + kind: CompletionItemKind.Field, + } + const sqlite = { label: 'sqlite', kind: CompletionItemKind.Constant } + const mysql = { label: 'mysql', kind: CompletionItemKind.Constant } + const postgresql = { + label: 'postgresql', + kind: CompletionItemKind.Constant, + } + const mssql = { + label: 'sqlserver', + kind: CompletionItemKind.Constant, + } + const array = { label: '[]', kind: CompletionItemKind.Property } + const quotationMarks = { + label: '""', + kind: CompletionItemKind.Property, + } + const envArgument = { + label: 'DATABASE_URL', + kind: CompletionItemKind.Constant, + } + const env = { label: 'env()', kind: CompletionItemKind.Property } + + test('Diagnoses datasource field suggestions in empty block', () => { + assertCompletion( + emptyBlocksUri, + { line: 1, character: 0 }, + { + isIncomplete: false, + items: [fieldProvider, fieldUrl, fieldShadowDatabaseUrl], + }, + ) + }) + + test('Diagnoses datasource field suggestions with existing field', () => { assertCompletion( sqliteDocUri, - { line: 18, character: 13 }, + { line: 2, character: 0 }, { - isIncomplete: true, - items: [array, quotationMarks], + isIncomplete: false, + items: [fieldUrl, fieldShadowDatabaseUrl], }, ) - }) + assertCompletion( + dataSourceWithUri, + { line: 2, character: 0 }, + { + isIncomplete: false, + items: [fieldProvider, fieldShadowDatabaseUrl], + }, + ) + }) - test('Diagnoses multiple provider suggestions for datasource block', () => { - assertCompletion( - sqliteDocUri, - { line: 6, character: 15 }, - { - isIncomplete: true, - items: [mysql, postgresql, sqlite, mssql], - }, - ), + test('Diagnoses url argument suggestions for datasource block', () => { + assertCompletion( + dataSourceWithUri, + { line: 7, character: 10 }, + { + isIncomplete: true, + items: [quotationMarks, env], + }, + ), + assertCompletion( + dataSourceWithUri, + { line: 11, character: 15 }, + { + isIncomplete: false, + items: [envArgument], + }, + ) + }) + + test('Diagnoses single provider suggestions for datasource block', () => { + assertCompletion( + sqliteDocUri, + { line: 14, character: 14 }, + { + isIncomplete: true, + items: [mysql, postgresql, sqlite, mssql], + }, + ), + assertCompletion( + sqliteDocUri, + { line: 18, character: 13 }, + { + isIncomplete: true, + items: [array, quotationMarks], + }, + ) + }) + + test('Diagnoses multiple provider suggestions for datasource block', () => { assertCompletion( sqliteDocUri, - { line: 22, character: 14 }, + { line: 6, character: 15 }, { isIncomplete: true, - items: [quotationMarks], + items: [mysql, postgresql, sqlite, mssql], + }, + ), + assertCompletion( + sqliteDocUri, + { line: 22, character: 14 }, + { + isIncomplete: true, + items: [quotationMarks], + }, + ) + assertCompletion( + sqliteDocUri, + { line: 10, character: 25 }, + { + isIncomplete: true, + items: [mysql, postgresql, mssql], }, ) - assertCompletion( - sqliteDocUri, - { line: 10, character: 25 }, - { - isIncomplete: true, - items: [mysql, postgresql, mssql], - }, - ) + }) }) - // GENERATOR BLOCK + suite('GENERATOR BLOCK', () => { + const fieldOutput = { label: 'output', kind: CompletionItemKind.Field } + const fieldBinaryTargets = { + label: 'binaryTargets', + kind: CompletionItemKind.Field, + } - const fieldOutput = { label: 'output', kind: CompletionItemKind.Field } - const fieldBinaryTargets = { - label: 'binaryTargets', - kind: CompletionItemKind.Field, - } + const generatorWithExistingFieldsUri = + 'completions/generatorWithExistingFields.prisma' - const generatorWithExistingFieldsUri = - 'completions/generatorWithExistingFields.prisma' - - test('Diagnoses generator field suggestions in empty block', () => { - assertCompletion( - emptyBlocksUri, - { line: 5, character: 0 }, - { - isIncomplete: false, - items: [ - fieldProvider, - fieldOutput, - fieldBinaryTargets, - fieldPreviewFeatures, - ], - }, - ) - }) + test('Diagnoses generator field suggestions in empty block', () => { + assertCompletion( + emptyBlocksUri, + { line: 5, character: 0 }, + { + isIncomplete: false, + items: [ + fieldProvider, + fieldOutput, + fieldBinaryTargets, + fieldPreviewFeatures, + ], + }, + ) + }) - test('Diagnoses generator field suggestions with existing fields', () => { - assertCompletion( - generatorWithExistingFieldsUri, - { line: 2, character: 0 }, - { - isIncomplete: false, - items: [fieldOutput, fieldBinaryTargets, fieldPreviewFeatures], - }, - ) - assertCompletion( - generatorWithExistingFieldsUri, - { line: 7, character: 0 }, - { - isIncomplete: false, - items: [fieldProvider, fieldBinaryTargets, fieldPreviewFeatures], - }, - ) + test('Diagnoses generator field suggestions with existing fields', () => { + assertCompletion( + generatorWithExistingFieldsUri, + { line: 2, character: 0 }, + { + isIncomplete: false, + items: [fieldOutput, fieldBinaryTargets, fieldPreviewFeatures], + }, + ) + assertCompletion( + generatorWithExistingFieldsUri, + { line: 7, character: 0 }, + { + isIncomplete: false, + items: [fieldProvider, fieldBinaryTargets, fieldPreviewFeatures], + }, + ) + }) }) - // BLOCK ATTRIBUTES - - const blockAttributeId = { - label: '@@id([])', - kind: CompletionItemKind.Property, - } - const blockAttributeMap = { - label: '@@map("")', - kind: CompletionItemKind.Property, - } - const blockAttributeUnique = { - label: '@@unique([])', - kind: CompletionItemKind.Property, - } - const blockAttributeIndex = { - label: '@@index([])', - kind: CompletionItemKind.Property, - } - const blockAttributeIgnore = { - label: '@@ignore', - kind: CompletionItemKind.Property, - } + suite('BLOCK ATTRIBUTES', () => { + const blockAttributeId = { + label: '@@id([])', + kind: CompletionItemKind.Property, + } + const blockAttributeMap = { + label: '@@map("")', + kind: CompletionItemKind.Property, + } + const blockAttributeUnique = { + label: '@@unique([])', + kind: CompletionItemKind.Property, + } + const blockAttributeIndex = { + label: '@@index([])', + kind: CompletionItemKind.Property, + } + const blockAttributeIgnore = { + label: '@@ignore', + kind: CompletionItemKind.Property, + } - test('Diagnoses block attribute suggestions first in a line', () => { - assertCompletion( - emptyBlocksUri, - { line: 9, character: 0 }, - { - isIncomplete: false, - items: [ - blockAttributeMap, - blockAttributeId, - blockAttributeUnique, - blockAttributeIndex, - blockAttributeIgnore, - ], - }, - ) - }) + test('Diagnoses block attribute suggestions first in a line', () => { + assertCompletion( + emptyBlocksUri, + { line: 9, character: 0 }, + { + isIncomplete: false, + items: [ + blockAttributeMap, + blockAttributeId, + blockAttributeUnique, + blockAttributeIndex, + blockAttributeIgnore, + ], + }, + ) + }) - test('Diagnoses block attribute suggestions with existing attributes first in a line', () => { - assertCompletion( - modelBlocksUri, - { line: 5, character: 0 }, - { - isIncomplete: false, - items: [ - blockAttributeMap, - blockAttributeId, - blockAttributeUnique, - blockAttributeIndex, - blockAttributeIgnore, - ], - }, - ) - assertCompletion( - modelBlocksUri, - { line: 14, character: 0 }, - { - isIncomplete: false, - items: [ - blockAttributeMap, - blockAttributeUnique, - blockAttributeIndex, - blockAttributeIgnore, - ], - }, - ) + test('Diagnoses block attribute suggestions with existing attributes first in a line', () => { + assertCompletion( + modelBlocksUri, + { line: 5, character: 0 }, + { + isIncomplete: false, + items: [ + blockAttributeMap, + blockAttributeId, + blockAttributeUnique, + blockAttributeIndex, + blockAttributeIgnore, + ], + }, + ) + assertCompletion( + modelBlocksUri, + { line: 14, character: 0 }, + { + isIncomplete: false, + items: [ + blockAttributeMap, + blockAttributeUnique, + blockAttributeIndex, + blockAttributeIgnore, + ], + }, + ) + }) }) - // TYPES - - test('Diagnoses type suggestions in model block', () => { - assertCompletion( - modelBlocksUri, - { line: 51, character: 7 }, - { - isIncomplete: true, - items: [ - { label: 'String', kind: CompletionItemKind.TypeParameter }, - { label: 'Boolean', kind: CompletionItemKind.TypeParameter }, - { label: 'Int', kind: CompletionItemKind.TypeParameter }, - { label: 'Float', kind: CompletionItemKind.TypeParameter }, - { label: 'DateTime', kind: CompletionItemKind.TypeParameter }, - { label: 'Json', kind: CompletionItemKind.TypeParameter }, - { label: 'Bytes', kind: CompletionItemKind.TypeParameter }, - { label: 'Decimal', kind: CompletionItemKind.TypeParameter }, - { label: 'BigInt', kind: CompletionItemKind.TypeParameter }, - { label: 'Unsupported("")', kind: CompletionItemKind.TypeParameter }, - { label: 'User', kind: CompletionItemKind.Reference }, - { label: 'Post', kind: CompletionItemKind.Reference }, - { label: 'Person', kind: CompletionItemKind.Reference }, - { label: 'Test', kind: CompletionItemKind.Reference }, - { label: 'Cat', kind: CompletionItemKind.Reference }, - { label: 'SecondUser', kind: CompletionItemKind.Reference }, - { label: 'ThirdUser', kind: CompletionItemKind.Reference }, - { label: 'TypeCheck', kind: CompletionItemKind.Reference }, - { label: 'Hello', kind: CompletionItemKind.Reference }, - { label: 'DateTest', kind: CompletionItemKind.Reference }, - { label: 'UserType', kind: CompletionItemKind.Reference }, - { label: 'ForthUser', kind: CompletionItemKind.Reference }, - ], - }, - ) + suite('TYPES', () => { + test('Diagnoses type suggestions in model block', () => { + assertCompletion( + modelBlocksUri, + { line: 51, character: 7 }, + { + isIncomplete: true, + items: [ + { label: 'String', kind: CompletionItemKind.TypeParameter }, + { label: 'Boolean', kind: CompletionItemKind.TypeParameter }, + { label: 'Int', kind: CompletionItemKind.TypeParameter }, + { label: 'Float', kind: CompletionItemKind.TypeParameter }, + { label: 'DateTime', kind: CompletionItemKind.TypeParameter }, + { label: 'Json', kind: CompletionItemKind.TypeParameter }, + { label: 'Bytes', kind: CompletionItemKind.TypeParameter }, + { label: 'Decimal', kind: CompletionItemKind.TypeParameter }, + { label: 'BigInt', kind: CompletionItemKind.TypeParameter }, + { + label: 'Unsupported("")', + kind: CompletionItemKind.TypeParameter, + }, + { label: 'User', kind: CompletionItemKind.Reference }, + { label: 'Post', kind: CompletionItemKind.Reference }, + { label: 'Person', kind: CompletionItemKind.Reference }, + { label: 'Test', kind: CompletionItemKind.Reference }, + { label: 'Cat', kind: CompletionItemKind.Reference }, + { label: 'SecondUser', kind: CompletionItemKind.Reference }, + { label: 'ThirdUser', kind: CompletionItemKind.Reference }, + { label: 'TypeCheck', kind: CompletionItemKind.Reference }, + { label: 'Hello', kind: CompletionItemKind.Reference }, + { label: 'DateTest', kind: CompletionItemKind.Reference }, + { label: 'UserType', kind: CompletionItemKind.Reference }, + { label: 'ForthUser', kind: CompletionItemKind.Reference }, + ], + }, + ) + }) }) - // FIELD ATTRIBUTES - - const fieldAttributeId = { - label: '@id', - kind: CompletionItemKind.Property, - } - const fieldAttributeUnique = { - label: '@unique', - kind: CompletionItemKind.Property, - } - const fieldAttributeMap = { - label: '@map("")', - kind: CompletionItemKind.Property, - } - const fieldAttributeDefault = { - label: '@default()', - kind: CompletionItemKind.Property, - } - const fieldAttributeRelation = { - label: '@relation()', - kind: CompletionItemKind.Property, - } - const fieldAttributeUpdatedAt = { - label: '@updatedAt', - kind: CompletionItemKind.Property, - } - const fieldAttributeIgnore = { - label: '@ignore', - kind: CompletionItemKind.Property, - } - - const functionCuid = { - label: 'cuid()', - kind: CompletionItemKind.Function, - } - const functionUuid = { - label: 'uuid()', - kind: CompletionItemKind.Function, - } - const functionAutoInc = { - label: 'autoincrement()', - kind: CompletionItemKind.Function, - } - const functionNow = { - label: 'now()', - kind: CompletionItemKind.Function, - } - const functionDbGenerated = { - label: 'dbgenerated("")', - kind: CompletionItemKind.Function, - } - const staticValueTrue = { - label: 'true', - kind: CompletionItemKind.Value, - } - const staticValueFalse = { - label: 'false', - kind: CompletionItemKind.Value, - } - const enumValueOne = { - label: 'ADMIN', - kind: CompletionItemKind.Value, - } - const enumValueTwo = { - label: 'NORMAL', - kind: CompletionItemKind.Value, - } - - const fieldsProperty = { - label: 'fields: []', - kind: CompletionItemKind.Property, - } + suite('FIELD ATTRIBUTES', () => { + const fieldAttributeId = { + label: '@id', + kind: CompletionItemKind.Property, + } + const fieldAttributeUnique = { + label: '@unique', + kind: CompletionItemKind.Property, + } + const fieldAttributeMap = { + label: '@map("")', + kind: CompletionItemKind.Property, + } + const fieldAttributeDefault = { + label: '@default()', + kind: CompletionItemKind.Property, + } + const fieldAttributeRelation = { + label: '@relation()', + kind: CompletionItemKind.Property, + } + const fieldAttributeUpdatedAt = { + label: '@updatedAt', + kind: CompletionItemKind.Property, + } + const fieldAttributeIgnore = { + label: '@ignore', + kind: CompletionItemKind.Property, + } - const referencesProperty = { - label: 'references: []', - kind: CompletionItemKind.Property, - } - const onDeleteProperty = { - label: 'onDelete: ', - kind: CompletionItemKind.Property, - } - const onUpdateProperty = { - label: 'onUpdate: ', - kind: CompletionItemKind.Property, - } + const functionCuid = { + label: 'cuid()', + kind: CompletionItemKind.Function, + } + const functionUuid = { + label: 'uuid()', + kind: CompletionItemKind.Function, + } + const functionAutoInc = { + label: 'autoincrement()', + kind: CompletionItemKind.Function, + } + const functionNow = { + label: 'now()', + kind: CompletionItemKind.Function, + } + const functionDbGenerated = { + label: 'dbgenerated("")', + kind: CompletionItemKind.Function, + } + const staticValueTrue = { + label: 'true', + kind: CompletionItemKind.Value, + } + const staticValueFalse = { + label: 'false', + kind: CompletionItemKind.Value, + } + const enumValueOne = { + label: 'ADMIN', + kind: CompletionItemKind.Value, + } + const enumValueTwo = { + label: 'NORMAL', + kind: CompletionItemKind.Value, + } - const nameProperty = { - label: '""', - kind: CompletionItemKind.Property, - } + const fieldsProperty = { + label: 'fields: []', + kind: CompletionItemKind.Property, + } + const referencesProperty = { + label: 'references: []', + kind: CompletionItemKind.Property, + } + const onDeleteProperty = { + label: 'onDelete: ', + kind: CompletionItemKind.Property, + } + const onUpdateProperty = { + label: 'onUpdate: ', + kind: CompletionItemKind.Property, + } + const nameQuotesProperty = { + label: '""', + kind: CompletionItemKind.Property, + } + const nameProperty = { + label: 'name: ', + kind: CompletionItemKind.Property, + } - test('Diagnoses field and block attribute suggestions', () => { - assertCompletion( - modelBlocksUri, - { line: 18, character: 14 }, - { - isIncomplete: false, - items: [ - fieldAttributeId, - fieldAttributeUnique, - fieldAttributeMap, - fieldAttributeDefault, - fieldAttributeRelation, - fieldAttributeIgnore, - ], - }, - ) - assertCompletion( - modelBlocksUri, - { line: 19, character: 14 }, - { - isIncomplete: false, - items: [ - fieldAttributeUnique, - fieldAttributeMap, - fieldAttributeDefault, - fieldAttributeRelation, - fieldAttributeIgnore, - ], - }, - ) - assertCompletion( - modelBlocksUri, - { line: 61, character: 20 }, - { - isIncomplete: false, - items: [ - fieldAttributeUnique, - fieldAttributeMap, - fieldAttributeDefault, - fieldAttributeRelation, - fieldAttributeUpdatedAt, - fieldAttributeIgnore, - ], - }, - ) - assertCompletion( - modelBlocksUri, - { line: 13, character: 16 }, - { - isIncomplete: false, - items: [ - fieldAttributeUnique, - fieldAttributeMap, - fieldAttributeDefault, - fieldAttributeRelation, - fieldAttributeIgnore, - ], - }, - ) - - assertCompletion( - modelBlocksUri, - { line: 74, character: 24 }, - { - isIncomplete: false, - items: [{ label: 'lastName', kind: CompletionItemKind.Field }], - }, - ) - }) + test('Diagnoses field and block attribute suggestions', () => { + assertCompletion( + modelBlocksUri, + { line: 18, character: 14 }, + { + isIncomplete: false, + items: [ + fieldAttributeId, + fieldAttributeUnique, + fieldAttributeMap, + fieldAttributeDefault, + fieldAttributeRelation, + fieldAttributeIgnore, + ], + }, + ) + assertCompletion( + modelBlocksUri, + { line: 19, character: 14 }, + { + isIncomplete: false, + items: [ + fieldAttributeUnique, + fieldAttributeMap, + fieldAttributeDefault, + fieldAttributeRelation, + fieldAttributeIgnore, + ], + }, + ) + assertCompletion( + modelBlocksUri, + { line: 61, character: 20 }, + { + isIncomplete: false, + items: [ + fieldAttributeUnique, + fieldAttributeMap, + fieldAttributeDefault, + fieldAttributeRelation, + fieldAttributeUpdatedAt, + fieldAttributeIgnore, + ], + }, + ) + assertCompletion( + modelBlocksUri, + { line: 13, character: 16 }, + { + isIncomplete: false, + items: [ + fieldAttributeUnique, + fieldAttributeMap, + fieldAttributeDefault, + fieldAttributeRelation, + fieldAttributeIgnore, + ], + }, + ) - test('Diagnoses functions as default values', () => { - assertCompletion( - modelBlocksUri, - { line: 11, character: 24 }, - { - isIncomplete: false, - items: [functionDbGenerated, functionAutoInc], - }, - ) - assertCompletion( - modelBlocksUri, - { line: 28, character: 27 }, - { - isIncomplete: false, - items: [functionDbGenerated, functionUuid, functionCuid], - }, - ) - assertCompletion( - modelBlocksUri, - { line: 30, character: 36 }, - { - isIncomplete: false, - items: [functionDbGenerated, functionNow], - }, - ) - }) + assertCompletion( + modelBlocksUri, + { line: 74, character: 24 }, + { + isIncomplete: false, + items: [{ label: 'lastName', kind: CompletionItemKind.Field }], + }, + ) + }) - test('Diagnoses static default values', () => { - assertCompletion( - modelBlocksUri, - { line: 24, character: 28 }, - { - isIncomplete: false, - items: [functionDbGenerated, staticValueTrue, staticValueFalse], - }, - ) - }) + test('Diagnoses functions as default values', () => { + assertCompletion( + modelBlocksUri, + { line: 11, character: 24 }, + { + isIncomplete: false, + items: [functionDbGenerated, functionAutoInc], + }, + ) + assertCompletion( + modelBlocksUri, + { line: 28, character: 27 }, + { + isIncomplete: false, + items: [functionDbGenerated, functionUuid, functionCuid], + }, + ) + assertCompletion( + modelBlocksUri, + { line: 30, character: 36 }, + { + isIncomplete: false, + items: [functionDbGenerated, functionNow], + }, + ) + }) - test('Diagnoses default suggestions for enum values', () => { - assertCompletion( - modelBlocksUri, - { line: 62, character: 27 }, - { - isIncomplete: false, - items: [functionDbGenerated, enumValueOne, enumValueTwo], - }, - ) - }) + test('Diagnoses static default values', () => { + assertCompletion( + modelBlocksUri, + { line: 24, character: 28 }, + { + isIncomplete: false, + items: [functionDbGenerated, staticValueTrue, staticValueFalse], + }, + ) + }) - test('Diagnoses default suggestions for enum values excluding comments', () => { - assertCompletion( - enumCommentUri, - { line: 11, character: 30 }, - { - isIncomplete: false, - items: [functionDbGenerated, enumValueOne, enumValueTwo], - }, - ) - }) + test('Diagnoses default suggestions for enum values', () => { + assertCompletion( + modelBlocksUri, + { line: 62, character: 27 }, + { + isIncomplete: false, + items: [functionDbGenerated, enumValueOne, enumValueTwo], + }, + ) + }) - test('Diagnoses arguments of @@unique', () => { - assertCompletion( - modelBlocksUri, - { line: 38, character: 15 }, - { - isIncomplete: false, - items: [ - { label: 'firstName', kind: CompletionItemKind.Field }, - { label: 'lastName', kind: CompletionItemKind.Field }, - { label: 'isAdmin', kind: CompletionItemKind.Field }, - ], - }, - ) - }) + test('Diagnoses default suggestions for enum values excluding comments', () => { + assertCompletion( + enumCommentUri, + { line: 11, character: 30 }, + { + isIncomplete: false, + items: [functionDbGenerated, enumValueOne, enumValueTwo], + }, + ) + }) - test('Diagnoses arguments of @@id', () => { - assertCompletion( - modelBlocksUri, - { line: 46, character: 10 }, - { - isIncomplete: false, - items: [ - { label: 'firstName', kind: CompletionItemKind.Field }, - { label: 'lastName', kind: CompletionItemKind.Field }, - { label: 'isAdmin', kind: CompletionItemKind.Field }, - ], - }, - ) - }) + test('Diagnoses arguments of @@unique', () => { + assertCompletion( + modelBlocksUri, + { line: 38, character: 15 }, + { + isIncomplete: false, + items: [ + { label: 'firstName', kind: CompletionItemKind.Field }, + { label: 'lastName', kind: CompletionItemKind.Field }, + { label: 'isAdmin', kind: CompletionItemKind.Field }, + ], + }, + ) + }) - test('Diagnoses arguments of @@index', () => { - assertCompletion( - modelBlocksUri, - { line: 47, character: 13 }, - { - isIncomplete: false, - items: [ - { label: 'firstName', kind: CompletionItemKind.Field }, - { label: 'lastName', kind: CompletionItemKind.Field }, - { label: 'isAdmin', kind: CompletionItemKind.Field }, - ], - }, - ) - }) + test('Diagnoses arguments of @@id', () => { + assertCompletion( + modelBlocksUri, + { line: 46, character: 10 }, + { + isIncomplete: false, + items: [ + { label: 'firstName', kind: CompletionItemKind.Field }, + { label: 'lastName', kind: CompletionItemKind.Field }, + { label: 'isAdmin', kind: CompletionItemKind.Field }, + ], + }, + ) + }) - const relationDirectiveUri = 'completions/relationDirective.prisma' - test('Diagnoses arguments of @relation directive', () => { - assertCompletion( - relationDirectiveUri, - { line: 12, character: 26 }, - { - isIncomplete: false, - items: [ - referencesProperty, - fieldsProperty, - onDeleteProperty, - onUpdateProperty, - nameProperty, - ], - }, - ) - assertCompletion( - relationDirectiveUri, - { line: 21, character: 39 }, - { - isIncomplete: false, - items: [ - { label: 'id', kind: CompletionItemKind.Field }, - { label: 'items', kind: CompletionItemKind.Field }, - { label: 'total', kind: CompletionItemKind.Field }, - ], - }, - ) - assertCompletion( - relationDirectiveUri, - { line: 30, character: 44 }, - { - isIncomplete: false, - items: [ - fieldsProperty, - onDeleteProperty, - onUpdateProperty, - nameProperty, - ], - }, - ) - assertCompletion( - relationDirectiveUri, - { line: 39, character: 45 }, - { - isIncomplete: false, - items: [ - referencesProperty, - onDeleteProperty, - onUpdateProperty, - nameProperty, - ], - }, - ) - assertCompletion( - relationDirectiveUri, - { line: 48, character: 35 }, - { - isIncomplete: false, - items: [ - { label: 'id', kind: CompletionItemKind.Field }, - { label: 'productName', kind: CompletionItemKind.Field }, - { label: 'productPrice', kind: CompletionItemKind.Field }, - { label: 'quantity', kind: CompletionItemKind.Field }, - { label: 'orderId', kind: CompletionItemKind.Field }, - ], - }, - ) + test('Diagnoses arguments of @@index', () => { + assertCompletion( + modelBlocksUri, + { line: 47, character: 13 }, + { + isIncomplete: false, + items: [ + { label: 'firstName', kind: CompletionItemKind.Field }, + { label: 'lastName', kind: CompletionItemKind.Field }, + { label: 'isAdmin', kind: CompletionItemKind.Field }, + ], + }, + ) + }) + + suite('Diagnoses arguments of @relation directive', function () { + test('@relation(|)', () => { + assertCompletion( + relationDirectiveUri, + { line: 12, character: 26 }, + { + isIncomplete: false, + items: [ + referencesProperty, + fieldsProperty, + onDeleteProperty, + onUpdateProperty, + nameQuotesProperty, + nameProperty, + ], + }, + ) + }) + test('@relation(references: [|])', () => { + assertCompletion( + relationDirectiveUri, + { line: 21, character: 39 }, + { + isIncomplete: false, + items: [ + { label: 'id', kind: CompletionItemKind.Field }, + { label: 'items', kind: CompletionItemKind.Field }, + { label: 'total', kind: CompletionItemKind.Field }, + ], + }, + ) + }) + test('@relation(references: [id], |)', () => { + assertCompletion( + relationDirectiveUri, + { line: 30, character: 44 }, + { + isIncomplete: false, + items: [ + fieldsProperty, + onDeleteProperty, + onUpdateProperty, + nameQuotesProperty, + nameProperty, + ], + }, + ) + }) + test('order Order @relation(fields: [orderId], |)', () => { + assertCompletion( + relationDirectiveUri, + { line: 39, character: 45 }, + { + isIncomplete: false, + items: [ + referencesProperty, + onDeleteProperty, + onUpdateProperty, + nameQuotesProperty, + nameProperty, + ], + }, + ) + }) + test('@relation(fields: [|])', () => { + assertCompletion( + relationDirectiveUri, + { line: 48, character: 35 }, + { + isIncomplete: false, + items: [ + { label: 'id', kind: CompletionItemKind.Field }, + { label: 'productName', kind: CompletionItemKind.Field }, + { label: 'productPrice', kind: CompletionItemKind.Field }, + { label: 'quantity', kind: CompletionItemKind.Field }, + { label: 'orderId', kind: CompletionItemKind.Field }, + ], + }, + ) + }) + test('@relation(fields: [orderId], references: [id], |)', () => { + assertCompletion( + relationDirectiveUri, + { line: 57, character: 62 }, + { + isIncomplete: false, + items: [ + onDeleteProperty, + onUpdateProperty, + nameQuotesProperty, + nameProperty, + ], + }, + ) + }) + }) }) }) diff --git a/packages/language-server/test/fixtures/completions/relationDirective.prisma b/packages/language-server/test/fixtures/completions/relationDirective.prisma index 75d590ab0a..1a75f10568 100644 --- a/packages/language-server/test/fixtures/completions/relationDirective.prisma +++ b/packages/language-server/test/fixtures/completions/relationDirective.prisma @@ -47,4 +47,13 @@ model OrderItemFive { quantity Int orderId Int order Order @relation(fields: []) -} \ No newline at end of file +} + +model OrderItemSix { + id Int @id @default(autoincrement()) + productName String + productPrice Int + quantity Int + orderId Int + order Order @relation(fields: [orderId], references: [id], ) +} diff --git a/packages/vscode/src/test/completion.test.ts b/packages/vscode/src/test/completion.test.ts index 8c5488fd1f..72607ddd3d 100644 --- a/packages/vscode/src/test/completion.test.ts +++ b/packages/vscode/src/test/completion.test.ts @@ -39,569 +39,607 @@ async function testCompletion( ) } -suite('Should auto-complete', () => { - // Uri's - - const emptyDocUri = getDocUri('completions/empty.prisma') - const sqliteDocUri = getDocUri('completions/datasourceWithSqlite.prisma') - const dataSourceWithUri = getDocUri('completions/datasourceWithUrl.prisma') - const emptyBlocksUri = getDocUri('completions/emptyBlocks.prisma') - const modelBlocksUri = getDocUri('completions/modelBlocks.prisma') - const enumCommentUri = getDocUri('completions/enumWithComments.prisma') - - // ALL BLOCKS - - test('Diagnoses block type suggestions with sqlite as provider', async () => { - await testCompletion( - sqliteDocUri, - new vscode.Position(4, 0), - new vscode.CompletionList( - [ - { label: 'datasource', kind: vscode.CompletionItemKind.Class }, - { label: 'generator', kind: vscode.CompletionItemKind.Class }, - { label: 'model', kind: vscode.CompletionItemKind.Class }, - ], - false, - ), - false, - ) - }) +const dataSourceWithUri = getDocUri('completions/datasourceWithUrl.prisma') +const emptyBlocksUri = getDocUri('completions/emptyBlocks.prisma') +const modelBlocksUri = getDocUri('completions/modelBlocks.prisma') +const enumCommentUri = getDocUri('completions/enumWithComments.prisma') +const emptyDocUri = getDocUri('completions/empty.prisma') +const sqliteDocUri = getDocUri('completions/datasourceWithSqlite.prisma') +const relationDirectiveUri = getDocUri('completions/relationDirective.prisma') + +const fieldPreviewFeatures = { + label: 'previewFeatures', + kind: vscode.CompletionItemKind.Field, +} +const fieldProvider = { + label: 'provider', + kind: vscode.CompletionItemKind.Field, +} - test('Diagnoses block type suggestions for empty file', async () => { - await testCompletion( - emptyDocUri, - new vscode.Position(0, 0), - new vscode.CompletionList( - [ - { label: 'datasource', kind: vscode.CompletionItemKind.Class }, - { label: 'enum', kind: vscode.CompletionItemKind.Class }, - { label: 'generator', kind: vscode.CompletionItemKind.Class }, - { label: 'model', kind: vscode.CompletionItemKind.Class }, - ], +suite('Completions', () => { + suite('BASE BLOCKS', () => { + test('Diagnoses block type suggestions with sqlite as provider', async () => { + await testCompletion( + sqliteDocUri, + new vscode.Position(4, 0), + new vscode.CompletionList( + [ + { label: 'datasource', kind: vscode.CompletionItemKind.Class }, + { label: 'generator', kind: vscode.CompletionItemKind.Class }, + { label: 'model', kind: vscode.CompletionItemKind.Class }, + ], + false, + ), false, - ), - false, - ) - }) - - // DATASOURCE BLOCK - - const fieldPreviewFeatures = { - label: 'previewFeatures', - kind: vscode.CompletionItemKind.Field, - } - const fieldProvider = { - label: 'provider', - kind: vscode.CompletionItemKind.Field, - } - const fieldUrl = { label: 'url', kind: vscode.CompletionItemKind.Field } - const fieldShadowDatabaseUrl = { - label: 'shadowDatabaseUrl', - kind: vscode.CompletionItemKind.Field, - } - const sqlite = { label: 'sqlite', kind: vscode.CompletionItemKind.Constant } - const mysql = { label: 'mysql', kind: vscode.CompletionItemKind.Constant } - const postgresql = { - label: 'postgresql', - kind: vscode.CompletionItemKind.Constant, - } - const mssql = { label: 'sqlserver', kind: vscode.CompletionItemKind.Constant } - const array = { label: '[]', kind: vscode.CompletionItemKind.Property } - const quotationMarks = { - label: '""', - kind: vscode.CompletionItemKind.Property, - } - const envArgument = { - label: 'DATABASE_URL', - kind: vscode.CompletionItemKind.Constant, - } - const env = { label: 'env()', kind: vscode.CompletionItemKind.Property } - - test('Diagnoses datasource field suggestions in empty block', async () => { - await testCompletion( - emptyBlocksUri, - new vscode.Position(1, 0), - new vscode.CompletionList([ - fieldProvider, - fieldUrl, - fieldShadowDatabaseUrl, - ]), - false, - ) - }) + ) + }) - test('Diagnoses datasource field suggestions with existing field', async () => { - await testCompletion( - sqliteDocUri, - new vscode.Position(2, 0), - new vscode.CompletionList([fieldUrl, fieldShadowDatabaseUrl]), - false, - ) - await testCompletion( - dataSourceWithUri, - new vscode.Position(2, 0), - new vscode.CompletionList([fieldProvider, fieldShadowDatabaseUrl]), - false, - ) + test('Diagnoses block type suggestions for empty file', async () => { + await testCompletion( + emptyDocUri, + new vscode.Position(0, 0), + new vscode.CompletionList( + [ + { label: 'datasource', kind: vscode.CompletionItemKind.Class }, + { label: 'enum', kind: vscode.CompletionItemKind.Class }, + { label: 'generator', kind: vscode.CompletionItemKind.Class }, + { label: 'model', kind: vscode.CompletionItemKind.Class }, + ], + false, + ), + false, + ) + }) }) - test('Diagnoses url argument suggestions for datasource block', async () => { - await testCompletion( - dataSourceWithUri, - new vscode.Position(7, 10), - new vscode.CompletionList([quotationMarks, env], true), - false, - ), + suite('DATABASE BLOCK', () => { + const fieldUrl = { label: 'url', kind: vscode.CompletionItemKind.Field } + const fieldShadowDatabaseUrl = { + label: 'shadowDatabaseUrl', + kind: vscode.CompletionItemKind.Field, + } + const sqlite = { label: 'sqlite', kind: vscode.CompletionItemKind.Constant } + const mysql = { label: 'mysql', kind: vscode.CompletionItemKind.Constant } + const postgresql = { + label: 'postgresql', + kind: vscode.CompletionItemKind.Constant, + } + const mssql = { + label: 'sqlserver', + kind: vscode.CompletionItemKind.Constant, + } + const array = { label: '[]', kind: vscode.CompletionItemKind.Property } + const quotationMarks = { + label: '""', + kind: vscode.CompletionItemKind.Property, + } + const envArgument = { + label: 'DATABASE_URL', + kind: vscode.CompletionItemKind.Constant, + } + const env = { label: 'env()', kind: vscode.CompletionItemKind.Property } + + test('Diagnoses datasource field suggestions in empty block', async () => { await testCompletion( - dataSourceWithUri, - new vscode.Position(11, 15), - new vscode.CompletionList([envArgument], false), - true, + emptyBlocksUri, + new vscode.Position(1, 0), + new vscode.CompletionList([ + fieldProvider, + fieldUrl, + fieldShadowDatabaseUrl, + ]), + false, ) - }) + }) - test('Diagnoses single provider suggestions for datasource block', async () => { - await testCompletion( - sqliteDocUri, - new vscode.Position(14, 14), - new vscode.CompletionList([mysql, postgresql, sqlite, mssql], true), - false, - ), + test('Diagnoses datasource field suggestions with existing field', async () => { await testCompletion( sqliteDocUri, - new vscode.Position(18, 13), - new vscode.CompletionList([quotationMarks, array], true), + new vscode.Position(2, 0), + new vscode.CompletionList([fieldUrl, fieldShadowDatabaseUrl]), false, ) - }) + await testCompletion( + dataSourceWithUri, + new vscode.Position(2, 0), + new vscode.CompletionList([fieldProvider, fieldShadowDatabaseUrl]), + false, + ) + }) - test('Diagnoses multiple provider suggestions for datasource block', async () => { - await testCompletion( - sqliteDocUri, - new vscode.Position(6, 15), - new vscode.CompletionList([mysql, postgresql, sqlite, mssql], true), - true, - ), + test('Diagnoses url argument suggestions for datasource block', async () => { + await testCompletion( + dataSourceWithUri, + new vscode.Position(7, 10), + new vscode.CompletionList([quotationMarks, env], true), + false, + ), + await testCompletion( + dataSourceWithUri, + new vscode.Position(11, 15), + new vscode.CompletionList([envArgument], false), + true, + ) + }) + + test('Diagnoses single provider suggestions for datasource block', async () => { + await testCompletion( + sqliteDocUri, + new vscode.Position(14, 14), + new vscode.CompletionList([mysql, postgresql, sqlite, mssql], true), + false, + ), + await testCompletion( + sqliteDocUri, + new vscode.Position(18, 13), + new vscode.CompletionList([quotationMarks, array], true), + false, + ) + }) + + test('Diagnoses multiple provider suggestions for datasource block', async () => { await testCompletion( sqliteDocUri, - new vscode.Position(22, 14), - new vscode.CompletionList([quotationMarks], true), + new vscode.Position(6, 15), + new vscode.CompletionList([mysql, postgresql, sqlite, mssql], true), + true, + ), + await testCompletion( + sqliteDocUri, + new vscode.Position(22, 14), + new vscode.CompletionList([quotationMarks], true), + true, + ) + await testCompletion( + sqliteDocUri, + new vscode.Position(10, 25), + new vscode.CompletionList([mysql, postgresql, mssql], true), true, ) - await testCompletion( - sqliteDocUri, - new vscode.Position(10, 25), - new vscode.CompletionList([mysql, postgresql, mssql], true), - true, - ) + }) }) - // GENERATOR BLOCK - - const fieldOutput = { label: 'output', kind: vscode.CompletionItemKind.Field } - const fieldBinaryTargets = { - label: 'binaryTargets', - kind: vscode.CompletionItemKind.Field, - } - - const generatorWithExistingFieldsUri = getDocUri( - 'completions/generatorWithExistingFields.prisma', - ) - test('Diagnoses generator field suggestions in empty block', async () => { - await testCompletion( - emptyBlocksUri, - new vscode.Position(5, 0), - new vscode.CompletionList([ - fieldBinaryTargets, - fieldOutput, - fieldPreviewFeatures, - fieldProvider, - ]), - false, + suite('GENERATOR BLOCK', () => { + const fieldOutput = { + label: 'output', + kind: vscode.CompletionItemKind.Field, + } + const fieldBinaryTargets = { + label: 'binaryTargets', + kind: vscode.CompletionItemKind.Field, + } + + const generatorWithExistingFieldsUri = getDocUri( + 'completions/generatorWithExistingFields.prisma', ) - }) + test('Diagnoses generator field suggestions in empty block', async () => { + await testCompletion( + emptyBlocksUri, + new vscode.Position(5, 0), + new vscode.CompletionList([ + fieldBinaryTargets, + fieldOutput, + fieldPreviewFeatures, + fieldProvider, + ]), + false, + ) + }) - test('Diagnoses generator field suggestions with existing fields', async () => { - await activate(generatorWithExistingFieldsUri) - await testCompletion( - generatorWithExistingFieldsUri, - new vscode.Position(2, 0), - new vscode.CompletionList([ - fieldBinaryTargets, - fieldOutput, - fieldPreviewFeatures, - ]), - true, - ) - await testCompletion( - generatorWithExistingFieldsUri, - new vscode.Position(7, 0), - new vscode.CompletionList([ - fieldBinaryTargets, - fieldPreviewFeatures, - fieldProvider, - ]), - true, - ) + test('Diagnoses generator field suggestions with existing fields', async () => { + await activate(generatorWithExistingFieldsUri) + await testCompletion( + generatorWithExistingFieldsUri, + new vscode.Position(2, 0), + new vscode.CompletionList([ + fieldBinaryTargets, + fieldOutput, + fieldPreviewFeatures, + ]), + true, + ) + await testCompletion( + generatorWithExistingFieldsUri, + new vscode.Position(7, 0), + new vscode.CompletionList([ + fieldBinaryTargets, + fieldPreviewFeatures, + fieldProvider, + ]), + true, + ) + }) }) - // BLOCK ATTRIBUTES - - const blockAttributeId = { - label: '@@id([])', - kind: vscode.CompletionItemKind.Property, - } - const blockAttributeMap = { - label: '@@map("")', - kind: vscode.CompletionItemKind.Property, - } - const blockAttributeUnique = { - label: '@@unique([])', - kind: vscode.CompletionItemKind.Property, - } - const blockAttributeIndex = { - label: '@@index([])', - kind: vscode.CompletionItemKind.Property, - } - const blockAttributeIgnore = { - label: '@@ignore', - kind: vscode.CompletionItemKind.Property, - } - - test('Diagnoses block attribute suggestions first in a line', async () => { - await testCompletion( - emptyBlocksUri, - new vscode.Position(9, 0), - new vscode.CompletionList([ - blockAttributeId, - blockAttributeIndex, - blockAttributeMap, - blockAttributeUnique, - blockAttributeIgnore, - ]), - false, - ) - }) + suite('BLOCK ATTRIBUTES', () => { + const blockAttributeId = { + label: '@@id([])', + kind: vscode.CompletionItemKind.Property, + } + const blockAttributeMap = { + label: '@@map("")', + kind: vscode.CompletionItemKind.Property, + } + const blockAttributeUnique = { + label: '@@unique([])', + kind: vscode.CompletionItemKind.Property, + } + const blockAttributeIndex = { + label: '@@index([])', + kind: vscode.CompletionItemKind.Property, + } + const blockAttributeIgnore = { + label: '@@ignore', + kind: vscode.CompletionItemKind.Property, + } + + test('Diagnoses block attribute suggestions first in a line', async () => { + await testCompletion( + emptyBlocksUri, + new vscode.Position(9, 0), + new vscode.CompletionList([ + blockAttributeId, + blockAttributeIndex, + blockAttributeMap, + blockAttributeUnique, + blockAttributeIgnore, + ]), + false, + ) + }) - test('Diagnoses block attribute suggestions with existing attributes first in a line', async () => { - await activate(modelBlocksUri) - await testCompletion( - modelBlocksUri, - new vscode.Position(5, 0), - new vscode.CompletionList([ - blockAttributeId, - blockAttributeIndex, - blockAttributeMap, - blockAttributeUnique, - blockAttributeIgnore, - ]), - true, - ) - await testCompletion( - modelBlocksUri, - new vscode.Position(14, 0), - new vscode.CompletionList([ - blockAttributeIndex, - blockAttributeMap, - blockAttributeUnique, - blockAttributeIgnore, - ]), - true, - ) + test('Diagnoses block attribute suggestions with existing attributes first in a line', async () => { + await activate(modelBlocksUri) + await testCompletion( + modelBlocksUri, + new vscode.Position(5, 0), + new vscode.CompletionList([ + blockAttributeId, + blockAttributeIndex, + blockAttributeMap, + blockAttributeUnique, + blockAttributeIgnore, + ]), + true, + ) + await testCompletion( + modelBlocksUri, + new vscode.Position(14, 0), + new vscode.CompletionList([ + blockAttributeIndex, + blockAttributeMap, + blockAttributeUnique, + blockAttributeIgnore, + ]), + true, + ) + }) }) - // TYPES - - test('Diagnoses type suggestions in model block', async () => { - await testCompletion( - modelBlocksUri, - new vscode.Position(51, 7), - new vscode.CompletionList( - [ - { label: 'Boolean', kind: vscode.CompletionItemKind.TypeParameter }, - { label: 'Cat', kind: vscode.CompletionItemKind.Reference }, - { label: 'DateTime', kind: vscode.CompletionItemKind.TypeParameter }, - { label: 'Float', kind: vscode.CompletionItemKind.TypeParameter }, - { label: 'Hello', kind: vscode.CompletionItemKind.Reference }, - { label: 'Int', kind: vscode.CompletionItemKind.TypeParameter }, - { label: 'Json', kind: vscode.CompletionItemKind.TypeParameter }, - { label: 'Person', kind: vscode.CompletionItemKind.Reference }, - { label: 'Post', kind: vscode.CompletionItemKind.Reference }, - { label: 'SecondUser', kind: vscode.CompletionItemKind.Reference }, - { label: 'String', kind: vscode.CompletionItemKind.TypeParameter }, - { label: 'Bytes', kind: vscode.CompletionItemKind.TypeParameter }, - { label: 'Decimal', kind: vscode.CompletionItemKind.TypeParameter }, - { label: 'BigInt', kind: vscode.CompletionItemKind.TypeParameter }, - { - label: 'Unsupported("")', - kind: vscode.CompletionItemKind.TypeParameter, - }, - { label: 'Test', kind: vscode.CompletionItemKind.Reference }, - { label: 'ThirdUser', kind: vscode.CompletionItemKind.Reference }, - { label: 'TypeCheck', kind: vscode.CompletionItemKind.Reference }, - { label: 'User', kind: vscode.CompletionItemKind.Reference }, - ], + suite('TYPES', () => { + test('Diagnoses type suggestions in model block', async () => { + await testCompletion( + modelBlocksUri, + new vscode.Position(51, 7), + new vscode.CompletionList( + [ + { label: 'Boolean', kind: vscode.CompletionItemKind.TypeParameter }, + { label: 'Cat', kind: vscode.CompletionItemKind.Reference }, + { + label: 'DateTime', + kind: vscode.CompletionItemKind.TypeParameter, + }, + { label: 'Float', kind: vscode.CompletionItemKind.TypeParameter }, + { label: 'Hello', kind: vscode.CompletionItemKind.Reference }, + { label: 'Int', kind: vscode.CompletionItemKind.TypeParameter }, + { label: 'Json', kind: vscode.CompletionItemKind.TypeParameter }, + { label: 'Person', kind: vscode.CompletionItemKind.Reference }, + { label: 'Post', kind: vscode.CompletionItemKind.Reference }, + { label: 'SecondUser', kind: vscode.CompletionItemKind.Reference }, + { label: 'String', kind: vscode.CompletionItemKind.TypeParameter }, + { label: 'Bytes', kind: vscode.CompletionItemKind.TypeParameter }, + { label: 'Decimal', kind: vscode.CompletionItemKind.TypeParameter }, + { label: 'BigInt', kind: vscode.CompletionItemKind.TypeParameter }, + { + label: 'Unsupported("")', + kind: vscode.CompletionItemKind.TypeParameter, + }, + { label: 'Test', kind: vscode.CompletionItemKind.Reference }, + { label: 'ThirdUser', kind: vscode.CompletionItemKind.Reference }, + { label: 'TypeCheck', kind: vscode.CompletionItemKind.Reference }, + { label: 'User', kind: vscode.CompletionItemKind.Reference }, + ], + true, + ), true, - ), - true, - ) + ) + }) }) - // FIELD ATTRIBUTES + suite('FIELD ATTRIBUTES', () => { + const fieldAttributeId = { + label: '@id', + kind: vscode.CompletionItemKind.Property, + } + const fieldAttributeUnique = { + label: '@unique', + kind: vscode.CompletionItemKind.Property, + } + const fieldAttributeMap = { + label: '@map("")', + kind: vscode.CompletionItemKind.Property, + } + const fieldAttributeDefault = { + label: '@default()', + kind: vscode.CompletionItemKind.Property, + } + const fieldAttributeRelation = { + label: '@relation()', + kind: vscode.CompletionItemKind.Property, + } + const fieldAttributeIgnore = { + label: '@ignore', + kind: vscode.CompletionItemKind.Property, + } + const functionCuid = { + label: 'cuid()', + kind: vscode.CompletionItemKind.Function, + } + const functionUuid = { + label: 'uuid()', + kind: vscode.CompletionItemKind.Function, + } + const functionAutoInc = { + label: 'autoincrement()', + kind: vscode.CompletionItemKind.Function, + } + const functionNow = { + label: 'now()', + kind: vscode.CompletionItemKind.Function, + } + const functionDbGenerated = { + label: 'dbgenerated("")', + kind: vscode.CompletionItemKind.Function, + } + const staticValueTrue = { + label: 'true', + kind: vscode.CompletionItemKind.Value, + } + const staticValueFalse = { + label: 'false', + kind: vscode.CompletionItemKind.Value, + } + const fieldsProperty = { + label: 'fields: []', + kind: vscode.CompletionItemKind.Property, + } + const referencesProperty = { + label: 'references: []', + kind: vscode.CompletionItemKind.Property, + } + const onDeleteProperty = { + label: 'onDelete: ', + kind: vscode.CompletionItemKind.Property, + } + const onUpdateProperty = { + label: 'onUpdate: ', + kind: vscode.CompletionItemKind.Property, + } + const nameQuotesProperty = { + label: '""', + kind: vscode.CompletionItemKind.Property, + } + const nameProperty = { + label: 'name: ', + kind: vscode.CompletionItemKind.Property, + } + + test('Diagnoses field and block attribute suggestions', async () => { + await activate(modelBlocksUri) - const fieldAttributeId = { - label: '@id', - kind: vscode.CompletionItemKind.Property, - } - const fieldAttributeUnique = { - label: '@unique', - kind: vscode.CompletionItemKind.Property, - } - const fieldAttributeMap = { - label: '@map("")', - kind: vscode.CompletionItemKind.Property, - } - const fieldAttributeDefault = { - label: '@default()', - kind: vscode.CompletionItemKind.Property, - } - const fieldAttributeRelation = { - label: '@relation()', - kind: vscode.CompletionItemKind.Property, - } - const fieldAttributeIgnore = { - label: '@ignore', - kind: vscode.CompletionItemKind.Property, - } - - const functionCuid = { - label: 'cuid()', - kind: vscode.CompletionItemKind.Function, - } - const functionUuid = { - label: 'uuid()', - kind: vscode.CompletionItemKind.Function, - } - const functionAutoInc = { - label: 'autoincrement()', - kind: vscode.CompletionItemKind.Function, - } - const functionNow = { - label: 'now()', - kind: vscode.CompletionItemKind.Function, - } - const functionDbGenerated = { - label: 'dbgenerated("")', - kind: vscode.CompletionItemKind.Function, - } - const staticValueTrue = { - label: 'true', - kind: vscode.CompletionItemKind.Value, - } - const staticValueFalse = { - label: 'false', - kind: vscode.CompletionItemKind.Value, - } - - const fieldsProperty = { - label: 'fields: []', - kind: vscode.CompletionItemKind.Property, - } - const referencesProperty = { - label: 'references: []', - kind: vscode.CompletionItemKind.Property, - } - const onDeleteProperty = { - label: 'onDelete: ', - kind: vscode.CompletionItemKind.Property, - } - const onUpdateProperty = { - label: 'onUpdate: ', - kind: vscode.CompletionItemKind.Property, - } - - const nameProperty = { - label: '""', - kind: vscode.CompletionItemKind.Property, - } - - test('Diagnoses field and block attribute suggestions', async () => { - await testCompletion( - modelBlocksUri, - new vscode.Position(18, 14), - new vscode.CompletionList([ - fieldAttributeDefault, - fieldAttributeId, - fieldAttributeMap, - fieldAttributeRelation, - fieldAttributeUnique, - fieldAttributeIgnore, - ]), - true, - ) - await testCompletion( - modelBlocksUri, - new vscode.Position(19, 14), - new vscode.CompletionList([ - fieldAttributeDefault, - fieldAttributeMap, - fieldAttributeRelation, - fieldAttributeUnique, - fieldAttributeIgnore, - ]), - true, - ) - }) + await testCompletion( + modelBlocksUri, + new vscode.Position(18, 14), + new vscode.CompletionList([ + fieldAttributeDefault, + fieldAttributeId, + fieldAttributeMap, + fieldAttributeRelation, + fieldAttributeUnique, + fieldAttributeIgnore, + ]), + true, + ) + await testCompletion( + modelBlocksUri, + new vscode.Position(19, 14), + new vscode.CompletionList([ + fieldAttributeDefault, + fieldAttributeMap, + fieldAttributeRelation, + fieldAttributeUnique, + fieldAttributeIgnore, + ]), + true, + ) + }) - test('Diagnoses functions as default values', async () => { - await testCompletion( - modelBlocksUri, - new vscode.Position(11, 24), - new vscode.CompletionList([functionDbGenerated, functionAutoInc]), - true, - ) - await testCompletion( - modelBlocksUri, - new vscode.Position(28, 27), - new vscode.CompletionList([ - functionDbGenerated, - functionCuid, - functionUuid, - ]), - true, - ) - await testCompletion( - modelBlocksUri, - new vscode.Position(30, 36), - new vscode.CompletionList([functionDbGenerated, functionNow]), - true, - ) - }) + test('Diagnoses functions as default values', async () => { + await testCompletion( + modelBlocksUri, + new vscode.Position(11, 24), + new vscode.CompletionList([functionDbGenerated, functionAutoInc]), + true, + ) + await testCompletion( + modelBlocksUri, + new vscode.Position(28, 27), + new vscode.CompletionList([ + functionDbGenerated, + functionCuid, + functionUuid, + ]), + true, + ) + await testCompletion( + modelBlocksUri, + new vscode.Position(30, 36), + new vscode.CompletionList([functionDbGenerated, functionNow]), + true, + ) + }) - test('Diagnoses static default values', async () => { - await testCompletion( - modelBlocksUri, - new vscode.Position(24, 28), - new vscode.CompletionList([ - functionDbGenerated, - staticValueFalse, - staticValueTrue, - ]), - true, - ) - }) + test('Diagnoses static default values', async () => { + await testCompletion( + modelBlocksUri, + new vscode.Position(24, 28), + new vscode.CompletionList([ + functionDbGenerated, + staticValueFalse, + staticValueTrue, + ]), + true, + ) + }) - test('Diagnoses arguments of @@unique', async () => { - await testCompletion( - modelBlocksUri, - new vscode.Position(38, 15), - new vscode.CompletionList([ - { label: 'firstName', kind: vscode.CompletionItemKind.Field }, - { label: 'isAdmin', kind: vscode.CompletionItemKind.Field }, - { label: 'lastName', kind: vscode.CompletionItemKind.Field }, - ]), - true, - ) - }) + test('Diagnoses arguments of @@unique', async () => { + await testCompletion( + modelBlocksUri, + new vscode.Position(38, 15), + new vscode.CompletionList([ + { label: 'firstName', kind: vscode.CompletionItemKind.Field }, + { label: 'isAdmin', kind: vscode.CompletionItemKind.Field }, + { label: 'lastName', kind: vscode.CompletionItemKind.Field }, + ]), + true, + ) + }) - test('Diagnoses arguments of @@id', async () => { - await testCompletion( - modelBlocksUri, - new vscode.Position(46, 10), - new vscode.CompletionList([ - { label: 'firstName', kind: vscode.CompletionItemKind.Field }, - { label: 'isAdmin', kind: vscode.CompletionItemKind.Field }, - { label: 'lastName', kind: vscode.CompletionItemKind.Field }, - ]), - true, - ) - }) + test('Diagnoses arguments of @@id', async () => { + await testCompletion( + modelBlocksUri, + new vscode.Position(46, 10), + new vscode.CompletionList([ + { label: 'firstName', kind: vscode.CompletionItemKind.Field }, + { label: 'isAdmin', kind: vscode.CompletionItemKind.Field }, + { label: 'lastName', kind: vscode.CompletionItemKind.Field }, + ]), + true, + ) + }) - test('Diagnoses arguments of @@index', async () => { - await testCompletion( - modelBlocksUri, - new vscode.Position(47, 13), - new vscode.CompletionList([ - { label: 'firstName', kind: vscode.CompletionItemKind.Field }, - { label: 'isAdmin', kind: vscode.CompletionItemKind.Field }, - { label: 'lastName', kind: vscode.CompletionItemKind.Field }, - ]), - true, - ) - }) + test('Diagnoses arguments of @@index', async () => { + await testCompletion( + modelBlocksUri, + new vscode.Position(47, 13), + new vscode.CompletionList([ + { label: 'firstName', kind: vscode.CompletionItemKind.Field }, + { label: 'isAdmin', kind: vscode.CompletionItemKind.Field }, + { label: 'lastName', kind: vscode.CompletionItemKind.Field }, + ]), + true, + ) + }) - test('Diagnoses default suggestions for enum values excluding comments', async () => { - await testCompletion( - enumCommentUri, - new vscode.Position(11, 30), - new vscode.CompletionList([ - functionDbGenerated, - { label: 'ADMIN', kind: vscode.CompletionItemKind.Value }, - { label: 'NORMAL', kind: vscode.CompletionItemKind.Value }, - ]), - - false, - ) - }) + test('Diagnoses default suggestions for enum values excluding comments', async () => { + await testCompletion( + enumCommentUri, + new vscode.Position(11, 30), + new vscode.CompletionList([ + functionDbGenerated, + { label: 'ADMIN', kind: vscode.CompletionItemKind.Value }, + { label: 'NORMAL', kind: vscode.CompletionItemKind.Value }, + ]), - const relationDirectiveUri = getDocUri('completions/relationDirective.prisma') - test('Diagnoses arguments of @relation directive', async () => { - await activate(relationDirectiveUri) - await testCompletion( - relationDirectiveUri, - new vscode.Position(12, 26), - new vscode.CompletionList([ - nameProperty, - fieldsProperty, - referencesProperty, - onDeleteProperty, - onUpdateProperty, - ]), - true, - ) - await testCompletion( - relationDirectiveUri, - new vscode.Position(21, 39), - new vscode.CompletionList([ - { label: 'id', kind: vscode.CompletionItemKind.Field }, - { label: 'items', kind: vscode.CompletionItemKind.Field }, - { label: 'total', kind: vscode.CompletionItemKind.Field }, - ]), - true, - ) - await testCompletion( - relationDirectiveUri, - new vscode.Position(30, 44), - new vscode.CompletionList([ - nameProperty, - fieldsProperty, - onDeleteProperty, - onUpdateProperty, - ]), - true, - ) - await testCompletion( - relationDirectiveUri, - new vscode.Position(39, 45), - new vscode.CompletionList([ - nameProperty, - referencesProperty, - onDeleteProperty, - onUpdateProperty, - ]), - true, - ) - await testCompletion( - relationDirectiveUri, - new vscode.Position(48, 35), - new vscode.CompletionList([ - { label: 'id', kind: vscode.CompletionItemKind.Field }, - { label: 'orderId', kind: vscode.CompletionItemKind.Field }, - { label: 'productName', kind: vscode.CompletionItemKind.Field }, - { label: 'productPrice', kind: vscode.CompletionItemKind.Field }, - { label: 'quantity', kind: vscode.CompletionItemKind.Field }, - ]), - true, - ) + false, + ) + }) + + suite('Diagnoses arguments of @relation directive', function () { + test('@relation(|)', async () => { + await activate(relationDirectiveUri) + + await testCompletion( + relationDirectiveUri, + new vscode.Position(12, 26), + new vscode.CompletionList([ + nameQuotesProperty, + nameProperty, + fieldsProperty, + referencesProperty, + onDeleteProperty, + onUpdateProperty, + ]), + true, + ) + }) + test('@relation(references: [|])', async () => { + await testCompletion( + relationDirectiveUri, + new vscode.Position(21, 39), + new vscode.CompletionList([ + { label: 'id', kind: vscode.CompletionItemKind.Field }, + { label: 'items', kind: vscode.CompletionItemKind.Field }, + { label: 'total', kind: vscode.CompletionItemKind.Field }, + ]), + true, + ) + }) + test('@relation(references: [id], |)', async () => { + await testCompletion( + relationDirectiveUri, + new vscode.Position(30, 44), + new vscode.CompletionList([ + nameQuotesProperty, + nameProperty, + fieldsProperty, + onDeleteProperty, + onUpdateProperty, + ]), + true, + ) + }) + test('order Order @relation(fields: [orderId], |)', async () => { + await testCompletion( + relationDirectiveUri, + new vscode.Position(39, 45), + new vscode.CompletionList([ + nameQuotesProperty, + nameProperty, + referencesProperty, + onDeleteProperty, + onUpdateProperty, + ]), + true, + ) + }) + test('@relation(fields: [|])', async () => { + await testCompletion( + relationDirectiveUri, + new vscode.Position(48, 35), + new vscode.CompletionList([ + { label: 'id', kind: vscode.CompletionItemKind.Field }, + { label: 'orderId', kind: vscode.CompletionItemKind.Field }, + { label: 'productName', kind: vscode.CompletionItemKind.Field }, + { label: 'productPrice', kind: vscode.CompletionItemKind.Field }, + { label: 'quantity', kind: vscode.CompletionItemKind.Field }, + ]), + true, + ) + }) + test('@relation(fields: [orderId], references: [id], |)', async () => { + await testCompletion( + relationDirectiveUri, + new vscode.Position(57, 62), + new vscode.CompletionList([ + nameQuotesProperty, + nameProperty, + onDeleteProperty, + onUpdateProperty, + ]), + true, + ) + }) + }) }) }) diff --git a/packages/vscode/testFixture/completions/relationDirective.prisma b/packages/vscode/testFixture/completions/relationDirective.prisma index 75d590ab0a..1a75f10568 100644 --- a/packages/vscode/testFixture/completions/relationDirective.prisma +++ b/packages/vscode/testFixture/completions/relationDirective.prisma @@ -47,4 +47,13 @@ model OrderItemFive { quantity Int orderId Int order Order @relation(fields: []) -} \ No newline at end of file +} + +model OrderItemSix { + id Int @id @default(autoincrement()) + productName String + productPrice Int + quantity Int + orderId Int + order Order @relation(fields: [orderId], references: [id], ) +}