diff --git a/packages/language-server/src/__test__/Schema.test.ts b/packages/language-server/src/__test__/Schema.test.ts new file mode 100644 index 0000000000..5d486629e8 --- /dev/null +++ b/packages/language-server/src/__test__/Schema.test.ts @@ -0,0 +1,51 @@ +import { loadRelatedSchemaFiles } from '@prisma/schema-files-loader' +import { TextDocument } from 'vscode-languageserver-textdocument' +import { URI } from 'vscode-uri' +import { beforeEach, describe, expect, test, vi } from 'vitest' +import { PrismaSchema } from '../lib/Schema' + +vi.mock('@prisma/config', () => ({ + loadConfigFromFile: vi.fn().mockResolvedValue({ config: undefined }), +})) + +vi.mock('@prisma/schema-files-loader', async (importOriginal) => ({ + ...(await importOriginal()), + loadRelatedSchemaFiles: vi.fn(), +})) + +describe('PrismaSchema.load', () => { + beforeEach(() => { + vi.mocked(loadRelatedSchemaFiles).mockReset() + }) + + test('preserves the client URI for an open document', async () => { + const clientUri = 'file:///C:/workspace/schema.prisma' + const filePath = URI.parse(clientUri).fsPath + const document = TextDocument.create(clientUri, 'prisma', 7, 'model User {\n id Int @id\n}') + + vi.mocked(loadRelatedSchemaFiles).mockResolvedValue([[filePath, document.getText()]]) + + const schema = await PrismaSchema.load({ currentDocument: document, allDocuments: [document] }) + + expect(schema.documents).toHaveLength(1) + expect(schema.documents[0].uri).toBe(clientUri) + }) + + test('creates a file URI for a related document that is not open', async () => { + const clientUri = 'file:///C:/workspace/schema.prisma' + const currentDocument = TextDocument.create(clientUri, 'prisma', 1, '') + const relatedFilePath = URI.parse('file:///C:/workspace/related.prisma').fsPath + const relatedContent = 'model Related {\n id Int @id\n}' + + vi.mocked(loadRelatedSchemaFiles).mockResolvedValue([ + [URI.parse(clientUri).fsPath, currentDocument.getText()], + [relatedFilePath, relatedContent], + ]) + + const schema = await PrismaSchema.load({ currentDocument, allDocuments: [currentDocument] }) + + expect(schema.documents).toHaveLength(2) + expect(schema.documents[1].uri).toBe(URI.file(relatedFilePath).toString()) + expect(schema.documents[1].content).toBe(relatedContent) + }) +}) diff --git a/packages/language-server/src/lib/Schema.ts b/packages/language-server/src/lib/Schema.ts index 31d3539244..6e743b9f16 100644 --- a/packages/language-server/src/lib/Schema.ts +++ b/packages/language-server/src/lib/Schema.ts @@ -10,6 +10,7 @@ import { loadConfigFromFile, type PrismaConfigInternal } from '@prisma/config' import { Position } from 'vscode-languageserver' import { TextDocument } from 'vscode-languageserver-textdocument' import { URI } from 'vscode-uri' +import path from 'path' import { getCurrentLine } from './ast' export type Line = { @@ -84,12 +85,21 @@ export async function loadConfig(configRoot?: string): Promise { // `loadRelatedSchemaFiles` locates and returns either a single schema files, or a set of related schema files. const schemaFiles = await loadRelatedSchemaFiles(fsPath, createFilesResolver(allDocuments)) + const openDocumentUris = new Map( + allDocuments.map((document) => [filePathKey(URI.parse(document.uri).fsPath), document.uri]), + ) const documents = schemaFiles.map(([filePath, content]) => { - return new SchemaDocument(TextDocument.create(URI.file(filePath).toString(), 'prisma', 1, content)) + const uri = openDocumentUris.get(filePathKey(filePath)) ?? URI.file(filePath).toString() + return new SchemaDocument(TextDocument.create(uri, 'prisma', 1, content)) }) return documents } +function filePathKey(filePath: string): string { + const normalizedPath = path.normalize(filePath) + return process.platform === 'linux' ? normalizedPath : normalizedPath.toLowerCase() +} + type PrismaSchemaInput = { currentDocument: TextDocument; allDocuments: TextDocument[] } | SchemaDocument[] export class PrismaSchema {