[vscode-spring-cli]: project add and remove commands
This commit is contained in:
@@ -61,6 +61,16 @@
|
||||
"command": "vscode-spring-cli.project-catalog.remove",
|
||||
"title": "Remove Project Catalog",
|
||||
"category": "Spring CLI"
|
||||
},
|
||||
{
|
||||
"command": "vscode-spring-cli.project.add",
|
||||
"title": "Add Project",
|
||||
"category": "Spring CLI"
|
||||
},
|
||||
{
|
||||
"command": "vscode-spring-cli.project.remove",
|
||||
"title": "Remove Project",
|
||||
"category": "Spring CLI"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as path from "path";
|
||||
import { BootAddMetadata, BootNewMetadata, Project } from "./cli-types";
|
||||
import { CLI } from "./extension";
|
||||
import { enterText, getTargetPomXml, openDialogForFolder } from "./utils";
|
||||
import { enterText, getTargetPomXml, mapProjectToQuickPick, openDialogForFolder } from "./utils";
|
||||
import vscode, { QuickPickItem, QuickPickItemKind } from 'vscode';
|
||||
import fs from 'fs'
|
||||
|
||||
@@ -19,7 +19,7 @@ export async function handleBootAdd(pom?: vscode.Uri): Promise<void> {
|
||||
return;
|
||||
}
|
||||
if (!metadata.catalogType) {
|
||||
metadata.catalogType = (await vscode.window.showQuickPick(CLI.projectList().map(mapProjectTypetoQuickPick), { canPickMany: false }))?.label;
|
||||
metadata.catalogType = (await vscode.window.showQuickPick(CLI.projectList().then(ps => ps.map(mapProjectToQuickPick)), { canPickMany: false, ignoreFocusOut: true }))?.label;
|
||||
}
|
||||
return CLI.bootAdd(metadata);
|
||||
}
|
||||
@@ -31,43 +31,54 @@ export async function handleBootNew(targetFolder?: string): Promise<void> {
|
||||
metadata.targetFolder = targetFolder || (await openDialogForFolder({title: "Select Parent Folder"})).fsPath;
|
||||
|
||||
// Select project type from the list of available types
|
||||
metadata.catalogId = (await vscode.window.showQuickPick(CLI.projectList().map(mapProjectTypetoQuickPick), { canPickMany: false }))?.label;
|
||||
metadata.catalogId = (await vscode.window.showQuickPick(CLI.projectList().then(ps => ps.map(mapProjectToQuickPick)), { canPickMany: false }))?.label;
|
||||
|
||||
metadata.name = await enterText({
|
||||
title: "Project Name",
|
||||
prompt: "Enter Project Name",
|
||||
defaultValue: metadata.name || metadata.catalogId,
|
||||
validate: value => {
|
||||
if (!/^[a-z_][a-z0-9_]*(-[a-z_][a-z0-9_]*)*$/.test(value)) {
|
||||
return "Invalid Project Name";
|
||||
if (!metadata.catalogId) {
|
||||
// Cancelled
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
metadata.name = await enterText({
|
||||
title: "Project Name",
|
||||
prompt: "Enter Project Name",
|
||||
defaultValue: metadata.name || metadata.catalogId,
|
||||
validate: value => {
|
||||
if (!/^[a-z_][a-z0-9_]*(-[a-z_][a-z0-9_]*)*$/.test(value)) {
|
||||
return "Invalid Project Name";
|
||||
}
|
||||
if (fs.existsSync(path.resolve(metadata.targetFolder, value))) {
|
||||
return "Folder or file with such name exists under selected parent folder";
|
||||
}
|
||||
}
|
||||
if (fs.existsSync(path.resolve(metadata.targetFolder, value))) {
|
||||
return "Folder or file with such name exists under selected parent folder";
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
metadata.artifactId = await enterText({
|
||||
title: "Artifact Id",
|
||||
prompt: "Enter Artifact Id",
|
||||
defaultValue: metadata.name,
|
||||
validate: value => (/^[a-z_][a-z0-9_]*(-[a-z_][a-z0-9_]*)*$/.test(value)) ? undefined : "Invalid Artifact Id"
|
||||
});
|
||||
|
||||
metadata.artifactId = await enterText({
|
||||
title: "Artifact Id",
|
||||
prompt: "Enter Artifact Id",
|
||||
defaultValue: metadata.name,
|
||||
validate: value => (/^[a-z_][a-z0-9_]*(-[a-z_][a-z0-9_]*)*$/.test(value)) ? undefined : "Invalid Artifact Id"
|
||||
});
|
||||
metadata.groupId = await enterText({
|
||||
title: "Group Id",
|
||||
prompt: "Enter Group Id",
|
||||
defaultValue: "com.example",
|
||||
validate: value => (/^[a-z_][a-z0-9_]*(\.[a-z0-9_]+)*$/.test(value)) ? undefined : "Invalid Group Id"
|
||||
});
|
||||
|
||||
// Root package name
|
||||
metadata.rootPackage = await enterText({
|
||||
title: "Root Package Name",
|
||||
prompt: "Enter Root Package Name",
|
||||
defaultValue: `${metadata.groupId}.${metadata.name.replace("-", ".")}`,
|
||||
validate: value => (/^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+[0-9a-z_]$/.test(value)) ? undefined : "Invalid Package Name"
|
||||
});
|
||||
} catch (error) {
|
||||
// Cancelled
|
||||
return;
|
||||
}
|
||||
|
||||
metadata.groupId = await enterText({
|
||||
title: "Group Id",
|
||||
prompt: "Enter Group Id",
|
||||
defaultValue: "com.example",
|
||||
validate: value => (/^[a-z_][a-z0-9_]*(\.[a-z0-9_]+)*$/.test(value)) ? undefined : "Invalid Group Id"
|
||||
});
|
||||
|
||||
// Root package name
|
||||
metadata.rootPackage = await enterText({
|
||||
title: "Root Package Name",
|
||||
prompt: "Enter Root Package Name",
|
||||
defaultValue: `${metadata.groupId}.${metadata.name.replace("-", ".")}`,
|
||||
validate: value => (/^[a-z][a-z0-9_]*(\.[a-z0-9_]+)+[0-9a-z_]$/.test(value)) ? undefined : "Invalid Package Name"
|
||||
});
|
||||
|
||||
// Create project and open in the workspace or new window
|
||||
if (metadata.name && metadata.catalogId && metadata.targetFolder) {
|
||||
@@ -95,14 +106,6 @@ export async function handleBootNew(targetFolder?: string): Promise<void> {
|
||||
|
||||
}
|
||||
|
||||
function mapProjectTypetoQuickPick(metadata: Project): 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[] = [
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
export interface Project {
|
||||
id: string;
|
||||
name: string;
|
||||
url: string;
|
||||
description?: string;
|
||||
catalogId?: string;
|
||||
catalog?: string;
|
||||
tags?: string[]
|
||||
}
|
||||
|
||||
|
||||
@@ -5,30 +5,30 @@ const SPRING_CLI_TASK_TYPE = 'spring-cli';
|
||||
|
||||
export class Cli {
|
||||
|
||||
projectList() : Project[] {
|
||||
return [
|
||||
projectList() : Thenable<Project[]> {
|
||||
return Promise.resolve([
|
||||
{
|
||||
id: 'web',
|
||||
name: 'web',
|
||||
description: 'Hello, World RESTful web service.',
|
||||
url: 'https://github.com/rd-1-2022/rest-service',
|
||||
catalogId: 'gs',
|
||||
catalog: 'gs',
|
||||
tags: ['java-17', 'boot-3.1.x', 'rest', 'web']
|
||||
},
|
||||
{
|
||||
id: 'jpa',
|
||||
name: 'jpa',
|
||||
description: 'Learn how to work with JPA data persistence using Spring Data JPA.',
|
||||
url: 'https://github.com/rd-1-2022/rpt-spring-data-jpa',
|
||||
catalogId: 'gs',
|
||||
catalog: 'gs',
|
||||
tags: ['java-17', 'boot-3.1.x', 'jpa', 'h2']
|
||||
},
|
||||
{
|
||||
id: 'scheduling',
|
||||
name: 'scheduling',
|
||||
description: 'How to schedule tasks',
|
||||
url: 'https://github.com/rd-1-2022/rpt-spring-scheduling-tasks',
|
||||
catalogId: 'gs',
|
||||
catalog: 'gs',
|
||||
tags: ['scheduling']
|
||||
}
|
||||
];
|
||||
]);
|
||||
}
|
||||
|
||||
projectCatalogList(): Thenable<ProjectCatalog[]> {
|
||||
@@ -82,7 +82,7 @@ export class Cli {
|
||||
args.push("--tags");
|
||||
args.push(catalog.tags.join(","));
|
||||
}
|
||||
return this.exec("Add Project Catalog", `"${catalog.name}"`, args);
|
||||
return this.exec("Add Project Catalog", `'${catalog.name}'`, args);
|
||||
}
|
||||
|
||||
projectCatalogRemove(name: string): Promise<void> {
|
||||
@@ -92,7 +92,37 @@ export class Cli {
|
||||
"--name",
|
||||
name,
|
||||
];
|
||||
return this.exec("Remove Project Catalog", `"${name}"`, args);
|
||||
return this.exec("Remove Project Catalog", `'${name}'`, args);
|
||||
}
|
||||
|
||||
projectAdd(project: Project) {
|
||||
const args = [
|
||||
"project",
|
||||
"add",
|
||||
"--name",
|
||||
project.name,
|
||||
"--url",
|
||||
project.url,
|
||||
];
|
||||
if (project.description) {
|
||||
args.push("--description");
|
||||
args.push(project.description);
|
||||
}
|
||||
if (project.tags) {
|
||||
args.push("--tags");
|
||||
args.push(project.tags.join(","));
|
||||
}
|
||||
return this.exec("Add Project", `'${project.name}'`, args);
|
||||
}
|
||||
|
||||
projectRemove(name: string) {
|
||||
const args = [
|
||||
"project",
|
||||
"remove",
|
||||
"--name",
|
||||
name
|
||||
];
|
||||
return this.exec("Remove Project", `'${name}'`, args);
|
||||
}
|
||||
|
||||
bootNew(metadata: BootNewMetadata): Promise<void> {
|
||||
@@ -100,21 +130,21 @@ export class Cli {
|
||||
"boot",
|
||||
"new",
|
||||
"--name",
|
||||
`"${metadata.name}"`,
|
||||
metadata.name,
|
||||
"--from",
|
||||
`"${metadata.catalogId}"`
|
||||
metadata.catalogId
|
||||
];
|
||||
if (metadata.groupId) {
|
||||
args.push("--group-id")
|
||||
args.push(`"${metadata.groupId}"`);
|
||||
args.push(metadata.groupId);
|
||||
}
|
||||
if (metadata.artifactId) {
|
||||
args.push("--artifact-id")
|
||||
args.push(`"${metadata.artifactId}"`);
|
||||
args.push(metadata.artifactId);
|
||||
}
|
||||
if (metadata.rootPackage) {
|
||||
args.push("--package-name");
|
||||
args.push(`"${metadata.rootPackage}"`);
|
||||
args.push(metadata.rootPackage);
|
||||
}
|
||||
return this.exec("New Boot Project", `'${metadata.catalogId}'`, args, metadata.targetFolder);
|
||||
}
|
||||
|
||||
@@ -3,14 +3,20 @@ import * as vscode from "vscode";
|
||||
import { Cli } from "./cli";
|
||||
import { handleBootAdd, handleBootNew } from "./boot";
|
||||
import { handleCatalogAdd, handleCatalogRemove } from "./project-catalog";
|
||||
import { handleProjectAdd, handleProjectRemove } from "./project";
|
||||
|
||||
export const CLI = new Cli();
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext): Promise<void> {
|
||||
|
||||
vscode.commands.registerCommand('vscode-spring-cli.boot.new', handleBootNew);
|
||||
vscode.commands.registerCommand('vscode-spring-cli.boot.add', handleBootAdd);
|
||||
|
||||
vscode.commands.registerCommand('vscode-spring-cli.project-catalog.add', handleCatalogAdd);
|
||||
vscode.commands.registerCommand('vscode-spring-cli.project-catalog.remove', handleCatalogRemove);
|
||||
|
||||
vscode.commands.registerCommand('vscode-spring-cli.project.add', handleProjectAdd);
|
||||
vscode.commands.registerCommand('vscode-spring-cli.project.remove', handleProjectRemove);
|
||||
}
|
||||
|
||||
export async function deactivate(): Promise<void> {
|
||||
|
||||
@@ -42,7 +42,13 @@ export async function handleCatalogAdd() {
|
||||
title: "URL",
|
||||
prompt: "Enter URL:",
|
||||
placeholder: "https://github.com/my-org/my-project-catalog",
|
||||
validate: v => /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)?/gi.test(v) ? "" : "Invalid URL value"
|
||||
validate: v => {
|
||||
try {
|
||||
Uri.parse(v, true);
|
||||
} catch (error) {
|
||||
return "Invalid URL value"
|
||||
}
|
||||
}
|
||||
});
|
||||
const description = await enterText({
|
||||
title: "Description",
|
||||
|
||||
56
vscode-extensions/vscode-spring-cli/src/project.ts
Normal file
56
vscode-extensions/vscode-spring-cli/src/project.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { Uri, window } from "vscode";
|
||||
import { enterText, mapProjectToQuickPick } from "./utils";
|
||||
|
||||
import { CLI } from "./extension";
|
||||
|
||||
export async function handleProjectAdd() {
|
||||
try {
|
||||
const currentProjectNames = (await CLI.projectList()).map(p => p.name);
|
||||
const name = await enterText({
|
||||
title: "Name",
|
||||
prompt: "Enter Name:",
|
||||
validate: v => {
|
||||
if (!/^\S+$/.test(v)) {
|
||||
return "Invalid Project Catalog Name";
|
||||
}
|
||||
if (currentProjectNames.includes(v)) {
|
||||
return "Name alreasy exists"
|
||||
}
|
||||
}
|
||||
});
|
||||
const url = await enterText({
|
||||
title: "URL",
|
||||
prompt: "Enter URL:",
|
||||
placeholder: "https://github.com/my-org/my-project",
|
||||
validate: v => {
|
||||
try {
|
||||
Uri.parse(v, true);
|
||||
} catch (error) {
|
||||
return "Invalid URL value"
|
||||
}
|
||||
}
|
||||
});
|
||||
const description = await enterText({
|
||||
title: "Description",
|
||||
prompt: "Enter Description:"
|
||||
});
|
||||
const tags = (await enterText({
|
||||
title: "Tags",
|
||||
prompt: "Enter Tags as strings separated by spaces and/or commas",
|
||||
placeholder: "java, spring, eureka, config"
|
||||
})).split(/(,)?\s+/);
|
||||
} catch (error) {
|
||||
// Ignore error - must have been cancelled
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export async function handleProjectRemove() {
|
||||
const name = (await window.showQuickPick(CLI.projectList().then(ps => ps.map(mapProjectToQuickPick)), {
|
||||
canPickMany: false,
|
||||
ignoreFocusOut: true,
|
||||
}))?.label;
|
||||
if (name) {
|
||||
return CLI.projectRemove(name);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { InputBox, OpenDialogOptions, Uri, WorkspaceFolder, window, workspace } from "vscode";
|
||||
import { InputBox, OpenDialogOptions, QuickPickItem, QuickPickItemKind, Uri, WorkspaceFolder, window, workspace } from "vscode";
|
||||
import path from "path"
|
||||
import { Project } from "./cli-types";
|
||||
|
||||
export async function openDialogForFolder(customOptions: OpenDialogOptions): Promise<Uri> {
|
||||
const options: OpenDialogOptions = {
|
||||
@@ -24,7 +25,7 @@ export function enterText(opts: {
|
||||
defaultValue?: string,
|
||||
placeholder?: string
|
||||
}): Promise<string> {
|
||||
return new Promise<string>((resolve) => {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
const inputBox: InputBox = window.createInputBox();
|
||||
inputBox.title = opts.title;
|
||||
inputBox.placeholder = opts.placeholder;
|
||||
@@ -36,10 +37,11 @@ export function enterText(opts: {
|
||||
});
|
||||
inputBox.onDidAccept(() => {
|
||||
if (!inputBox.validationMessage) {
|
||||
resolve(inputBox.value);
|
||||
inputBox.hide();
|
||||
}
|
||||
});
|
||||
inputBox.onDidHide(() => resolve(inputBox.value));
|
||||
inputBox.onDidHide(() => reject("cancelled"));
|
||||
inputBox.show();
|
||||
});
|
||||
}
|
||||
@@ -86,3 +88,12 @@ export async function getTargetPomXml(): Promise<Uri> {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function mapProjectToQuickPick(project: Project): QuickPickItem {
|
||||
return {
|
||||
label: project.name,
|
||||
kind: QuickPickItemKind.Default,
|
||||
description: project.description
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"./**/*.ts"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
"node_modules",
|
||||
"out"
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user