UNPKG

@prisma/language-server

Version:
180 lines (155 loc) • 5.3 kB
import { Range, Position } from 'vscode-languageserver' import type { TextDocument } from 'vscode-languageserver-textdocument' import { MAX_SAFE_VALUE_i32 } from '../constants' import { Block, getBlocks } from './block' import { PrismaSchema } from '../Schema' export function fullDocumentRange(document: TextDocument): Range { const lastLineId = document.lineCount - 1 return { start: { line: 0, character: 0 }, end: { line: lastLineId, character: MAX_SAFE_VALUE_i32 }, } } export function getCurrentLine(document: TextDocument, line: number): string { return document.getText({ start: { line: line, character: 0 }, end: { line: line, character: MAX_SAFE_VALUE_i32 }, }) } export function isFirstInsideBlock(position: Position, currentLine: string): boolean { if (currentLine.trim().length === 0) { return true } const stringTilPosition = currentLine.slice(0, position.character) const matchArray = /\w+/.exec(stringTilPosition) if (!matchArray) { return true } return ( matchArray.length === 1 && matchArray.index !== undefined && stringTilPosition.length - matchArray.index - matchArray[0].length === 0 ) } export function getWordAtPosition(document: TextDocument, position: Position): string { const currentLine = getCurrentLine(document, position.line) // search for the word's beginning and end const beginning: number = currentLine.slice(0, position.character + 1).search(/\S+$/) const end: number = currentLine.slice(position.character).search(/\W/) if (end < 0) { return '' } return currentLine.slice(beginning, end + position.character) } export function getBlockAtPosition(fileUri: string, line: number, schema: PrismaSchema): Block | undefined { for (const block of getBlocks(schema)) { if (fileUri !== block.definingDocument.uri) { continue } if (block.range.start.line > line) { return } if (line <= block.range.end.line) { return block } } return } export function getSymbolBeforePosition(document: TextDocument, position: Position): string { return document.getText({ start: { line: position.line, character: position.character - 1, }, end: { line: position.line, character: position.character }, }) } export function positionIsAfterFieldAndType( position: Position, document: TextDocument, wordsBeforePosition: string[], ): boolean { const symbolBeforePosition = getSymbolBeforePosition(document, position) const symbolBeforeIsWhiteSpace = symbolBeforePosition.search(/\s/) const hasAtRelation = wordsBeforePosition.length === 2 && symbolBeforePosition === '@' const hasWhiteSpaceBeforePosition = wordsBeforePosition.length === 2 && symbolBeforeIsWhiteSpace !== -1 return wordsBeforePosition.length > 2 || hasAtRelation || hasWhiteSpaceBeforePosition } // checks if e.g. inside 'fields' or 'references' attribute export function isInsideGivenProperty( currentLineUntrimmed: string, wordsBeforePosition: string[], attributeName: string, position: Position, ): boolean { if (!isInsideAttribute(currentLineUntrimmed, position, '[]')) { return false } // We sort all attributes by their position const sortedAttributes = [ { name: 'fields', position: wordsBeforePosition.findIndex((word) => word.includes('fields')), }, { name: 'references', position: wordsBeforePosition.findIndex((word) => word.includes('references')), }, ].sort((a, b) => (a.position < b.position ? 1 : -1)) // If the last attribute (higher position) // is the one we are looking for we are in this attribute if (sortedAttributes[0].name === attributeName) { return true } else { return false } } /*** * @param symbols expects e.g. '()', '[]' or '""' */ export function isInsideAttribute(currentLineUntrimmed: string, position: Position, symbols: string): boolean { let numberOfOpenBrackets = 0 let numberOfClosedBrackets = 0 for (let i = 0; i < position.character; i++) { if (currentLineUntrimmed[i] === symbols[0]) { numberOfOpenBrackets++ } else if (currentLineUntrimmed[i] === symbols[1]) { numberOfClosedBrackets++ } } return numberOfOpenBrackets > numberOfClosedBrackets } export function isInsideFieldArgument(currentLineUntrimmed: string, position: Position): boolean { const symbols = '()' let numberOfOpenBrackets = 0 let numberOfClosedBrackets = 0 for (let i = 0; i < position.character; i++) { if (currentLineUntrimmed[i] === symbols[0]) { numberOfOpenBrackets++ } else if (currentLineUntrimmed[i] === symbols[1]) { numberOfClosedBrackets++ } } return numberOfOpenBrackets >= 2 && numberOfOpenBrackets > numberOfClosedBrackets } export function getValuesInsideSquareBrackets(line: string): string[] { const regexp = /\[([^\]]+)\]/ const matches = regexp.exec(line) if (!matches || !matches[1]) { return [] } const result = matches[1].split(',').map((name) => { name = name // trim whitespace .trim() // remove ""? .replace('"', '') .replace('"', '') // Remove period at the end for composite types if (name.endsWith('.')) { return name.slice(0, -1) } return name }) return result }