item);
+ }
+ }),
+ input.onDidChangeSelection(items => resolve(items[0])),
+ input.onDidHide(() => {
+ (async () => {
+ reject(shouldResume && await shouldResume() ? InputFlowAction.resume : InputFlowAction.cancel);
+ })()
+ .catch(reject);
+ })
+ );
+ if (this.current) {
+ this.current.dispose();
+ }
+ this.current = input;
+ this.current.show();
if (activeItem) {
- input.activeItems = [activeItem];
- }
- });
- } finally {
- disposables.forEach(d => d.dispose());
- }
- }
-
- async showInputBox({ title, step, totalSteps, value, prompt, validate, buttons, ignoreFocusOut, placeholder, shouldResume }: P) {
- const disposables: Disposable[] = [];
- try {
- return await new Promise((resolve, reject) => {
- const input = window.createInputBox();
- input.title = title;
- input.step = step;
- input.totalSteps = totalSteps;
- input.value = value || '';
- input.prompt = prompt;
- input.ignoreFocusOut = ignoreFocusOut ?? false;
- input.placeholder = placeholder;
- input.buttons = [
- ...(this.steps.length > 1 ? [QuickInputButtons.Back] : []),
- ...(buttons || [])
- ];
- let validating = validate('');
- disposables.push(
- input.onDidTriggerButton(item => {
- if (item === QuickInputButtons.Back) {
- reject(InputFlowAction.back);
- } else {
- resolve(item);
- }
- }),
- input.onDidAccept(async () => {
- const value = input.value;
- input.enabled = false;
- input.busy = true;
- if (!(await validate(value))) {
- resolve(value);
- }
- input.enabled = true;
- input.busy = false;
- }),
- input.onDidChangeValue(async text => {
- const current = validate(text);
- validating = current;
- const validationMessage = await current;
- if (current === validating) {
- input.validationMessage = validationMessage;
- }
- }),
- input.onDidHide(() => {
- (async () => {
- reject(shouldResume && await shouldResume() ? InputFlowAction.resume : InputFlowAction.cancel);
- })()
- .catch(reject);
- })
- );
- if (this.current) {
- this.current.dispose();
- }
- this.current = input;
- this.current.show();
- });
- } finally {
- disposables.forEach(d => d.dispose());
- }
- }
+ input.activeItems = [activeItem];
+ }
+ });
+ } finally {
+ disposables.forEach(d => d.dispose());
+ }
+ }
+
+ async showInputBox({ title, step, totalSteps, value, prompt, validate, buttons, ignoreFocusOut, placeholder, shouldResume }: P) {
+ const disposables: Disposable[] = [];
+ try {
+ return await new Promise((resolve, reject) => {
+ const input = window.createInputBox();
+ input.title = title;
+ input.step = step;
+ input.totalSteps = totalSteps;
+ input.value = value || '';
+ input.prompt = prompt;
+ input.ignoreFocusOut = ignoreFocusOut ?? false;
+ input.placeholder = placeholder;
+ input.buttons = [
+ ...(this.steps.length > 1 ? [QuickInputButtons.Back] : []),
+ ...(buttons || [])
+ ];
+ let validating = validate ? validate('') : Promise.resolve(undefined);
+ disposables.push(
+ input.onDidTriggerButton(item => {
+ if (item === QuickInputButtons.Back) {
+ reject(InputFlowAction.back);
+ } else {
+ resolve(item);
+ }
+ }),
+ input.onDidAccept(async () => {
+ const value = input.value;
+ input.enabled = false;
+ input.busy = true;
+ if (!(validate && await validate(value))) {
+ resolve(value);
+ }
+ input.enabled = true;
+ input.busy = false;
+ }),
+ input.onDidChangeValue(async text => {
+ if (validate) {
+ const current = validate(text);
+ validating = current;
+ const validationMessage = await current;
+ if (current === validating) {
+ input.validationMessage = validationMessage;
+ }
+ }
+ }),
+ input.onDidHide(() => {
+ (async () => {
+ reject(shouldResume && await shouldResume() ? InputFlowAction.resume : InputFlowAction.cancel);
+ })()
+ .catch(reject);
+ })
+ );
+ if (this.current) {
+ this.current.dispose();
+ }
+ this.current = input;
+ this.current.show();
+ });
+ } finally {
+ disposables.forEach(d => d.dispose());
+ }
+ }
}
\ No newline at end of file
diff --git a/vscode/microsoft-kiota/src/util.ts b/vscode/microsoft-kiota/src/util.ts
new file mode 100644
index 0000000000..86c2a2e4ef
--- /dev/null
+++ b/vscode/microsoft-kiota/src/util.ts
@@ -0,0 +1,95 @@
+import * as vscode from 'vscode';
+import * as path from 'path';
+import * as fs from 'fs';
+import { APIMANIFEST, CLIENT, CLIENTS, KIOTA_DIRECTORY, KIOTA_WORKSPACE_FILE, PLUGIN, PLUGINS } from './constants';
+import { migrateFromLockFile, displayMigrationMessages } from './migrateFromLockFile';
+
+const clientTypes = [CLIENT, CLIENTS];
+const pluginTypes = [PLUGIN, PLUGINS, APIMANIFEST];
+
+export function isClientType(type: string): boolean {
+ return clientTypes.includes(type);
+}
+
+export function isPluginType(type: string): boolean {
+ return pluginTypes.includes(type);
+}
+
+export async function updateTreeViewIcons(treeViewId: string, showIcons: boolean, showRegenerateIcon: boolean = false) {
+ await vscode.commands.executeCommand('setContext', `${treeViewId}.showIcons`, showIcons);
+ await vscode.commands.executeCommand('setContext', `${treeViewId}.showRegenerateIcon`, showRegenerateIcon);
+}
+
+export function getWorkspaceJsonPath(): string {
+ return path.join(getWorkspaceJsonDirectory(),KIOTA_DIRECTORY, KIOTA_WORKSPACE_FILE);
+};
+
+export function getWorkspaceJsonDirectory(clientNameOrPluginName?: string): string {
+ const baseDir = path.join(
+ vscode.workspace.workspaceFolders && vscode.workspace.workspaceFolders.length > 0
+ ? vscode.workspace.workspaceFolders[0].uri.fsPath
+ : process.env.HOME ?? process.env.USERPROFILE ?? process.cwd()
+ );
+
+ let workspaceFolder = !vscode.workspace.workspaceFolders || vscode.workspace.workspaceFolders.length === 0
+ ? path.join(baseDir, 'kiota', clientNameOrPluginName ?? '')
+ : baseDir;
+
+ if (!fs.existsSync(workspaceFolder)) {
+ fs.mkdirSync(workspaceFolder, { recursive: true });
+ }
+ return workspaceFolder;
+}
+
+//used to store output in the App Package directory in the case where the workspace is a Teams Toolkit Project
+export function findAppPackageDirectory(directory: string): string | null {
+ if (!fs.existsSync(directory)) {
+ return null;
+ }
+
+ const entries = fs.readdirSync(directory, { withFileTypes: true });
+
+ for (const entry of entries) {
+ const fullPath = path.join(directory, entry.name);
+
+ if (entry.isDirectory()) {
+ if (entry.name === 'appPackage') {
+ return fullPath;
+ }
+
+ const subDirectory = findAppPackageDirectory(fullPath);
+ if (subDirectory) {
+ return subDirectory;
+ }
+ }
+ }
+
+ return null;
+}
+
+export async function handleMigration(
+ context: vscode.ExtensionContext,
+ workspaceFolder: vscode.WorkspaceFolder
+): Promise {
+ vscode.window.withProgress({
+ location: vscode.ProgressLocation.Notification,
+ title: vscode.l10n.t("Migrating your API clients..."),
+ cancellable: false
+ }, async (progress) => {
+ progress.report({ increment: 0 });
+
+ try {
+ const migrationResult = await migrateFromLockFile(context, workspaceFolder.uri.fsPath);
+
+ progress.report({ increment: 100 });
+
+ if (migrationResult && migrationResult.length > 0) {
+ displayMigrationMessages(migrationResult);
+ } else {
+ vscode.window.showWarningMessage(vscode.l10n.t("Migration completed, but no changes were detected."));
+ }
+ } catch (error) {
+ vscode.window.showErrorMessage(vscode.l10n.t(`Migration failed: ${error}`));
+ }
+ });
+}
\ No newline at end of file
diff --git a/vscode/microsoft-kiota/src/workspaceTreeProvider.ts b/vscode/microsoft-kiota/src/workspaceTreeProvider.ts
new file mode 100644
index 0000000000..e97a32d47b
--- /dev/null
+++ b/vscode/microsoft-kiota/src/workspaceTreeProvider.ts
@@ -0,0 +1,71 @@
+import * as vscode from 'vscode';
+import * as path from 'path';
+import * as fs from 'fs';
+import { KIOTA_WORKSPACE_FILE } from './constants';
+import { getWorkspaceJsonPath } from './util';
+
+
+export class WorkspaceTreeProvider implements vscode.TreeDataProvider {
+ public isWSPresent: boolean;
+ private _onDidChangeTreeData: vscode.EventEmitter = new vscode.EventEmitter();
+ readonly onDidChangeTreeData: vscode.Event = this._onDidChangeTreeData.event;
+ constructor( isWSPresent: boolean) {
+ this.isWSPresent = isWSPresent;
+ }
+
+ async refreshView(): Promise {
+ this._onDidChangeTreeData.fire();
+}
+
+ async getChildren(element?: vscode.TreeItem): Promise {
+ if (!this.isWSPresent) {
+ return [];
+ }
+ if (!element) {
+ return [new vscode.TreeItem(KIOTA_WORKSPACE_FILE, vscode.TreeItemCollapsibleState.None)];
+ }
+ return [];
+ }
+
+ getTreeItem(element: vscode.TreeItem): vscode.TreeItem {
+ if (element) {
+ element.command = {
+ command: 'kiota.workspace.openWorkspaceFile',
+ title: vscode.l10n.t("Open File"),
+ arguments: [vscode.Uri.file(getWorkspaceJsonPath())]
+ };
+ element.contextValue = 'file';
+ }
+ return element;
+ }
+}
+
+async function openResource(resource: vscode.Uri): Promise {
+ await vscode.window.showTextDocument(resource);
+}
+async function isKiotaWorkspaceFilePresent(): Promise {
+ if(!vscode.workspace.workspaceFolders || vscode.workspace.workspaceFolders.length === 0){
+ return false;
+ }
+ const workspaceFileDir = path.resolve(getWorkspaceJsonPath());
+ try {
+ await fs.promises.access(workspaceFileDir);
+ } catch (error) {
+ return false;
+ }
+ return true;
+}
+
+export async function loadTreeView(context: vscode.ExtensionContext): Promise {
+ const treeDataProvider = new WorkspaceTreeProvider(await isKiotaWorkspaceFilePresent());
+ context.subscriptions.push(vscode.workspace.onDidChangeWorkspaceFolders(async () => {
+ treeDataProvider.isWSPresent = await isKiotaWorkspaceFilePresent();
+ await vscode.commands.executeCommand('kiota.workspace.refresh'); // Refresh the tree view when workspace folders change
+ }));
+ context.subscriptions.push(vscode.window.createTreeView('kiota.workspace', { treeDataProvider }));
+ context.subscriptions.push(vscode.commands.registerCommand('kiota.workspace.openWorkspaceFile', openResource));
+ context.subscriptions.push(vscode.commands.registerCommand('kiota.workspace.refresh', async () => {
+ treeDataProvider.isWSPresent = await isKiotaWorkspaceFilePresent();
+ await treeDataProvider.refreshView();
+ }));
+}