Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suggest @map when not already present in line #1320

Merged
merged 2 commits into from
Dec 12, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 14 additions & 1 deletion packages/language-server/src/__test__/completion.test.ts
Original file line number Diff line number Diff line change
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")
Druue marked this conversation as resolved.
Show resolved Hide resolved
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
Original file line number Diff line number Diff line change
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')
Druue marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like we should do the same for @unique, @default(), @ignore, @relation after this PR

}

return suggestions
}

/**
Expand Down
41 changes: 12 additions & 29 deletions packages/language-server/src/completion/completions.ts
Original file line number Diff line number Diff line change
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