[vscode-spring-cli]: boot new command support

This commit is contained in:
aboyko
2024-01-17 17:02:15 -05:00
parent dce68a0f2a
commit ce45532748
5 changed files with 91 additions and 92 deletions

View File

@@ -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<void>((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<string> {
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);
}

View File

@@ -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<void> {
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<void> {
return vscode.window.withProgress({
location: vscode.ProgressLocation.Window,
cancellable: true,
title
}, (progress, cancellation) => {
progress.report({message});
return new Promise<void>(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();
}
});
});
});
}
}

View File

@@ -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;

View File

@@ -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<void> {
vscode.commands.registerCommand('vscode-spring-cli.new-project', () => {
handleCommand({});
handleNew({});
});
}

View File

@@ -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<Uri> {
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<string> {
return new Promise<string>((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();
});
}