UNPKG

@prisma/language-server

Version:
439 lines • 17.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.isDatamodelBlockName = void 0; exports.isRelationField = isRelationField; exports.isValidFieldName = isValidFieldName; exports.isEnumValue = isEnumValue; exports.printLogMessage = printLogMessage; exports.renameReferencesForFieldName = renameReferencesForFieldName; exports.renameReferencesForEnumValue = renameReferencesForEnumValue; exports.renameReferencesForModelName = renameReferencesForModelName; exports.insertBasicRename = insertBasicRename; exports.mapExistsAlready = mapExistsAlready; exports.insertMapAttribute = insertMapAttribute; exports.extractCurrentName = extractCurrentName; exports.appendEdit = appendEdit; exports.appendEdits = appendEdits; exports.mergeEditMaps = mergeEditMaps; const ast_1 = require("../ast"); const constants_1 = require("../constants"); function getType(currentLine) { const wordsInLine = currentLine.split(/\s+/); if (wordsInLine.length < 2) { return ''; } return wordsInLine[1].replace('?', '').replace('[]', ''); } function isRelationField(currentLine, schema) { const relationNames = (0, ast_1.getAllRelationNames)(schema, constants_1.relationNamesRegexFilter); const type = getType(currentLine); if (type == '') { return false; } return relationNames.includes(type); } function extractFirstWord(line) { return line.replace(/ .*/, ''); } function isValidFieldName(currentLine, position, block, document) { if (block.type === 'datasource' || block.type === 'generator' || block.type === 'enum' || position.line == block.range.start.line || position.line == block.range.end.line) { return false; } if (currentLine.startsWith('@')) { return false; } // check if position is inside first word const currentLineUntrimmed = (0, ast_1.getCurrentLine)(document, position.line); const firstWord = extractFirstWord(currentLine); const indexOfFirstWord = currentLineUntrimmed.indexOf(firstWord); const isFieldName = indexOfFirstWord <= position.character && indexOfFirstWord + firstWord.length >= position.character; if (!isFieldName) { return false; } // remove type modifiers const type = getType(currentLine); return type !== '' && type !== undefined; } const isDatamodelBlockName = (position, block, schema, document) => { // TODO figure out how to use DatamodelBlockType if (!['model', 'view', 'type', 'enum'].includes(block.type)) { return false; } if (position.line !== block.range.start.line) { return renameDatamodelBlockWhereUsedAsType(block, schema, document, position); } switch (block.type) { case 'model': return position.character > 5; case 'enum': case 'view': case 'type': return position.character > 4; default: return false; } }; exports.isDatamodelBlockName = isDatamodelBlockName; function renameDatamodelBlockWhereUsedAsType(block, schema, document, position) { // TODO type? if (block.type !== 'model') { return false; } const allRelationNames = (0, ast_1.getAllRelationNames)(schema, constants_1.relationNamesRegexFilter); const currentName = (0, ast_1.getWordAtPosition)(document, position); const isRelation = allRelationNames.includes(currentName); if (!isRelation) { return false; } const indexOfRelation = schema .linesAsArray() .findIndex((line) => line.text.startsWith(block.type) && line.text.includes(currentName)); return indexOfRelation !== -1; } function isEnumValue(line, position, block, document) { return (block.type === 'enum' && position.line !== block.range.start.line && !line.startsWith('@@') && !(0, ast_1.getWordAtPosition)(document, position).startsWith('@')); } function printLogMessage(currentName, newName, isBlockRename, isFieldRename, isEnumValueRename, blockType) { const message = `'${currentName}' was renamed to '${newName}'`; let typeOfRename = ''; if (isBlockRename) { typeOfRename = `${blockType} `; } else if (isFieldRename) { typeOfRename = 'Field '; } else if (isEnumValueRename) { typeOfRename = 'Enum value '; } console.log(typeOfRename + message); } function insertInlineRename(currentName, line) { const character = lastNewLineCharacter(line.untrimmedText); return { [line.document.uri]: [ { range: { start: { line: line.lineIndex, character, }, end: { line: line.lineIndex, character, }, }, newText: ` @map("${currentName}")`, }, ], }; } function lastNewLineCharacter(lineText) { const i = lineText.length - 1; if (lineText[i] === '\n' && lineText[i - 1] === '\r') { return i - 1; } else if (lineText[i] === '\n') { return i; } else { return 0; } } function insertMapBlockAttribute(oldName, block) { return { [block.definingDocument.uri]: [ { range: { start: { line: block.range.end.line, character: 0, }, end: block.range.end, }, newText: `\t@@map("${oldName}")\n}`, }, ], }; } function positionIsNotInsideSearchedBlocks(line, searchedBlocks) { if (searchedBlocks.length === 0) { return true; } return !searchedBlocks.some((block) => line >= block.range.start.line && line <= block.range.end.line); } /** * Renames references in any '@@index', '@@id' and '@@unique' attributes in the same model. * Renames references in any referenced fields inside a '@relation' attribute in the same model (fields: []). * Renames references inside a '@relation' attribute in other model blocks (references: []). */ function renameReferencesForFieldName(currentName, newName, schema, block, isRelationFieldRename) { const edits = {}; const searchStringsSameBlock = ['@@index', '@@id', '@@unique']; const relationAttribute = '@relation'; // search in same model first let reachedStartLine = false; for (const { document, lineIndex, text, untrimmedText } of schema.iterLines()) { if (lineIndex === block.range.start.line + 1) { reachedStartLine = true; } if (!reachedStartLine) { continue; } if (lineIndex === block.range.end.line) { break; } if (text.includes(relationAttribute) && text.includes(currentName) && !isRelationFieldRename) { // search for fields references const indexOfFieldsStart = untrimmedText.indexOf('fields:'); const indexOfFieldEnd = untrimmedText.slice(indexOfFieldsStart).indexOf(']') + indexOfFieldsStart; const fields = untrimmedText.slice(indexOfFieldsStart, indexOfFieldEnd + 1); const indexOfFoundValue = fields.indexOf(currentName); const fieldValues = (0, ast_1.getValuesInsideSquareBrackets)(fields); if (indexOfFoundValue !== -1 && fieldValues.includes(currentName)) { // found a referenced field appendEdit(edits, document.uri, { range: { start: { line: lineIndex, character: indexOfFieldsStart + indexOfFoundValue, }, end: { line: lineIndex, character: indexOfFieldsStart + indexOfFoundValue + currentName.length, }, }, newText: newName, }); } } // search for references in index, id and unique block attributes if (searchStringsSameBlock.some((s) => text.includes(s)) && text.includes(currentName)) { const valuesInsideBracket = (0, ast_1.getValuesInsideSquareBrackets)(untrimmedText); if (valuesInsideBracket.includes(currentName)) { const indexOfCurrentValue = untrimmedText.indexOf(currentName); appendEdit(edits, document.uri, { range: { start: { line: lineIndex, character: indexOfCurrentValue, }, end: { line: lineIndex, character: indexOfCurrentValue + currentName.length, }, }, newText: newName, }); } } } // search for references in other model blocks for (const { document, lineIndex, text, untrimmedText } of schema.iterLines()) { if (text.includes(block.name) && text.includes(currentName) && text.includes(relationAttribute)) { // get the index of the second word const indexOfReferences = untrimmedText.indexOf('references:'); const indexOfReferencesEnd = untrimmedText.slice(indexOfReferences).indexOf(']') + indexOfReferences; const references = untrimmedText.slice(indexOfReferences, indexOfReferencesEnd + 1); const indexOfFoundValue = references.indexOf(currentName); const referenceValues = (0, ast_1.getValuesInsideSquareBrackets)(references); if (indexOfFoundValue !== -1 && referenceValues.includes(currentName)) { appendEdit(edits, document.uri, { range: { start: { line: lineIndex, character: indexOfReferences + indexOfFoundValue, }, end: { line: lineIndex, character: indexOfReferences + indexOfFoundValue + currentName.length, }, }, newText: newName, }); } } } return edits; } /** * Renames references where the current enum value is used as a default value in other model blocks. */ function renameReferencesForEnumValue(currentValue, newName, schema, enumName) { const edits = {}; const searchString = `@default(${currentValue})`; for (const { document, lineIndex, text, untrimmedText } of schema.iterLines()) { if (text.includes(searchString) && text.includes(enumName)) { // get the index of the second word const indexOfCurrentName = untrimmedText.indexOf(searchString); appendEdit(edits, document.uri, { range: { start: { line: lineIndex, character: indexOfCurrentName, }, end: { line: lineIndex, character: indexOfCurrentName + searchString.length, }, }, newText: `@default(${newName})`, }); } } return edits; } /** * Renames references where the model name is used as a relation type in the same and other model blocks. */ function renameReferencesForModelName(currentName, newName, schema) { const searchedBlocks = []; const edits = {}; for (const { document, lineIndex, text } of schema.iterLines()) { // check if inside model if (text.includes(currentName) && positionIsNotInsideSearchedBlocks(lineIndex, searchedBlocks)) { const block = (0, ast_1.getBlockAtPosition)(document.uri, lineIndex, schema); // TODO type here if (block && block.type == 'model') { searchedBlocks.push(block); // search for field types in current block const fieldTypes = (0, ast_1.getFieldTypesFromCurrentBlock)(schema, block); for (const fieldType of fieldTypes.fieldTypes.keys()) { if (fieldType.replace('?', '').replace('[]', '') === currentName) { // replace here const foundFieldTypes = fieldTypes.fieldTypes.get(fieldType); if (!foundFieldTypes?.locations) { return edits; } for (const { lineIndex, document: foundDocument } of foundFieldTypes.locations) { const currentLineUntrimmed = foundDocument.lines[lineIndex].untrimmedText; const wordsInLine = foundDocument.lines[lineIndex].text.split(/\s+/); // get the index of the second word const indexOfFirstWord = currentLineUntrimmed.indexOf(wordsInLine[0]); const indexOfCurrentName = currentLineUntrimmed.indexOf(currentName, indexOfFirstWord + wordsInLine[0].length); appendEdit(edits, document.uri, { range: { start: { line: lineIndex, character: indexOfCurrentName, }, end: { line: lineIndex, character: indexOfCurrentName + currentName.length, }, }, newText: newName, }); } } } } } } return edits; } function mapFieldAttributeExistsAlready(line) { return line.includes('@map('); } function mapBlockAttributeExistsAlready(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; } if (text.startsWith('@@map(')) { return true; } } return false; } function insertBasicRename(newName, currentName, document, line) { const currentLineUntrimmed = (0, ast_1.getCurrentLine)(document, line); const indexOfCurrentName = currentLineUntrimmed.indexOf(currentName); return { [document.uri]: [ { range: { start: { line: line, character: indexOfCurrentName, }, end: { line: line, character: indexOfCurrentName + currentName.length, }, }, newText: newName, }, ], }; } function mapExistsAlready(currentLine, schema, block, isDatamodelBlockRename) { if (isDatamodelBlockRename) { return mapBlockAttributeExistsAlready(block, schema); } else { return mapFieldAttributeExistsAlready(currentLine); } } function insertMapAttribute(currentName, position, block, isDatamodelBlockRename) { if (isDatamodelBlockRename) { return insertMapBlockAttribute(currentName, block); } else { const line = block.definingDocument.lines[position.line]; return insertInlineRename(currentName, line); } } function extractCurrentName(line, isBlockRename, isEnumValueRename, isFieldRename, document, position) { if (isBlockRename) { const currentLineUntrimmed = (0, ast_1.getCurrentLine)(document, position.line); const currentLineTillPosition = currentLineUntrimmed .slice(0, position.character + currentLineUntrimmed.slice(position.character).search(/\W/)) .trim(); const wordsBeforePosition = currentLineTillPosition.split(/\s+/); if (wordsBeforePosition.length < 2) { return ''; } return wordsBeforePosition[1]; } if (isEnumValueRename || isFieldRename) { return extractFirstWord(line); } return ''; } function appendEdit(edits, documentUri, edit) { let docEdits = edits[documentUri]; if (!docEdits) { docEdits = []; edits[documentUri] = docEdits; } docEdits.push(edit); } function appendEdits(edits, documentUri, editsToAppend) { for (const edit of editsToAppend) { appendEdit(edits, documentUri, edit); } } function mergeEditMaps(maps) { const result = {}; for (const map of maps) { for (const [documentUri, edits] of Object.entries(map)) { appendEdits(result, documentUri, edits); } } return result; } //# sourceMappingURL=rename.js.map