@prisma/language-server
Version:
Prisma Language Server
177 lines • 7.37 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.getDocumentationForBlock = void 0;
exports.getBlocks = getBlocks;
exports.getDatamodelBlock = getDatamodelBlock;
exports.getFieldsFromCurrentBlock = getFieldsFromCurrentBlock;
exports.getFieldTypesFromCurrentBlock = getFieldTypesFromCurrentBlock;
exports.getCompositeTypeFieldsRecursively = getCompositeTypeFieldsRecursively;
const vscode_languageserver_1 = require("vscode-languageserver");
const fields_1 = require("./fields");
const findAtPosition_1 = require("./findAtPosition");
// Note: this is a generator function, which returns a Generator object.
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
function* getBlocks(schema) {
let blockName = '';
let blockType = '';
let blockNameRange;
let blockStart = vscode_languageserver_1.Position.create(0, 0);
const allowedBlockIdentifiers = ['model', 'type', 'enum', 'datasource', 'generator', 'view'];
for (const { document, lineIndex, text } of schema.iterLines()) {
// if start of block: `BlockType name {`
if (allowedBlockIdentifiers.some((identifier) => text.startsWith(identifier)) && text.includes('{')) {
if (blockType && blockNameRange) {
// Recover from missing block end
yield {
type: blockType,
range: vscode_languageserver_1.Range.create(blockStart, vscode_languageserver_1.Position.create(lineIndex - 1, 0)),
nameRange: blockNameRange,
name: blockName,
definingDocument: document,
};
blockType = '';
blockNameRange = undefined;
}
const index = text.search(/\s+/);
blockType = ~index ? text.slice(0, index) : text;
blockName = text.slice(blockType.length, text.length - 2).trimStart();
const startCharacter = text.length - 2 - blockName.length;
blockName = blockName.trimEnd();
blockNameRange = vscode_languageserver_1.Range.create(lineIndex, startCharacter, lineIndex, startCharacter + blockName.length);
blockStart = vscode_languageserver_1.Position.create(lineIndex, 0);
continue;
}
// if end of block: `}`
if (text.startsWith('}') && blockType && blockNameRange) {
yield {
type: blockType,
range: vscode_languageserver_1.Range.create(blockStart, vscode_languageserver_1.Position.create(lineIndex, 1)),
nameRange: blockNameRange,
name: blockName,
definingDocument: document,
};
blockType = '';
blockNameRange = undefined;
}
}
}
function getDatamodelBlock(blockName, schema) {
// get start position of block
const results = schema
.linesAsArray()
.map(({ document, lineIndex, text }) => {
if ((text.includes('model') || text.includes('type') || text.includes('enum') || text.includes('view')) &&
text.includes(blockName)) {
return [document, lineIndex];
}
})
.filter((result) => result !== undefined);
if (results.length === 0) {
return;
}
const foundBlocks = results
.map(([document, lineNo]) => {
const block = (0, findAtPosition_1.getBlockAtPosition)(document.uri, lineNo, schema);
if (block && block.name === blockName) {
return block;
}
})
.filter((block) => block !== undefined);
if (foundBlocks.length !== 1) {
return;
}
if (!foundBlocks[0]) {
return;
}
return foundBlocks[0];
}
function getFieldsFromCurrentBlock(schema, block, position) {
const fieldNames = [];
const lines = block.definingDocument.lines;
for (let lineIndex = block.range.start.line + 1; lineIndex < block.range.end.line; lineIndex++) {
if (!position || lineIndex !== position.line) {
const line = lines[lineIndex].text;
const fieldName = getFieldNameFromLine(line);
if (fieldName) {
fieldNames.push(fieldName);
}
}
}
return fieldNames;
}
function getFieldTypesFromCurrentBlock(schema, block, position) {
const fieldTypes = new Map();
const fieldTypeNames = {};
let reachedStartLine = false;
for (const { document, lineIndex, text: line } of schema.iterLines()) {
if (lineIndex === block.range.start.line + 1) {
reachedStartLine = true;
}
if (!reachedStartLine) {
continue;
}
if (lineIndex === block.range.end.line) {
break;
}
if (!line.startsWith('@@') && (!position || lineIndex !== position.line)) {
const fieldType = (0, fields_1.getFieldType)(line);
if (fieldType !== undefined) {
const existingFieldType = fieldTypes.get(fieldType);
if (!existingFieldType) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const fieldName = getFieldNameFromLine(line);
fieldTypes.set(fieldType, { locations: [{ document, lineIndex }], fieldName });
fieldTypeNames[fieldName] = fieldType;
}
else {
existingFieldType.locations.push({ document, lineIndex });
fieldTypes.set(fieldType, existingFieldType);
}
}
}
}
return { fieldTypes, fieldTypeNames };
}
function getCompositeTypeFieldsRecursively(schema, compositeTypeFieldNames, fieldTypesFromBlock) {
const compositeTypeFieldName = compositeTypeFieldNames.shift();
if (!compositeTypeFieldName) {
return [];
}
const fieldTypeNames = fieldTypesFromBlock.fieldTypeNames;
const fieldTypeName = fieldTypeNames[compositeTypeFieldName];
if (!fieldTypeName) {
return [];
}
const typeBlock = getDatamodelBlock(fieldTypeName, schema);
if (!typeBlock || typeBlock.type !== 'type') {
return [];
}
// if we are not at the end of the composite type, continue recursively
if (compositeTypeFieldNames.length) {
return getCompositeTypeFieldsRecursively(schema, compositeTypeFieldNames, getFieldTypesFromCurrentBlock(schema, typeBlock));
}
else {
return getFieldsFromCurrentBlock(schema, typeBlock);
}
}
// TODO (Joël) a regex for \w in first position would be better?
function getFieldNameFromLine(line) {
if (line.startsWith('//') || line.startsWith('@@')) {
return undefined;
}
const firstPartOfLine = line.replace(/ .*/, '');
return firstPartOfLine;
}
const getDocumentationForBlock = (block) => {
return getDocumentation(block.definingDocument, block.range.start.line, []);
};
exports.getDocumentationForBlock = getDocumentationForBlock;
const getDocumentation = (document, line, comments) => {
const comment = document.lines[line - 1]?.untrimmedText ?? '';
if (comment.startsWith('///')) {
comments.unshift(comment.slice(4).trim());
return getDocumentation(document, line - 1, comments);
}
return comments;
};
//# sourceMappingURL=block.js.map