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

Indexing single Lingua Franca projects #181

Merged
merged 10 commits into from
Oct 16, 2024
16 changes: 7 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,6 @@
"dark": "#57cc99",
"light": "#57cc99"
}
},
{
"id": "editorIcon.notCurrentProject",
"description": "Color for a TreeIteem label",
"defaults": {
"dark": "#fcbf49",
"light": "#fcbf49"
}
}
],
"semanticTokenScopes": [
Expand Down Expand Up @@ -278,7 +270,13 @@
"name": ""
}
]
}
},
"viewsWelcome": [
{
"view": "lf-lang-projects",
"contents": "No Lingua Franca project found. [Learn more](https://www.lf-lang.org/docs/) about setting up a Lingua Franca project structure.\n[Open Lingua Franca Project](command:linguafranca.openFolder)"
Copy link
Member

Choose a reason for hiding this comment

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

Should we point to a specific page?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, that’s the idea. Do you know which specific page?

Copy link
Member

Choose a reason for hiding this comment

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

https://www.lf-lang.org/docs/tools/code-extension/#usage but this page it rather incomplete. Also, there is no functionality to create a new project, or a description of how to do so. Should be easy to let lingo do it via VS Code once it's available as a library...

}
]
},
"devDependencies": {
"@types/chai": "^4.3.1",
Expand Down
2 changes: 2 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { registerCollapseAllCommand,
registerGoToLingoTomlCommand,
registerImportReactorCommand,
registerIncludeProjectCommand,
registerOpenFolderCommand,
registerOpenInSplitViewCommand,
registerOpenInTerminalCommand,
registerRefreshCommand} from './lfview/lf-data-provider-commands';
Expand Down Expand Up @@ -99,6 +100,7 @@ export async function activate(context: vscode.ExtensionContext) {
registerGoToLingoTomlCommand(context, lfDataProvider);
registerIncludeProjectCommand(context, lfDataProvider);
registerOpenInTerminalCommand(context);
registerOpenFolderCommand(context);

context.subscriptions.push(vscode.commands.registerCommand(
"linguafranca.checkDocker", checkDependencies.checkDocker
Expand Down
19 changes: 19 additions & 0 deletions src/lfview/lf-data-provider-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,22 @@ export function registerOpenInTerminalCommand(context: vscode.ExtensionContext)
));
}

export function registerOpenFolderCommand(context: vscode.ExtensionContext) {
context.subscriptions.push(vscode.commands.registerCommand(
'linguafranca.openFolder', async ( ) => {
// Prompt the user to select a folder
const folderUri = await vscode.window.showOpenDialog({
canSelectFolders: true, // Allow folder selection
canSelectFiles: false, // Disallow file selection
canSelectMany: false, // Allow only a single folder
openLabel: 'Select Folder'
});

if (folderUri && folderUri[0]) {
// Use vscode.openFolder to open the selected folder
vscode.commands.executeCommand('vscode.openFolder', folderUri[0], false);
}
}
));
}

23 changes: 10 additions & 13 deletions src/lfview/lf-data-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export class LFDataProviderNode extends vscode.TreeItem {
newIcon,
sameRootAsEditor
? new vscode.ThemeColor('editorIcon.currentProject')
: new vscode.ThemeColor('editorIcon.notCurrentProject')
: undefined
);
}

Expand Down Expand Up @@ -183,7 +183,7 @@ export class LFDataProviderNode extends vscode.TreeItem {
return editor.document.uri.fsPath.startsWith(this.uri.fsPath);
}
const pathSegments = this.uri.fsPath.split('/');
const srcOrBuildIndex = pathSegments.indexOf(this.type === LFDataProviderNodeType.LIBRARY ? 'build' : 'src');
const srcOrBuildIndex = pathSegments.lastIndexOf(this.type === LFDataProviderNodeType.LIBRARY ? 'build' : 'src');

if (srcOrBuildIndex === -1) {
return false;
Expand Down Expand Up @@ -219,11 +219,11 @@ export class LFDataProvider implements vscode.TreeDataProvider<LFDataProviderNod
private data: LFDataProviderNode[] = [];

// Utility properties
private searchSourceFiles: string = '**/src/*.lf';
private searchPathLocal: string = '**/src/lib/*.lf';
private searchPathLibrary: vscode.GlobPattern = '**/build/lfc_include/**/src/lib/*.lf';
private searchSourceFiles: vscode.GlobPattern = 'src/**/*.lf';
private searchPathLocal: vscode.GlobPattern = 'src/lib/*.lf';
private searchPathLibrary: vscode.GlobPattern = 'build/lfc_include/**/src/lib/*.lf';
private exclude_path_local: vscode.GlobPattern = '**/build/**'; // only for local LF libraries
private exclude_path_src: vscode.GlobPattern = `{${this.exclude_path_local},**/fed-gen/**,**/src-gen/***}`
private exclude_path_src: vscode.GlobPattern = `{${this.exclude_path_local},${this.searchPathLocal},**/fed-gen/**,**/src-gen/***}`

// Event emitter for tree data change
private _onDidChangeTreeData: vscode.EventEmitter<LFDataProviderNode | undefined | null | void> = new vscode.EventEmitter<LFDataProviderNode | undefined | null | void>();
Expand All @@ -245,7 +245,6 @@ export class LFDataProvider implements vscode.TreeDataProvider<LFDataProviderNod
private client: LanguageClient,
private context: vscode.ExtensionContext,
) {

// Register to file system changes: Create, Delete, Rename, Change
this.watchFileChanges(this.context);

Expand Down Expand Up @@ -361,7 +360,6 @@ export class LFDataProvider implements vscode.TreeDataProvider<LFDataProviderNod
updateIfExists(LFDataProviderNodeType.SOURCE);
}
});

this._onDidChangeTreeData.fire(undefined);
}

Expand Down Expand Up @@ -401,7 +399,6 @@ export class LFDataProvider implements vscode.TreeDataProvider<LFDataProviderNod
}
this._onDidChangeTreeData.fire(undefined);
}

findFiles(searchPath: string | vscode.GlobPattern, exclude_path: vscode.GlobPattern | null, type: LFDataProviderNodeType): void {
vscode.workspace.findFiles(searchPath, exclude_path ? exclude_path : null).then(uris => {
uris.forEach(uri => {
Expand Down Expand Up @@ -577,7 +574,7 @@ export class LFDataProvider implements vscode.TreeDataProvider<LFDataProviderNod
*/
buildRoot(uri: string, type: LFDataProviderNodeType | null): LFDataProviderNode {
const splittedUri = uri.split('/');
const srcIdx = splittedUri.indexOf(!type || type == LFDataProviderNodeType.LIBRARY ? 'build' : 'src');
const srcIdx = splittedUri.lastIndexOf(!type || type == LFDataProviderNodeType.LIBRARY ? 'build' : 'src');
Copy link
Member

Choose a reason for hiding this comment

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

Why this change? This seems to relate to our discussion. The question is whether we should find the file relative to the root of the workspace, or fine the root of the package by traversing up from the file. In lfc, we have no workspace root, so we do the latter. I think that for an IDE it makes more sense to do the former.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I use the last index because the uri passed to the buildRoot function is an absolute file path. For example, if my workspace is opened for a project named project with the following structure:

├── project
...
│   ├── src/ # Contains LF source files
│   │  ├── SourceFile.lf # Can also be in a subfolder, e.g. source1/SourceFile.lf
│   │  ├── lib/ # Directory for storing reusable reactors
│   │  │   ├── Input.lf # Example: reactor capturing external inputs (e.g., Microphone, Camera)
│   ├── build/   # Directory containing lingo packages
│   │  ├── lfc_include/ # Directory for storing reusable reactors
│   │  │   ├── lingo_downloaded_library/
...

Let's assume the project in your file system has an src folder in its absolute path. The uri passed to the function could be something like /root/src/project/src/SourceFile.lf. If you use indexOf, it will find the index of the first src folder, and the label shown in the tree view will be root instead of project. However, we want to locate the closest src folder to SourceFile.lf and use that.

Copy link
Member

Choose a reason for hiding this comment

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

This makes sense depending on whether we want to let the open file dictate the behavior of the editor or whether we want the workspace to do that. Here, you're switching from the latter to the former. We have to make sure this is what we want.

Copy link
Member

Choose a reason for hiding this comment

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

It also leaves unanswered the question of what to do when no file is opened in the editor. Do we look down in case no file is opened and up in case there is an open file?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think the answer to this is here as well

const projectLabel = splittedUri[srcIdx - 1];

const existingProject = this.data.find(item => item.label === projectLabel);
Expand All @@ -598,7 +595,7 @@ export class LFDataProvider implements vscode.TreeDataProvider<LFDataProviderNod
*/
buildLibraryRoot(uri: string, root: LFDataProviderNode, dataNode: LFDataProviderNode): LFDataProviderNode {
const splittedUri = uri.split('/');
const srcIdx = splittedUri.indexOf('src');
const srcIdx = splittedUri.lastIndexOf('src');
const projectLabel = splittedUri[srcIdx - 1];

let lingo = this.findOrCreateSubNode(root, "Lingo Packages", LFDataProviderNodeRole.SUB, LFDataProviderNodeType.LIBRARY, dataNode);
Expand Down Expand Up @@ -703,7 +700,7 @@ export class LFDataProvider implements vscode.TreeDataProvider<LFDataProviderNod
*/
getLibraryPath(path: string) {
const segments = path.split('/');
const srcIndex = segments.indexOf('src');
const srcIndex = segments.lastIndexOf('src');

// Check if the 'src' directory was found and there's at least one segment before it
if (srcIndex === -1 || srcIndex === 0) {
Expand Down Expand Up @@ -764,7 +761,7 @@ export class LFDataProvider implements vscode.TreeDataProvider<LFDataProviderNod
*/
goToLingoTomlCommand(node: LFDataProviderNode) {
const segments = node.uri.fsPath.split('/');
const srcIdx = segments.indexOf('build');
const srcIdx = segments.lastIndexOf('build');
let newUri = segments.slice(0, srcIdx).join('/').concat('/Lingo.toml');
vscode.workspace.openTextDocument(vscode.Uri.parse(newUri)).then(doc => {
vscode.window.showTextDocument(doc);
Expand Down
Loading