UNPKG

@prisma/language-server

Version:
324 lines (294 loc) 10.9 kB
import { CompletionItem, CompletionItemKind, InsertTextFormat, InsertTextMode } from 'vscode-languageserver' import { convertAttributesToCompletionItems, convertToCompletionItems } from './internals' import * as completions from './completions.json' import { PreviewFeatures } from '../types' export const sortLengthProperties: CompletionItem[] = convertToCompletionItems( // eslint-disable-next-line @typescript-eslint/no-non-null-assertion completions.fieldAttributes .find((item) => item.label === '@unique')! .params.filter((item) => item.label === 'length' || item.label === 'sort'), CompletionItemKind.Property, ) export const relationArguments: CompletionItem[] = convertAttributesToCompletionItems( completions.relationArguments, CompletionItemKind.Property, ) const sqlServerClusteredValuesCompletionItems: CompletionItem[] = [ { label: 'true', kind: CompletionItemKind.Value, insertTextFormat: InsertTextFormat.PlainText, documentation: { kind: 'markdown', value: 'CLUSTERED', }, }, { label: 'false', kind: CompletionItemKind.Value, insertTextFormat: InsertTextFormat.PlainText, documentation: { kind: 'markdown', value: 'NONCLUSTERED', }, }, ] /** * ```prisma * model A { * id Int @id * field Int @unique(sort: |) * otherField Int * * \@@unique(fields: [otherField(sort: |)]) * \@@index(fields: [id(sort: |)]) * } * ``` * And then specifically Sql Server, we also return: * ```prisma * model A { * id Int @id(sort: |) * * \@@id(fields: [id(sort: |)]) * } * ``` */ const sortValuesCompletionItems: CompletionItem[] = [ { label: 'Asc', kind: CompletionItemKind.Enum, insertTextFormat: InsertTextFormat.PlainText, documentation: { kind: 'markdown', value: 'Ascending', }, }, { label: 'Desc', kind: CompletionItemKind.Enum, insertTextFormat: InsertTextFormat.PlainText, documentation: { kind: 'markdown', value: 'Descending', }, }, ] const clusteredCompletion = (items: CompletionItem[]) => items.push({ label: 'clustered', insertText: 'clustered: $0', insertTextFormat: InsertTextFormat.Snippet, kind: CompletionItemKind.Property, documentation: 'An index, unique constraint or primary key can be created as clustered or non-clustered; altering the storage and retrieve behavior of the index.', }) const typeIndexCompletion = (items: CompletionItem[]) => items.push({ label: 'type', kind: CompletionItemKind.Property, insertText: 'type: $0', insertTextFormat: InsertTextFormat.Snippet, insertTextMode: InsertTextMode.adjustIndentation, documentation: { kind: 'markdown', value: 'Defines the access type of indexes: BTree (default) or Hash.', }, }) export const opsIndexFulltextCompletion = (items: CompletionItem[]) => items.push({ label: 'ops', insertText: 'ops: $0', insertTextFormat: InsertTextFormat.Snippet, kind: CompletionItemKind.Property, documentation: 'Specify the operator class for an indexed field.', }) //#region COCKROACHDB ONLY export const virtualSequenceDefaultCompletion = (items: CompletionItem[]) => items.push({ label: 'virtual', insertText: 'virtual', kind: CompletionItemKind.Property, documentation: 'Virtual sequences are sequences that do not generate monotonically increasing values and instead produce values like those generated by the built-in function unique_rowid(). They are intended for use in combination with SERIAL-typed columns.', }) export const minValueSequenceDefaultCompletion = (items: CompletionItem[]) => items.push({ label: 'minValue', insertText: 'minValue: $0', insertTextFormat: InsertTextFormat.Snippet, kind: CompletionItemKind.Property, documentation: 'The new minimum value of the sequence.', }) export const maxValueSequenceDefaultCompletion = (items: CompletionItem[]) => items.push({ label: 'maxValue', insertText: 'maxValue: $0', insertTextFormat: InsertTextFormat.Snippet, kind: CompletionItemKind.Property, documentation: 'The new maximum value of the sequence.', }) export const cacheSequenceDefaultCompletion = (items: CompletionItem[]) => items.push({ label: 'cache', insertText: 'cache: $0', insertTextFormat: InsertTextFormat.Snippet, kind: CompletionItemKind.Property, documentation: 'The number of sequence values to cache in memory for reuse in the session. A cache size of 1 means that there is no cache, and cache sizes of less than 1 are not valid.', }) export const incrementSequenceDefaultCompletion = (items: CompletionItem[]) => items.push({ label: 'increment', insertText: 'increment: $0', insertTextFormat: InsertTextFormat.Snippet, kind: CompletionItemKind.Property, documentation: 'The new value by which the sequence is incremented. A negative number creates a descending sequence. A positive number creates an ascending sequence.', }) export const startSequenceDefaultCompletion = (items: CompletionItem[]) => items.push({ label: 'start', insertText: 'start: $0', insertTextFormat: InsertTextFormat.Snippet, kind: CompletionItemKind.Property, documentation: 'The value the sequence starts at if you RESTART or if the sequence hits the MAXVALUE and CYCLE is set.', }) //#endregion export const scalarListDefaultCompletion = (items: CompletionItem[]) => items.unshift({ label: '[]', insertText: '[$0]', insertTextFormat: InsertTextFormat.Snippet, documentation: 'Set a default value on the list field', kind: CompletionItemKind.Value, }) export const booleanDefaultCompletions = (items: CompletionItem[]) => items.push({ label: 'true', kind: CompletionItemKind.Value }, { label: 'false', kind: CompletionItemKind.Value }) export function filterSortLengthBasedOnInput( attribute: '@@unique' | '@unique' | '@@id' | '@id' | '@@index', previewFeatures: PreviewFeatures[] | undefined, datasourceProvider: string | undefined, wordBeforePosition: string, items: CompletionItem[], ): CompletionItem[] { /* * 1 - Autocomplete values */ // Auto completion for sort: Desc | Asc // includes because `@unique(sort: |)` means wordBeforePosition = '@unique(sort:' if (wordBeforePosition.includes('sort:')) { return sortValuesCompletionItems } else { /* * 2 - Autocomplete properties */ // The length argument is available on MySQL only on the // @id, @@id, @unique, @@unique and @@index fields. // The sort argument is available for all databases on the // @unique, @@unique and @@index fields. // Additionally, SQL Server also allows it on @id and @@id. // Which translates too // - `length` argument for `@id`, `@@id`, `@unique`, `@@unique` and `@@index` (MySQL only) // - Note that on the `@@` the argument is on available a field - not on the top level attribute // - `sort` argument for `@unique`, `@@unique` and `@@index` (Additionally `@id` and `@@id` for SQL Server) if (datasourceProvider === 'mysql') { if (['@unique', '@@unique', '@@index'].includes(attribute)) { return items } else { // filter sort out return items.filter((arg) => arg.label !== 'sort') } } else if (datasourceProvider === 'sqlserver') { if (['@unique', '@@unique', '@@index', '@id', '@@id'].includes(attribute)) { // only filter length out return items.filter((arg) => arg.label !== 'length') } else { // filter length and sort out return items.filter((arg) => arg.label !== 'length' && arg.label !== 'sort') } } else { if (['@unique', '@@unique', '@@index'].includes(attribute)) { // only filter length out return items.filter((arg) => arg.label !== 'length') } else { // filter length and sort out return items.filter((arg) => arg.label !== 'length' && arg.label !== 'sort') } } } } export function getCompletionsForFieldAttributeArgs( fieldAttributeWithParams: '@unique' | '@id', previewFeatures: PreviewFeatures[] | undefined, datasourceProvider: string | undefined, wordBeforePosition: string, ): CompletionItem[] { const items = convertToCompletionItems( // eslint-disable-next-line @typescript-eslint/no-non-null-assertion completions.fieldAttributes.find((item) => item.label.includes(fieldAttributeWithParams))!.params, CompletionItemKind.Property, ) const completionItems = filterSortLengthBasedOnInput( fieldAttributeWithParams, previewFeatures, datasourceProvider, wordBeforePosition, items, ) if (datasourceProvider === 'sqlserver') { // Auto completion for SQL Server only, clustered: true | false if (wordBeforePosition.includes('clustered:')) { return sqlServerClusteredValuesCompletionItems } // add clustered propery to completion items completionItems.push({ label: 'clustered', insertText: 'clustered: $0', insertTextFormat: InsertTextFormat.Snippet, kind: CompletionItemKind.Property, documentation: 'An index, unique constraint or primary key can be created as clustered or non-clustered; altering the storage and retrieve behavior of the index.', }) } return completionItems } export function getCompletionsForBlockAttributeArgs({ blockAttributeWithParams, wordBeforePosition, datasourceProvider, // eslint-disable-next-line @typescript-eslint/no-unused-vars previewFeatures, }: { blockAttributeWithParams: '@@unique' | '@@id' | '@@index' | '@@fulltext' wordBeforePosition: string datasourceProvider: string | undefined previewFeatures: PreviewFeatures[] | undefined }): CompletionItem[] { const items = convertToCompletionItems( // eslint-disable-next-line @typescript-eslint/no-non-null-assertion completions.blockAttributes.find((item) => item.label.includes(blockAttributeWithParams))!.params, CompletionItemKind.Property, ) // SQL Server only, suggest clustered if (datasourceProvider === 'sqlserver' && blockAttributeWithParams !== '@@fulltext') { // Auto completion for SQL Server only, clustered: true | false if (wordBeforePosition.includes('clustered:')) { return sqlServerClusteredValuesCompletionItems } else { // add clustered to suggestions clusteredCompletion(items) } } // PostgreSQL only, suggest type else if ( blockAttributeWithParams === '@@index' && datasourceProvider && ['postgresql', 'postgres'].includes(datasourceProvider) ) { // TODO (Joël) figure out if we need to add cockroachdb provider here // The type argument is only available for PostgreSQL on @@index typeIndexCompletion(items) } return items }