UNPKG

@prisma/language-server

Version:
216 lines • 10 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.getSuggestionForBlockAttribute = getSuggestionForBlockAttribute; exports.getSuggestionForFieldAttribute = getSuggestionForFieldAttribute; const vscode_languageserver_1 = require("vscode-languageserver"); const klona_1 = require("klona"); const internals_1 = require("./internals"); const completions = __importStar(require("./completions.json")); const ast_1 = require("../ast"); const types_1 = require("./types"); const fieldAttributes = (0, internals_1.convertAttributesToCompletionItems)(completions.fieldAttributes, vscode_languageserver_1.CompletionItemKind.Property); const blockAttributes = (0, internals_1.convertAttributesToCompletionItems)(completions.blockAttributes, vscode_languageserver_1.CompletionItemKind.Property); const filterContextBlockAttributes = (schema, suggestions) => { // We can filter on the datasource const datasourceProvider = (0, ast_1.getFirstDatasourceProvider)(schema); // We can filter on the previewFeatures enabled const previewFeatures = (0, ast_1.getAllPreviewFeaturesFromGenerators)(schema); // Full text indexes (MySQL and MongoDB) // https://www.prisma.io/docs/concepts/components/prisma-schema/indexes#full-text-indexes-mysql-and-mongodb const isFullTextAvailable = Boolean(datasourceProvider && ['mysql', 'mongodb'].includes(datasourceProvider) && previewFeatures?.includes('fulltextindex')); const isMultiSchemaAvailable = Boolean(datasourceProvider && (datasourceProvider.includes('postgres') || datasourceProvider.includes('cockroachdb') || datasourceProvider.includes('sqlserver'))); const isShardKeyAvailable = Boolean(datasourceProvider && datasourceProvider.includes('mysql') && previewFeatures?.includes('shardkeys')); if (isFullTextAvailable === false) { // fullTextIndex is not available, we need to filter it out suggestions = suggestions.filter((arg) => arg.label !== '@@fulltext'); } if (!isMultiSchemaAvailable) { suggestions = suggestions.filter((item) => item.label !== '@@schema'); } if (!isShardKeyAvailable) { suggestions = suggestions.filter((item) => item.label !== '@@shardKey'); } return suggestions; }; /** * Removes all block attribute suggestions that are invalid in this context. * E.g. `@@id()` when already used should not be in the suggestions. */ function filterSuggestionsForBlock(suggestions, block, schema) { let reachedStartLine = false; for (const { lineIndex, text } of schema.iterLines()) { if (lineIndex === block.range.start.line + 1) { reachedStartLine = true; } if (!reachedStartLine) { continue; } if (lineIndex === block.range.end.line) { break; } // Ignore commented lines if (text.startsWith('//')) { continue; } // TODO we should also remove the other suggestions if used (default()...) // * Filter already-present attributes that can't be duplicated ; ['@id', '@@map', '@@ignore', '@@schema'].forEach((label) => { if (text.includes(label)) { suggestions = suggestions.filter((suggestion) => suggestion.label !== label); if (label === '@@ignore') { suggestions = suggestions.filter((suggestion) => suggestion.label !== '@ignore'); } if (label === '@id') { suggestions = suggestions.filter((suggestion) => suggestion.label !== '@@id'); } } }); } return suggestions; } const dataSourceNameCompletion = (items, datasourceName) => items.push({ // https://code.visualstudio.com/docs/editor/intellisense#_types-of-completions kind: vscode_languageserver_1.CompletionItemKind.Property, label: '@' + datasourceName, documentation: 'Defines a native database type that should be used for this field. See https://www.prisma.io/docs/concepts/components/prisma-schema/data-model#native-types-mapping', insertText: `@${datasourceName}$0`, insertTextFormat: vscode_languageserver_1.InsertTextFormat.Snippet, }); /** * Removes all line attribute suggestions that are invalid in this context. * E.g. `@map()` when already used should not be in the suggestions. */ function filterSuggestionsForLine(suggestions, currentLine, fieldType, fieldBlockType, schema) { const datasourceProvider = (0, ast_1.getFirstDatasourceProvider)(schema); const previewFeatures = (0, ast_1.getAllPreviewFeaturesFromGenerators)(schema); const isShardKeyAvailable = Boolean(datasourceProvider && datasourceProvider.includes('mysql') && previewFeatures?.includes('shardkeys')); if (!isShardKeyAvailable) { suggestions = suggestions.filter((item) => item.label !== '@shardKey'); } if (fieldBlockType === 'type') { // @default & @relation are invalid on field referencing a composite type // we filter them out suggestions = suggestions.filter((sugg) => sugg.label !== '@default' && sugg.label !== '@relation'); } // Tom: I think we allow ids on basically everything except relation fields // so it doesn't need to be restricted to Int and String. // These are terrible, terrible ideas of course, but you can have id DateTime @id or id Float @id. // TODO: decide if we want to only suggest things that make most sense or everything that is technically possible. const isAtIdAllowed = fieldType === 'Int' || fieldType === 'String' || fieldBlockType === 'enum'; if (!isAtIdAllowed) { // id not allowed suggestions = suggestions.filter((suggestion) => suggestion.label !== '@id'); } const isUpdatedAtAllowed = fieldType === 'DateTime'; if (!isUpdatedAtAllowed) { // updatedAt not allowed suggestions = suggestions.filter((suggestion) => suggestion.label !== '@updatedAt'); } // * Filter already-present attributes that can't be duplicated fieldAttributes.forEach(({ label }) => { if (currentLine.includes(label)) { suggestions = suggestions.filter((suggestion) => suggestion.label !== label); } }); return suggestions; } /** * * Only models and views currently support block attributes */ function getSuggestionForBlockAttribute(block, schema) { if (!['model', 'view'].includes(block.type)) { return []; } const suggestions = filterSuggestionsForBlock((0, klona_1.klona)(blockAttributes), block, schema); return filterContextBlockAttributes(schema, suggestions); } /** * Should suggest all field attributes for a given field * EX: id Int |> @id, @default, @datasourceName, ...etc * * If `@datasourceName.` |> suggests nativeTypes * @param block * @param currentLine * @param lines * @param wordsBeforePosition * @param document * @returns */ function getSuggestionForFieldAttribute(block, currentLine, schema, wordsBeforePosition, onError) { const fieldType = (0, ast_1.getFieldType)(currentLine); // If we don't find a field type (e.g. String, Int...), return no suggestion if (!fieldType) { return; } let suggestions = []; // Because @.? if (wordsBeforePosition.length >= 2) { const datasourceName = (0, ast_1.getFirstDatasourceName)(schema); const prismaType = wordsBeforePosition[1]; const nativeTypeSuggestions = (0, types_1.getNativeTypes)(schema, prismaType, onError); if (datasourceName) { if (!currentLine.includes(`@${datasourceName}`)) { dataSourceNameCompletion(suggestions, datasourceName); } if (nativeTypeSuggestions.length !== 0) { if ( // Check that we are not separated by a space like `@db. |` wordsBeforePosition[wordsBeforePosition.length - 1] === `@${datasourceName}`) { suggestions.push(...nativeTypeSuggestions); return { items: suggestions, isIncomplete: false, }; } } } } suggestions.push(...fieldAttributes); const datamodelBlock = (0, ast_1.getDatamodelBlock)(fieldType, schema); suggestions = filterSuggestionsForLine(suggestions, currentLine, fieldType, datamodelBlock?.type, schema); suggestions = filterSuggestionsForBlock(suggestions, block, schema); return { items: suggestions, isIncomplete: false, }; } //# sourceMappingURL=attributes.js.map