Skip to content

Commit

Permalink
Fixes: #1079
Browse files Browse the repository at this point in the history
Added test to ensure that `@map` is not suggested when already present in line
Updated another test to ensure that it does still show when already present on another line
Minor refactoring
  • Loading branch information
Druue committed Dec 8, 2022
1 parent 62a5f36 commit a4a4850
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 35 deletions.
15 changes: 14 additions & 1 deletion packages/language-server/src/__test__/completion.test.ts
Expand Up @@ -2043,7 +2043,7 @@ suite('Completions', function () {
provider: 'postgresql',
schema: /* Prisma */ `
model Post {
id Int @id @default()
id Int @id @default() @map("_id")
email String? @unique
name String |
}`,
Expand All @@ -2059,6 +2059,19 @@ suite('Completions', function () {
],
},
})
assertCompletion({
schema: /* Prisma */ `
model Post {
id Int @id @default()
email String? @unique
name String @map("_name")|
}
`,
expected: {
isIncomplete: false,
items: [fieldAttributeUnique, fieldAttributeDefault, fieldAttributeRelation, fieldAttributeIgnore],
},
})
})

const enumUserTypeExpectedItems = [
Expand Down
49 changes: 44 additions & 5 deletions packages/language-server/src/completion/completionUtils.ts
Expand Up @@ -11,7 +11,7 @@ import {
import * as completions from './completions.json'
import type { PreviewFeatures } from '../previewFeatures'
import nativeTypeConstructors, { NativeTypeConstructors } from '../prisma-fmt/nativeTypes'
import { Block, getValuesInsideSquareBrackets, isInsideAttribute } from '../util'
import { Block, BlockType, getValuesInsideSquareBrackets, isInsideAttribute } from '../util'

type JSONSimpleCompletionItems = {
label: string
Expand Down Expand Up @@ -377,8 +377,8 @@ export function toCompletionItems(allowedTypes: string[], kind: CompletionItemKi
* Removes all block attribute suggestions that are invalid in this context.
* E.g. `@@id()` when already used should not be in the suggestions.
*/
export function removeInvalidAttributeSuggestions(
supportedAttributes: CompletionItem[],
export function filterSuggestionsForBlock(
suggestions: CompletionItem[],
block: Block,
lines: string[],
): CompletionItem[] {
Expand All @@ -398,11 +398,50 @@ export function removeInvalidAttributeSuggestions(
if (!item.startsWith('//')) {
// TODO we should also remove the other suggestions if used (default()...)
if (item.includes('@id')) {
supportedAttributes = supportedAttributes.filter((attribute) => !attribute.label.includes('id'))
suggestions = suggestions.filter((attribute) => !attribute.label.includes('id'))
}
}
}
return supportedAttributes
return suggestions
}

/**
* Removes all line attribute suggestions that are invalid in this context.
* E.g. `@map()` when already used should not be in the suggestions.
*/
export function filterSuggestionsForLine(
suggestions: CompletionItem[],
currentLine: string,
fieldType: string,
fieldBlockType?: BlockType,
) {
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')
}

if (currentLine.includes('@map')) {
suggestions = suggestions.filter((suggestion) => suggestion.label !== '@map')
}

return suggestions
}

/**
Expand Down
41 changes: 12 additions & 29 deletions packages/language-server/src/completion/completions.ts
Expand Up @@ -28,11 +28,12 @@ import {
sortLengthProperties,
filterSortLengthBasedOnInput,
toCompletionItems,
removeInvalidAttributeSuggestions,
filterSuggestionsForBlock,
removeInvalidFieldSuggestions,
getNativeTypes,
handlePreviewFeatures,
relationModeValues,
filterSuggestionsForLine,
} from './completionUtils'
import listAllAvailablePreviewFeatures from '../prisma-fmt/listAllAvailablePreviewFeatures'
import {
Expand Down Expand Up @@ -60,7 +61,7 @@ function getSuggestionForModelBlockAttribute(block: Block, lines: string[]): Com
return []
}
// create deep copy
const suggestions: CompletionItem[] = removeInvalidAttributeSuggestions(klona(blockAttributes), block, lines)
const suggestions: CompletionItem[] = filterSuggestionsForBlock(klona(blockAttributes), block, lines)

// We can filter on the datasource
const datasourceProvider = getFirstDatasourceProvider(lines)
Expand Down Expand Up @@ -137,6 +138,13 @@ export function getSuggestionForFieldAttribute(
if (block.type !== 'model') {
return
}

const fieldType = getFieldType(currentLine)
// If we don't find a field type (e.g. String, Int...), return no suggestion
if (!fieldType) {
return
}

let suggestions: CompletionItem[] = []

// Because @.?
Expand Down Expand Up @@ -175,36 +183,11 @@ export function getSuggestionForFieldAttribute(

suggestions.push(...fieldAttributes)

const fieldType = getFieldType(currentLine)
// If we don't find a field type (e.g. String, Int...), return no suggestion
if (!fieldType) {
return
}

const modelOrTypeOrEnum = getModelOrTypeOrEnumBlock(fieldType, lines)
if (modelOrTypeOrEnum?.type === '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' || modelOrTypeOrEnum?.type === 'enum'
if (!isAtIdAllowed) {
// id not allowed
suggestions = suggestions.filter((sugg) => sugg.label !== '@id')
}

const isUpdatedAtAllowed = fieldType === 'DateTime'
if (!isUpdatedAtAllowed) {
// updatedAt not allowed
suggestions = suggestions.filter((sugg) => sugg.label !== '@updatedAt')
}
suggestions = filterSuggestionsForLine(suggestions, currentLine, fieldType, modelOrTypeOrEnum?.type)

suggestions = removeInvalidAttributeSuggestions(suggestions, block, lines)
suggestions = filterSuggestionsForBlock(suggestions, block, lines)

return {
items: suggestions,
Expand Down

0 comments on commit a4a4850

Please sign in to comment.