-
Notifications
You must be signed in to change notification settings - Fork 15
Import from anywhere #604
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
Merged
Merged
Import from anywhere #604
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
4831d0c
Use JayveeService instead of Langium specific one in utils
georg-schwarz f60e4e7
Add method findUnresolvedImportURIs to JayveeImportResolver
georg-schwarz 11849b5
Add hook on workspace initialization to load unresolved documents out…
georg-schwarz a79bbd3
Initialize the workspace in the VSCode extension
georg-schwarz f619fe4
Dynamic file loading from fs as string instead from URI to allow brow…
georg-schwarz 641d3be
Use FileSystemProvider instead of node:fs
georg-schwarz b9abbaa
Add launch config for electric vehicle example
georg-schwarz 675c795
Add hooks before initializing the workspace
georg-schwarz 1da7dc2
Use mutex to prevent adding documents multiple times
georg-schwarz 835f9ca
Only load documents with correct file extension
georg-schwarz 9a93a06
Fix error message when import cannot be resolved
georg-schwarz 2803955
Refactor check on correct file extension before loading
georg-schwarz d942134
Remove mutex due to deadlock
georg-schwarz 4ce82e2
Add tests for the dynamic import
georg-schwarz 74a5ae0
Place more narrow mutex lock around adding new documents
georg-schwarz 25274d6
Rename std-lib directory to workspace
georg-schwarz 86a66f7
Extract dynamic file import to own file and initialize while creating…
georg-schwarz 4d4b10c
Add license text
georg-schwarz add26e4
Fix copyright year
georg-schwarz 77c608c
Fix typo in test file name
georg-schwarz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,4 +4,4 @@ | |
|
||
# files generated by Langium | ||
src/lib/ast/generated | ||
src/lib/builtin-library/generated | ||
src/lib/workspace/generated |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
90 changes: 90 additions & 0 deletions
90
libs/language-server/src/lib/lsp/import-dynamic-reference.spec.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
// SPDX-FileCopyrightText: 2024 Friedrich-Alexander-Universitat Erlangen-Nurnberg | ||
// | ||
// SPDX-License-Identifier: AGPL-3.0-only | ||
|
||
import path from 'node:path'; | ||
|
||
import { NodeFileSystem } from 'langium/node'; | ||
|
||
import { | ||
type JayveeServices, | ||
createJayveeServices, | ||
isJayveeModel, | ||
} from '../../lib'; | ||
import { | ||
expectNoParserAndLexerErrors, | ||
parseTestFileInWorkingDir, | ||
} from '../../test'; | ||
import { type JayveeModel } from '../ast'; | ||
|
||
describe('References to imported elements outside of the working directory', () => { | ||
const WORKING_DIR = path.resolve( | ||
__dirname, | ||
'../../test/assets/import-dynamic-reference/models', // use the "deep" directory as working dir to avoid loading the "higher" dir | ||
); | ||
let services: JayveeServices; | ||
|
||
async function parseModel( | ||
relativeTestFilePath: string, | ||
): Promise<JayveeModel> { | ||
const document = await parseTestFileInWorkingDir( | ||
WORKING_DIR, | ||
relativeTestFilePath, | ||
services, | ||
); | ||
expectNoParserAndLexerErrors(document); | ||
|
||
const parsedModel = document.parseResult.value; | ||
assert(isJayveeModel(parsedModel), 'Test file is not valid Jayvee model'); | ||
return parsedModel; | ||
} | ||
|
||
beforeEach(() => { | ||
// Create language services | ||
services = createJayveeServices(NodeFileSystem).Jayvee; | ||
}); | ||
|
||
const validCases: [string, string][] = [ | ||
// [test name, test file path] | ||
[ | ||
'should resolve reference to imported element', | ||
'valid-import-from-outside-workdir.jv', | ||
], | ||
]; | ||
test.each(validCases)('%s', async (_, relativeTestFilePath) => { | ||
const model = await parseModel(relativeTestFilePath); | ||
|
||
expect(model.pipelines.length).toEqual(1); // file contains one pipeline | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const blocks = model.pipelines[0]!.blocks; | ||
expect(blocks.length).toEqual(1); // pipeline contains one block | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const reference = blocks[0]!.type; // of an imported block type | ||
|
||
expect(reference.ref).toBeDefined(); | ||
}); | ||
|
||
const invalidCases: [string, string][] = [ | ||
// [test name, test file path] | ||
[ | ||
'should not resolve reference to file with no jv extension', | ||
'invalid-import-wrong-file-type.jv', | ||
], | ||
[ | ||
'should not resolve reference to file that does not exist', | ||
'invalid-import-not-existing-file.jv', | ||
], | ||
]; | ||
test.each(invalidCases)('%s', async (_, relativeTestFilePath) => { | ||
const model = await parseModel(relativeTestFilePath); | ||
|
||
expect(model.pipelines.length).toEqual(1); // file contains one pipeline | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const blocks = model.pipelines[0]!.blocks; | ||
expect(blocks.length).toEqual(1); // pipeline contains one block | ||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion | ||
const reference = blocks[0]!.type; // of an imported block type | ||
|
||
expect(reference.ref).toBeUndefined(); | ||
}); | ||
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
74 changes: 74 additions & 0 deletions
74
libs/language-server/src/lib/workspace/dynamic-file-import.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// SPDX-FileCopyrightText: 2024 Friedrich-Alexander-Universitat Erlangen-Nurnberg | ||
// | ||
// SPDX-License-Identifier: AGPL-3.0-only | ||
|
||
import { DocumentState } from 'langium'; | ||
import { type URI } from 'vscode-uri'; | ||
|
||
import { type JayveeModel, isJayveeModel } from '../ast'; | ||
import { type JayveeServices } from '../jayvee-module'; | ||
|
||
export function addDynamicFileImport(services: JayveeServices): void { | ||
const documentBuilder = services.shared.workspace.DocumentBuilder; | ||
const importResolver = services.ImportResolver; | ||
|
||
documentBuilder.onBuildPhase(DocumentState.IndexedContent, async (docs) => { | ||
for (const doc of docs) { | ||
const model = doc.parseResult.value; | ||
if (!isJayveeModel(model)) { | ||
return; | ||
} | ||
const importURIs = importResolver.findUnresolvedImportURIs(model); | ||
|
||
for (const importURI of importURIs) { | ||
await loadDocumentFromFs(importURI, services); | ||
} | ||
} | ||
}); | ||
} | ||
|
||
async function loadDocumentFromFs( | ||
importURI: URI, | ||
services: JayveeServices, | ||
): Promise<void> { | ||
const allowedFileExtensions = services.shared.ServiceRegistry.all.flatMap( | ||
(e) => e.LanguageMetaData.fileExtensions, | ||
); | ||
const hasAllowedFileExtension = allowedFileExtensions.some((ext) => | ||
importURI.fsPath.endsWith(ext), | ||
); | ||
if (!hasAllowedFileExtension) { | ||
return; | ||
} | ||
|
||
const langiumDocuments = services.shared.workspace.LangiumDocuments; | ||
const documentBuilder = services.shared.workspace.DocumentBuilder; | ||
const documentFactory = services.shared.workspace.LangiumDocumentFactory; | ||
|
||
const file = await loadFileFromUri(importURI, services); | ||
if (file === undefined) { | ||
return; | ||
} | ||
|
||
const document = documentFactory.fromString<JayveeModel>(file, importURI); | ||
await documentBuilder.build([document], { validation: true }); | ||
|
||
await services.shared.workspace.WorkspaceLock.write(() => { | ||
if (!langiumDocuments.hasDocument(document.uri)) { | ||
langiumDocuments.addDocument(document); | ||
} | ||
}); | ||
} | ||
|
||
async function loadFileFromUri( | ||
uri: URI, | ||
services: JayveeServices, | ||
): Promise<string | undefined> { | ||
const fileSystemProvider = services.shared.workspace.FileSystemProvider; | ||
|
||
try { | ||
return await fileSystemProvider.readFile(uri); | ||
} catch (e) { | ||
return undefined; | ||
} | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.