From ce45532748e107c4fbbc0042b252f0fe19c5f85d Mon Sep 17 00:00:00 2001 From: aboyko Date: Wed, 17 Jan 2024 17:02:15 -0500 Subject: [PATCH] [vscode-spring-cli]: boot new command support --- .../src/boot-project/project-new.ts | 78 ------------------- .../vscode-spring-cli/src/cli/core.ts | 66 +++++++++++++--- .../vscode-spring-cli/src/cli/types.ts | 6 +- .../vscode-spring-cli/src/extension.ts | 4 +- .../vscode-spring-cli/src/utils.ts | 29 ++++++- 5 files changed, 91 insertions(+), 92 deletions(-) delete mode 100644 vscode-extensions/vscode-spring-cli/src/boot-project/project-new.ts diff --git a/vscode-extensions/vscode-spring-cli/src/boot-project/project-new.ts b/vscode-extensions/vscode-spring-cli/src/boot-project/project-new.ts deleted file mode 100644 index 55b554b91..000000000 --- a/vscode-extensions/vscode-spring-cli/src/boot-project/project-new.ts +++ /dev/null @@ -1,78 +0,0 @@ -import * as path from "path"; -import { CliNewProjectMetadata, CliProjectType } from "../cli/types"; -import { CLI } from "../extension"; -import { openDialogForFolder } from "../utils"; -import vscode, { InputBox, QuickPickItem, QuickPickItemKind } from 'vscode'; - -const OPEN_IN_NEW_WORKSPACE = "Open"; -const OPEN_IN_CURRENT_WORKSPACE = "Add to Workspace"; - -export async function handleCommand(metadata: CliNewProjectMetadata) { - if (!metadata.catalogType) { - const item = await vscode.window.showQuickPick(CLI.getProjectTypes().map(mapProjectTypetoQuickPick), { canPickMany: false }); - if (item) { - metadata.catalogType = item.label; - } - } - - await new Promise((resolve, reject) => { - const inputBox: InputBox = vscode.window.createInputBox(); - inputBox.title = 'Project Name'; - inputBox.placeholder = metadata.name || metadata.catalogType; - inputBox.prompt = 'Enter project name'; - inputBox.value = metadata.name; - inputBox.ignoreFocusOut = true; - inputBox.onDidAccept(() => { - metadata.name = inputBox.value; - inputBox.hide(); - }); - inputBox.onDidHide(() => resolve()); - inputBox.show(); - }); - - metadata.rootPackage = 'com.example.demo'; - - metadata.targetFolder = (await openDialogForFolder({})).fsPath; - - if (metadata.name && metadata.catalogType && metadata.targetFolder) { - vscode.window.showInformationMessage(`Folder="${metadata.targetFolder}" Command="spring boot new ${metadata.name} ${metadata.catalogType}"`) - CLI.createProject(metadata); - - // Open project either is the same workspace or new workspace - const hasOpenFolder = vscode.workspace.workspaceFolders !== undefined || vscode.workspace.rootPath !== undefined; - - const pathToOpen = path.resolve(metadata.targetFolder, metadata.name); - - // Don't prompt to open projectLocation if it's already a currently opened folder - if (hasOpenFolder && (vscode.workspace.workspaceFolders.some(folder => folder.uri.fsPath === pathToOpen) || vscode.workspace.rootPath === pathToOpen)) { - return; - } - const choice = await specifyOpenMethod(hasOpenFolder, vscode.Uri.file(pathToOpen)) - - if (choice === OPEN_IN_NEW_WORKSPACE) { - vscode.commands.executeCommand("vscode.openFolder", vscode.Uri.file(pathToOpen), hasOpenFolder); - } else if (choice === OPEN_IN_CURRENT_WORKSPACE) { - if (!vscode.workspace.workspaceFolders.find((workspaceFolder) => workspaceFolder.uri && pathToOpen.startsWith(workspaceFolder.uri.fsPath))) { - vscode.workspace.updateWorkspaceFolders(vscode.workspace.workspaceFolders.length, null, { uri: vscode.Uri.file(pathToOpen) }); - } - } - } - -} - -function mapProjectTypetoQuickPick(metadata: CliProjectType): QuickPickItem { - return { - label: metadata.id, - kind: QuickPickItemKind.Default, - description: metadata.description, - detail: metadata.tags.toString() - }; -} - -async function specifyOpenMethod(hasOpenFolder: boolean, projectLocation: vscode.Uri): Promise { - const candidates: string[] = [ - OPEN_IN_NEW_WORKSPACE, - hasOpenFolder ? OPEN_IN_CURRENT_WORKSPACE : undefined, - ].filter(Boolean); - return await vscode.window.showInformationMessage(`Successfully generated. Location: ${projectLocation.fsPath}`, ...candidates); -} \ No newline at end of file diff --git a/vscode-extensions/vscode-spring-cli/src/cli/core.ts b/vscode-extensions/vscode-spring-cli/src/cli/core.ts index ee2efbeee..cb55823c3 100644 --- a/vscode-extensions/vscode-spring-cli/src/cli/core.ts +++ b/vscode-extensions/vscode-spring-cli/src/cli/core.ts @@ -1,9 +1,11 @@ -import cp from 'child_process'; -import { CliNewProjectMetadata, CliProjectType } from "./types"; +import { BootNewProjectMetadata, ProjectType } from "./types"; +import vscode from "vscode"; + +const SPRING_CLI_TASK_TYPE = 'spring-cli'; export class Cli { - getProjectTypes() : CliProjectType[] { + getProjectTypes() : ProjectType[] { return [ { id: 'web', @@ -29,15 +31,61 @@ export class Cli { ]; } - createProject(metadata: CliNewProjectMetadata) { - this.exec(metadata.targetFolder, `spring boot new ${metadata.name} ${metadata.catalogType}`) + createBootProject(metadata: BootNewProjectMetadata): Promise { + const args = [ + "boot", + "new", + "--name", + `"${metadata.name}"`, + "--from", + `"${metadata.catalogType}"` + ]; + if (metadata.groupId) { + args.push("--group-id") + args.push(`"${metadata.groupId}"`); + } + if (metadata.artifactId) { + args.push("--artifact-id") + args.push(`"${metadata.artifactId}"`); + } + if (metadata.rootPackage) { + args.push("--package-name"); + args.push(`"${metadata.rootPackage}"`); + } + return this.exec('Create Spring Boot project', `'${metadata.catalogType}'`, metadata.targetFolder, args); } - private exec(cwd: string, cmd: string) { - cp.execSync(cmd, { - cwd + private async exec(title: string, message: string, cwd: string, args: string[]): Promise { + + return vscode.window.withProgress({ + location: vscode.ProgressLocation.Window, + cancellable: true, + title + }, (progress, cancellation) => { + + progress.report({message}); + + return new Promise(async (resolve, reject) => { + const process = new vscode.ProcessExecution('spring', args, { cwd }); + const task = new vscode.Task({ type: SPRING_CLI_TASK_TYPE}, vscode.workspace.getWorkspaceFolder(vscode.Uri.file(cwd)), `${title}: ${message}`, SPRING_CLI_TASK_TYPE, process); + const taskExecution = await vscode.tasks.executeTask(task); + if (cancellation.isCancellationRequested) { + reject(); + } + const cancelListener = cancellation.onCancellationRequested(() => { + cancelListener.dispose(); + reject(); + }) + const listener = vscode.tasks.onDidEndTaskProcess(e => { + if (e.execution === taskExecution) { + listener.dispose(); + resolve(); + } + }); + }); + }); } - + } diff --git a/vscode-extensions/vscode-spring-cli/src/cli/types.ts b/vscode-extensions/vscode-spring-cli/src/cli/types.ts index c29157201..be926e143 100644 --- a/vscode-extensions/vscode-spring-cli/src/cli/types.ts +++ b/vscode-extensions/vscode-spring-cli/src/cli/types.ts @@ -1,4 +1,4 @@ -export interface CliProjectType { +export interface ProjectType { id: string; description?: string; url?: string; @@ -6,8 +6,10 @@ export interface CliProjectType { tags?: string[] } -export interface CliNewProjectMetadata { +export interface BootNewProjectMetadata { name?: string; + groupId?: string; + artifactId?: string; catalogType?: string; rootPackage?: string; targetFolder?: string; diff --git a/vscode-extensions/vscode-spring-cli/src/extension.ts b/vscode-extensions/vscode-spring-cli/src/extension.ts index 32e07a74b..f25fa2b8e 100644 --- a/vscode-extensions/vscode-spring-cli/src/extension.ts +++ b/vscode-extensions/vscode-spring-cli/src/extension.ts @@ -4,13 +4,13 @@ "use strict"; import * as vscode from "vscode"; import { Cli } from "./cli/core"; -import { handleCommand } from "./boot-project/project-new"; +import { handleNew } from "./boot-project/boot-project"; export const CLI = new Cli(); export async function activate(context: vscode.ExtensionContext): Promise { vscode.commands.registerCommand('vscode-spring-cli.new-project', () => { - handleCommand({}); + handleNew({}); }); } diff --git a/vscode-extensions/vscode-spring-cli/src/utils.ts b/vscode-extensions/vscode-spring-cli/src/utils.ts index c7eb47dd2..aa69e8be9 100644 --- a/vscode-extensions/vscode-spring-cli/src/utils.ts +++ b/vscode-extensions/vscode-spring-cli/src/utils.ts @@ -1,4 +1,4 @@ -import { OpenDialogOptions, Uri, window, workspace } from "vscode"; +import { InputBox, OpenDialogOptions, Uri, window, workspace } from "vscode"; export async function openDialogForFolder(customOptions: OpenDialogOptions): Promise { const options: OpenDialogOptions = { @@ -15,3 +15,30 @@ export async function openDialogForFolder(customOptions: OpenDialogOptions): Pro return Promise.resolve(undefined); } } + +export function enterText(opts: { + title: string, + prompt: string, + validate?: (v: string) => string | undefined, + defaultValue?: string, + placeholder?: string +}): Promise { + return new Promise((resolve) => { + const inputBox: InputBox = window.createInputBox(); + inputBox.title = opts.title; + inputBox.placeholder = opts.placeholder; + inputBox.prompt = opts.prompt; + inputBox.value = opts.defaultValue; + inputBox.ignoreFocusOut = true; + inputBox.onDidChangeValue(() => { + inputBox.validationMessage = opts.validate ? opts.validate(inputBox.value) : undefined; + }); + inputBox.onDidAccept(() => { + if (!inputBox.validationMessage) { + inputBox.hide(); + } + }); + inputBox.onDidHide(() => resolve(inputBox.value)); + inputBox.show(); + }); +}