Skip to content
This repository has been archived by the owner on Apr 4, 2023. It is now read-only.

Commit

Permalink
An ability to switch (turn on and turn off ) multi-root mode on a dev…
Browse files Browse the repository at this point in the history
…file level

Signed-off-by: Roman Nikitenko <rnikiten@redhat.com>
  • Loading branch information
RomanNikitenko committed Feb 25, 2021
1 parent 1769075 commit 0fc5809
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
import * as fs from 'fs-extra';
import * as path from 'path';

import { Workspace, WorkspaceService } from '@eclipse-che/theia-remote-api/lib/common/workspace-service';
import { inject, injectable } from 'inversify';

import { DefaultWorkspaceServer } from '@theia/workspace/lib/node/default-workspace-server';
import { FileUri } from '@theia/core/lib/node';
import { WorkspaceService } from '@eclipse-che/theia-remote-api/lib/common/workspace-service';

interface TheiaWorkspace {
folders: TheiaWorkspacePath[];
Expand All @@ -33,33 +33,29 @@ export class CheWorkspaceServer extends DefaultWorkspaceServer {
// override any workspace that could have been defined through CLI and use entries from the devfile
// if not possible, use default method
protected async getRoot(): Promise<string | undefined> {
let projectsRoot: string;
if (process.env.CHE_PROJECTS_ROOT) {
projectsRoot = process.env.CHE_PROJECTS_ROOT;
} else {
projectsRoot = '/projects';
const workspace = await this.workspaceService.currentWorkspace();
if (!isMultiRoot(workspace)) {
return super.getRoot();
}

const projectsRootEnvVariable = process.env.CHE_PROJECTS_ROOT;
const projectsRoot = projectsRootEnvVariable ? projectsRootEnvVariable : '/projects';

// first, check if we have a che.theia-workspace file
const cheTheiaWorkspaceFile = path.resolve(projectsRoot, 'che.theia-workspace');
const cheTheiaWorkspaceFileUri = FileUri.create(cheTheiaWorkspaceFile);
const exists = await fs.pathExists(cheTheiaWorkspaceFile);
if (exists) {
return FileUri.create(cheTheiaWorkspaceFile).toString();
}

// no, then create the file

const workspace = await this.workspaceService.currentWorkspace();
const devfile = workspace.devfile;
if (devfile) {
const projects = devfile.projects;
if (projects) {
const theiaWorkspace: TheiaWorkspace = { folders: [] };
await fs.writeFile(cheTheiaWorkspaceFile, JSON.stringify(theiaWorkspace), { encoding: 'utf8' });
return FileUri.create(cheTheiaWorkspaceFile).toString();
}
if (!exists) {
// no, then create the file
const theiaWorkspace: TheiaWorkspace = { folders: [] };
await fs.writeFile(cheTheiaWorkspaceFile, JSON.stringify(theiaWorkspace), { encoding: 'utf8' });
}

return super.getRoot();
return cheTheiaWorkspaceFileUri.toString();
}
}

function isMultiRoot(workspace: Workspace): boolean {
const devfile = workspace.devfile;
return !!devfile && !!devfile.attributes && !!devfile.attributes.multiRoot && devfile.attributes.multiRoot === 'on';
}
3 changes: 2 additions & 1 deletion plugins/task-plugin/src/che-task-backend-module.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**********************************************************************
* Copyright (c) 2019-2020 Red Hat, Inc.
* Copyright (c) 2019-2021 Red Hat, Inc.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -49,6 +49,7 @@ container.bind(ProjectPathVariableResolver).toSelf().inSingletonScope();
container.bind(CheWorkspaceClient).toSelf().inSingletonScope();
container.bind(CheTaskPreviewMode).toSelf().inSingletonScope();
container.bind(PreviewUrlOpenService).toSelf().inSingletonScope();
container.bind(LaunchConfigurationsExporter).toSelf().inSingletonScope();
container.bind<ConfigurationsExporter>(ConfigurationsExporter).to(TaskConfigurationsExporter).inSingletonScope();
container.bind<ConfigurationsExporter>(ConfigurationsExporter).to(LaunchConfigurationsExporter).inSingletonScope();
container.bind(ExportConfigurationsManager).toSelf().inSingletonScope();
Expand Down
29 changes: 11 additions & 18 deletions plugins/task-plugin/src/export/export-configs-manager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**********************************************************************
* Copyright (c) 2019-2020 Red Hat, Inc.
* Copyright (c) 2019-2021 Red Hat, Inc.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
Expand All @@ -8,12 +8,10 @@
* SPDX-License-Identifier: EPL-2.0
***********************************************************************/

import * as startPoint from '../task-plugin-backend';
import * as theia from '@theia/plugin';

import { inject, injectable, multiInject } from 'inversify';

import { CheWorkspaceClient } from '../che-workspace-client';
import { LaunchConfigurationsExporter } from './launch-configs-exporter';
import { che as cheApi } from '@eclipse-che/api';

export const ConfigurationsExporter = Symbol('ConfigurationsExporter');
Expand Down Expand Up @@ -45,26 +43,21 @@ export class ExportConfigurationsManager {
@multiInject(ConfigurationsExporter)
protected readonly exporters: ConfigurationsExporter[];

init(): void {
this.export();
@inject(LaunchConfigurationsExporter)
protected readonly launchConfigurationsExporter: LaunchConfigurationsExporter;

theia.workspace.onDidChangeWorkspaceFolders(
event => {
const workspaceFolders = event.added;
if (workspaceFolders && workspaceFolders.length > 0) {
this.export();
}
},
undefined,
startPoint.getSubscriptions()
);
protected cheCommands: cheApi.workspace.Command[] = [];

async init(): Promise<void> {
this.cheCommands = await this.cheWorkspaceClient.getCommands();
this.launchConfigurationsExporter.init(this.cheCommands);
this.export();
}

async export(): Promise<void> {
const exportPromises = [];
const cheCommands = await this.cheWorkspaceClient.getCommands();
for (const exporter of this.exporters) {
exportPromises.push(this.doExport(cheCommands, exporter));
exportPromises.push(this.doExport(this.cheCommands, exporter));
}

await Promise.all(exportPromises);
Expand Down
23 changes: 19 additions & 4 deletions plugins/task-plugin/src/export/launch-configs-exporter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**********************************************************************
* Copyright (c) 2019-2020 Red Hat, Inc.
* Copyright (c) 2019-2021 Red Hat, Inc.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
Expand All @@ -8,6 +8,7 @@
* SPDX-License-Identifier: EPL-2.0
***********************************************************************/

import * as startPoint from '../task-plugin-backend';
import * as theia from '@theia/plugin';

import { ensureDirExists, modify, writeFile } from '../utils';
Expand All @@ -32,14 +33,28 @@ export class LaunchConfigurationsExporter implements ConfigurationsExporter {
@inject(VsCodeLaunchConfigsExtractor)
protected readonly vsCodeLaunchConfigsExtractor: VsCodeLaunchConfigsExtractor;

async export(commands: cheApi.workspace.Command[]): Promise<void> {
if (!theia.workspace.workspaceFolders) {
async init(commands: cheApi.workspace.Command[]): Promise<void> {
theia.workspace.onDidChangeWorkspaceFolders(
event => {
const workspaceFolders: theia.WorkspaceFolder[] | undefined = event.added;
if (workspaceFolders && workspaceFolders.length > 0) {
this.export(commands, workspaceFolders);
}
},
undefined,
startPoint.getSubscriptions()
);
}

async export(commands: cheApi.workspace.Command[], workspaceFolders?: theia.WorkspaceFolder[]): Promise<void> {
workspaceFolders = workspaceFolders ? workspaceFolders : theia.workspace.workspaceFolders;
if (!workspaceFolders) {
return;
}

const exportConfigsPromises: Promise<void>[] = [];

for (const workspaceFolder of theia.workspace.workspaceFolders) {
for (const workspaceFolder of workspaceFolders) {
exportConfigsPromises.push(this.doExport(workspaceFolder, commands));
}
await Promise.all(exportConfigsPromises);
Expand Down
86 changes: 62 additions & 24 deletions plugins/welcome-plugin/src/welcome-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**********************************************************************
* Copyright (c) 2020 Red Hat, Inc.
* Copyright (c) 2020-2021 Red Hat, Inc.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -53,22 +53,33 @@ async function getHtmlForWebview(context: theia.PluginContext): Promise<string>
}

// Open Readme file is there is one
export async function handleReadmeFiles(roots: theia.WorkspaceFolder[]): Promise<void> {
// In case of only one workspace
if (roots && roots.length === 1) {
const children = await theia.workspace.findFiles('README.md', 'node_modules/**', 1);
const updatedChildren = children.filter((child: theia.Uri) => {
if (child.fsPath.indexOf('node_modules') === -1) {
return child;
}
});
export async function handleReadmeFiles(
readmeHandledCallback?: () => void,
roots?: theia.WorkspaceFolder[]
): Promise<void> {
roots = roots ? roots : theia.workspace.workspaceFolders;
if (!roots || roots.length < 1) {
return;
}

if (updatedChildren.length >= 1) {
const openPath = theia.Uri.parse(updatedChildren[0] + '?open-handler=code-editor-preview');
const doc: theia.TextDocument | undefined = await theia.workspace.openTextDocument(openPath);
if (doc) {
theia.window.showTextDocument(doc);
}
const children = await theia.workspace.findFiles('README.md', 'node_modules/**', 1);
const updatedChildren = children.filter((child: theia.Uri) => {
if (child.fsPath.indexOf('node_modules') === -1) {
return child;
}
});

if (updatedChildren.length < 1) {
return;
}

const openPath = theia.Uri.parse(updatedChildren[0] + '?open-handler=code-editor-preview');
const doc: theia.TextDocument | undefined = await theia.workspace.openTextDocument(openPath);
if (doc) {
theia.window.showTextDocument(doc);

if (readmeHandledCallback) {
readmeHandledCallback();
}
}
}
Expand Down Expand Up @@ -121,15 +132,42 @@ export function start(context: theia.PluginContext): void {
showWelcomePage = configuration.get(Settings.SHOW_WELCOME_PAGE);
}

if (showWelcomePage && theia.window.visibleTextEditors.length === 0) {
setTimeout(async () => {
addPanel(context);

theia.workspace.onDidChangeWorkspaceFolders(event => {
handleReadmeFiles(event.added);
}, context.subscriptions);
}, 100);
if (!showWelcomePage || theia.window.visibleTextEditors.length > 0) {
return;
}

let cloneSourcesDisposable: theia.Disposable | undefined = undefined;
setTimeout(async () => {
addPanel(context);

const workspacePlugin = theia.plugins.getPlugin('Eclipse Che.@eclipse-che/workspace-plugin');
if (workspacePlugin && workspacePlugin.exports) {
// it handles the case when the multi-root mode is OFF
// we should remove this logic when we switch to the multi-root mode is ON by default
cloneSourcesDisposable = workspacePlugin.exports.onDidCloneSources(
() => handleReadmeFiles(readmeHandledCallback),
undefined,
context.subscriptions
);
} else {
handleReadmeFiles();
}
}, 100);

// handles the case when the multi-root mode is ON
const changeWorkspaceFoldersDisposable = theia.workspace.onDidChangeWorkspaceFolders(
event => handleReadmeFiles(readmeHandledCallback, event.added),
undefined,
context.subscriptions
);

const readmeHandledCallback = () => {
changeWorkspaceFoldersDisposable.dispose();

if (cloneSourcesDisposable) {
cloneSourcesDisposable.dispose();
}
};
}

export function stop(): void {}
31 changes: 14 additions & 17 deletions plugins/workspace-plugin/src/theia-commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ function isDevfileProjectConfig(
}

export interface TheiaImportCommand {
execute(): Promise<void>;
getProjectPath(): string;
/** @returns the path to the imported project */
execute(): Promise<string>;
}

export function buildProjectImportCommand(
Expand Down Expand Up @@ -120,11 +120,7 @@ export class TheiaGitCloneCommand implements TheiaImportCommand {
this.projectsRoot = projectsRoot;
}

getProjectPath(): string {
return this.projectPath;
}

clone(): PromiseLike<void> {
clone(): PromiseLike<string> {
return theia.window.withProgress(
{
location: theia.ProgressLocation.Notification,
Expand All @@ -140,7 +136,7 @@ export class TheiaGitCloneCommand implements TheiaImportCommand {
);
}

async execute(): Promise<void> {
async execute(): Promise<string> {
if (!git.isSecureGitURI(this.locationURI)) {
// clone using regular URI
return this.clone();
Expand Down Expand Up @@ -208,7 +204,7 @@ export class TheiaGitCloneCommand implements TheiaImportCommand {
continue;
}
// skip
return;
return Promise.reject(new Error(message));
}

// pause will be removed after debugging this method
Expand All @@ -228,7 +224,7 @@ export class TheiaGitCloneCommand implements TheiaImportCommand {
private async gitClone(
progress: theia.Progress<{ message?: string; increment?: number }>,
token: theia.CancellationToken
): Promise<void> {
): Promise<string> {
const args: string[] = ['clone', this.locationURI, this.projectPath];
if (this.checkoutBranch) {
args.push('--branch');
Expand Down Expand Up @@ -265,17 +261,19 @@ export class TheiaGitCloneCommand implements TheiaImportCommand {
} else {
theia.window.showInformationMessage(`${messageStart}.`);
}
return this.projectPath;
} catch (e) {
theia.window.showErrorMessage(`Couldn't clone ${this.locationURI}: ${e.message}`);
console.log(`Couldn't clone ${this.locationURI}`, e);
throw new Error(e);
}
}

// Gets only specified directory from given repository
private async gitSparseCheckout(
progress: theia.Progress<{ message?: string; increment?: number }>,
token: theia.CancellationToken
): Promise<void> {
): Promise<string> {
if (!this.sparseCheckoutDir) {
throw new Error('Parameter "sparseCheckoutDir" is not set for "' + this.projectName + '" project.');
}
Expand All @@ -296,6 +294,7 @@ export class TheiaGitCloneCommand implements TheiaImportCommand {
theia.window.showInformationMessage(
`Sources by template ${this.sparseCheckoutDir} of ${this.locationURI} was cloned to ${this.projectPath}.`
);
return this.projectPath;
}
}

Expand All @@ -321,15 +320,11 @@ export class TheiaImportZipCommand implements TheiaImportCommand {
}
}

getProjectPath(): string {
return this.projectDir;
}

async execute(): Promise<void> {
async execute(): Promise<string> {
const importZip = async (
progress: theia.Progress<{ message?: string; increment?: number }>,
token: theia.CancellationToken
): Promise<void> => {
): Promise<string> => {
try {
// download
const curlArgs = ['-sSL', '--output', this.zipfilePath];
Expand All @@ -354,9 +349,11 @@ export class TheiaImportZipCommand implements TheiaImportCommand {
if (zipfileParentDir.indexOf(os.tmpdir() + path.sep) === 0) {
fs.rmdirSync(zipfileParentDir);
}
return this.projectDir;
} catch (e) {
theia.window.showErrorMessage(`Couldn't import ${this.locationURI}: ${e.message}`);
console.error(`Couldn't import ${this.locationURI}`, e);
throw new Error(e);
}
};

Expand Down
Loading

0 comments on commit 0fc5809

Please sign in to comment.