Change "Edit Starters" to "Add Starters" (#149)
* only allow to add starters for current project Signed-off-by: Yan Zhang <yanzh@microsoft.com> * add starters: use workspaceEdit to update pom file add starters: workspaceEdit Signed-off-by: Yan Zhang <yanzh@microsoft.com> workspaceEdit-based add starters Signed-off-by: Yan Zhang <yanzh@microsoft.com> * add license Signed-off-by: Yan Zhang <yanzh@microsoft.com> * address comments Signed-off-by: Yan Zhang <yanzh@microsoft.com>
This commit is contained in:
Родитель
5a137437a9
Коммит
43825092a6
|
@ -89,6 +89,11 @@
|
|||
"integrity": "sha512-xRas+PW/fM/MoonB+Pawg48bGTjCqJsFUFwZpH/q2oW80AMHGDAUTUqySBnqeQ18e/SNi7NOCf3ZkYOzKm3Pqw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/xml-zero-lexer": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/xml-zero-lexer/-/xml-zero-lexer-2.1.0.tgz",
|
||||
"integrity": "sha512-UP1ZV82+PbGEvy1+vFZa55Q3FWc7wX64iFsbFlp+D3ULFPw8F8J5WGgRsQ575DXhFBeW9dQkV/KJsLkHp+xqqw=="
|
||||
},
|
||||
"@types/xml2js": {
|
||||
"version": "0.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.4.tgz",
|
||||
|
@ -5078,6 +5083,11 @@
|
|||
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
|
||||
"dev": true
|
||||
},
|
||||
"xml-zero-lexer": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/xml-zero-lexer/-/xml-zero-lexer-2.1.0.tgz",
|
||||
"integrity": "sha512-tr5RJrQOo4LBoSiknbeRJpedAiOKLdsgq1o5pV6aMa3prZbc2Gd2RnrxmcOELP15zkxJCqN2msC+mRYvVvEtkQ=="
|
||||
},
|
||||
"xml2js": {
|
||||
"version": "0.4.19",
|
||||
"resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz",
|
||||
|
|
18
package.json
18
package.json
|
@ -20,7 +20,7 @@
|
|||
"activationEvents": [
|
||||
"onCommand:spring.initializr.maven-project",
|
||||
"onCommand:spring.initializr.gradle-project",
|
||||
"onCommand:spring.initializr.editStarters",
|
||||
"onCommand:spring.initializr.addStarters",
|
||||
"onCommand:spring.initializr.createProject"
|
||||
],
|
||||
"main": "./dist/extension",
|
||||
|
@ -28,22 +28,22 @@
|
|||
"commands": [
|
||||
{
|
||||
"command": "spring.initializr.createProject",
|
||||
"title": "Create a Spring Boot Project",
|
||||
"title": "Create a Spring Boot Project...",
|
||||
"category": "Spring Initializr"
|
||||
},
|
||||
{
|
||||
"command": "spring.initializr.maven-project",
|
||||
"title": "Generate a Maven Project",
|
||||
"title": "Create a Maven Project...",
|
||||
"category": "Spring Initializr"
|
||||
},
|
||||
{
|
||||
"command": "spring.initializr.gradle-project",
|
||||
"title": "Generate a Gradle Project",
|
||||
"title": "Create a Gradle Project...",
|
||||
"category": "Spring Initializr"
|
||||
},
|
||||
{
|
||||
"command": "spring.initializr.editStarters",
|
||||
"title": "Edit starters",
|
||||
"command": "spring.initializr.addStarters",
|
||||
"title": "Add Starters...",
|
||||
"category": "Spring Initializr"
|
||||
}
|
||||
],
|
||||
|
@ -57,14 +57,14 @@
|
|||
"editor/context": [
|
||||
{
|
||||
"when": "resourceFilename == pom.xml",
|
||||
"command": "spring.initializr.editStarters",
|
||||
"command": "spring.initializr.addStarters",
|
||||
"group": "SpringInitializr"
|
||||
}
|
||||
],
|
||||
"explorer/context": [
|
||||
{
|
||||
"when": "resourceFilename == pom.xml",
|
||||
"command": "spring.initializr.editStarters",
|
||||
"command": "spring.initializr.addStarters",
|
||||
"group": "SpringInitializr"
|
||||
}
|
||||
]
|
||||
|
@ -149,6 +149,7 @@
|
|||
"@types/mocha": "^5.2.7",
|
||||
"@types/node": "^10.14.13",
|
||||
"@types/vscode": "1.30.0",
|
||||
"@types/xml-zero-lexer": "^2.1.0",
|
||||
"@types/xml2js": "^0.4.4",
|
||||
"glob": "^7.1.4",
|
||||
"mocha": "^7.1.1",
|
||||
|
@ -165,6 +166,7 @@
|
|||
"lodash": "^4.17.19",
|
||||
"md5": "^2.2.1",
|
||||
"vscode-extension-telemetry-wrapper": "^0.8.0",
|
||||
"xml-zero-lexer": "^2.1.0",
|
||||
"xml2js": "^0.4.19"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { setUserError } from "vscode-extension-telemetry-wrapper";
|
||||
|
||||
export class UserError extends Error {
|
||||
constructor(msg?: string) {
|
||||
super(msg);
|
||||
setUserError(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as vscode from "vscode";
|
||||
import { UserError } from "../error";
|
||||
import { ElementNode, getNodesByTag, XmlTagName } from "./lexer";
|
||||
|
||||
export async function updatePom(pomPath: string, deps: IArtifact[], boms: IBom[]) {
|
||||
const edit = new vscode.WorkspaceEdit();
|
||||
|
||||
const projectNode: ElementNode = await getActiveProjectNode();
|
||||
const dependenciesNode: ElementNode | undefined = projectNode.children && projectNode.children.find(node => node.tag === XmlTagName.Dependencies);
|
||||
if (dependenciesNode !== undefined) {
|
||||
await updateWorkspaceEdit(edit, pomPath, dependenciesNode, new DependencyNodes(deps));
|
||||
} else {
|
||||
await updateWorkspaceEdit(edit, pomPath, projectNode, new DependencyNodes(deps, {initParent: true}));
|
||||
}
|
||||
|
||||
if (boms && boms.length > 0) {
|
||||
const depMgmtNode: ElementNode | undefined = projectNode.children && projectNode.children.find(node => node.tag === XmlTagName.DependencyManagement);
|
||||
if (depMgmtNode !== undefined) {
|
||||
const depsNodes: ElementNode | undefined = depMgmtNode.children && depMgmtNode.children.find(node => node.tag === XmlTagName.Dependencies);
|
||||
if (depsNodes !== undefined) {
|
||||
await updateWorkspaceEdit(edit, pomPath, depsNodes, new BOMNodes(boms));
|
||||
} else {
|
||||
await updateWorkspaceEdit(edit, pomPath, depMgmtNode, new BOMNodes(boms, {parents: ["dependencies"]}));
|
||||
}
|
||||
} else {
|
||||
await updateWorkspaceEdit(edit, pomPath, projectNode, new BOMNodes(boms, {parents: ["dependencies", "dependencyManagement"]}));
|
||||
}
|
||||
}
|
||||
|
||||
vscode.workspace.applyEdit(edit);
|
||||
}
|
||||
|
||||
async function getActiveProjectNode() {
|
||||
if (!vscode.window.activeTextEditor) {
|
||||
throw new UserError("No POM file is open.");
|
||||
}
|
||||
|
||||
// Find out <dependencies> node and insert content.
|
||||
const content = vscode.window.activeTextEditor.document.getText();
|
||||
const projectNodes: ElementNode[] = getNodesByTag(content, XmlTagName.Project);
|
||||
if (projectNodes === undefined || projectNodes.length !== 1) {
|
||||
throw new UserError("Only support POM file with single <project> node.");
|
||||
}
|
||||
|
||||
return projectNodes[0];
|
||||
}
|
||||
|
||||
function constructNodeText(nodeToInsert: PomNode, baseIndent: string, indent: string, eol: string): string {
|
||||
const lines: string[] = nodeToInsert.getTextLines(indent);
|
||||
return ["", ...lines].join(`${eol}${baseIndent}${indent}`) + eol;
|
||||
}
|
||||
|
||||
async function updateWorkspaceEdit(edit: vscode.WorkspaceEdit, pomPath: string, parentNode: ElementNode, nodeToInsert: PomNode): Promise<vscode.WorkspaceEdit> {
|
||||
if (parentNode.contentStart === undefined || parentNode.contentEnd === undefined) {
|
||||
throw new UserError("Invalid target XML node to insert dependency.");
|
||||
}
|
||||
const currentDocument: vscode.TextDocument = await vscode.workspace.openTextDocument(pomPath);
|
||||
const textEditor: vscode.TextEditor = await vscode.window.showTextDocument(currentDocument);
|
||||
const baseIndent: string = getIndentation(currentDocument, parentNode.contentEnd);
|
||||
const options: vscode.TextEditorOptions = textEditor.options;
|
||||
const indent: string = options.insertSpaces ? " ".repeat(options.tabSize as number) : "\t";
|
||||
const eol: string = currentDocument.eol === vscode.EndOfLine.LF ? "\n" : "\r\n";
|
||||
|
||||
let insertPos: vscode.Position = currentDocument.positionAt(parentNode.contentEnd);
|
||||
// Not to mess up indentation, move cursor to line start:
|
||||
// <tab><tab>|</dependencies> => |<tab><whitespace></dependencies>
|
||||
const insPosLineStart: vscode.Position = new vscode.Position(insertPos.line, 0);
|
||||
const contentBefore: string = currentDocument.getText(new vscode.Range(insPosLineStart, insertPos));
|
||||
if (contentBefore.trim() === "") {
|
||||
insertPos = insPosLineStart;
|
||||
}
|
||||
|
||||
const targetText: string = constructNodeText(nodeToInsert, baseIndent, indent, eol);
|
||||
|
||||
edit.insert(currentDocument.uri, insertPos, targetText);
|
||||
return edit;
|
||||
}
|
||||
|
||||
function getIndentation(document: vscode.TextDocument, offset: number): string {
|
||||
const closingTagPosition: vscode.Position = document.positionAt(offset);
|
||||
return document.getText(new vscode.Range(
|
||||
new vscode.Position(closingTagPosition.line, 0),
|
||||
closingTagPosition
|
||||
));
|
||||
}
|
||||
|
||||
interface IArtifact {
|
||||
groupId: string;
|
||||
artifactId: string;
|
||||
version?: string;
|
||||
scope?: string;
|
||||
}
|
||||
|
||||
interface IBom {
|
||||
groupId: string;
|
||||
artifactId: string;
|
||||
version: string;
|
||||
scope?: string;
|
||||
type?: string;
|
||||
}
|
||||
|
||||
abstract class PomNode {
|
||||
protected static wrapWithParentNode(lines: string[], indent: string, parent: string) {
|
||||
return [
|
||||
`<${parent}>`,
|
||||
...lines.map(line => `${indent}${line}`),
|
||||
`</${parent}>`,
|
||||
];
|
||||
}
|
||||
|
||||
public abstract getTextLines(indent: string): string[];
|
||||
}
|
||||
|
||||
class DependencyNodes extends PomNode {
|
||||
constructor(private artifacts: IArtifact[], private options?: { initParent?: boolean }) {
|
||||
super();
|
||||
}
|
||||
|
||||
public getTextLines(indent: string): string[] {
|
||||
const listOfLines: string[] = [].concat(...this.artifacts.map(artifact => this.toTextLine(artifact, indent)));
|
||||
if (this.options && this.options.initParent) {
|
||||
return PomNode.wrapWithParentNode(listOfLines, indent, "dependencies");
|
||||
} else {
|
||||
return listOfLines;
|
||||
}
|
||||
}
|
||||
|
||||
private toTextLine(artifact: IArtifact, indent: string): string[] {
|
||||
const { groupId, artifactId, version, scope } = artifact;
|
||||
const lines: string[] = [
|
||||
`<groupId>${groupId}</groupId>`,
|
||||
`<artifactId>${artifactId}</artifactId>`,
|
||||
version && `<version>${version}</version>`,
|
||||
scope && scope !== "compile" && `<scope>${scope}</scope>`,
|
||||
].filter(Boolean);
|
||||
return PomNode.wrapWithParentNode(lines, indent, "dependency");
|
||||
}
|
||||
}
|
||||
|
||||
class BOMNodes extends PomNode {
|
||||
constructor(private boms: IBom[], private options?: { parents?: string[] }) { super(); }
|
||||
|
||||
public getTextLines(indent: string): string[] {
|
||||
const listOfLines: string[][] = this.boms.map(bom => this.bomToTextLine(bom, indent));
|
||||
let lines: string[] = [].concat(...listOfLines);
|
||||
if (this.options && this.options.parents && this.options.parents.length > 0) {
|
||||
for (const parent of this.options.parents) {
|
||||
lines = PomNode.wrapWithParentNode(lines, indent, parent);
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
private bomToTextLine(bom: IBom, indent: string): string[] {
|
||||
const { groupId, artifactId, version } = bom;
|
||||
const lines: string[] = [
|
||||
`<groupId>${groupId}</groupId>`,
|
||||
`<artifactId>${artifactId}</artifactId>`,
|
||||
`<version>${version}</version>`,
|
||||
`<type>pom</type>`,
|
||||
`<scope>import</scope>`,
|
||||
];
|
||||
return PomNode.wrapWithParentNode(lines, indent, "dependency");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import Lexx, { NodeTypes } from "xml-zero-lexer";
|
||||
|
||||
export enum XmlTagName {
|
||||
GroupId = "groupId",
|
||||
ArtifactId = "artifactId",
|
||||
Version = "version",
|
||||
Dependencies = "dependencies",
|
||||
Plugins = "plugins",
|
||||
Project = "project",
|
||||
DependencyManagement = "dependencyManagement"
|
||||
}
|
||||
|
||||
export class ElementNode {
|
||||
public parent: ElementNode;
|
||||
public tag: string;
|
||||
public text?: string;
|
||||
public contentStart?: number;
|
||||
public contentEnd?: number;
|
||||
public children?: ElementNode[];
|
||||
|
||||
constructor(parent: ElementNode, tag: string) {
|
||||
this.parent = parent;
|
||||
this.tag = tag;
|
||||
this.text = "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a child for current node.
|
||||
* NOTE: an ElementNode can have **either** a value or a list of children.
|
||||
* E.g.
|
||||
* ```xml
|
||||
* <dependency>
|
||||
* <groupId>test-gid</groupId>
|
||||
* <artifactId>test-aid</artifactId>
|
||||
* </dependency>
|
||||
* ```
|
||||
* For node `dependency`, it has two children.
|
||||
* For node `groupId`, it has value of `test-gid`.
|
||||
*
|
||||
* @param child the child element node.
|
||||
*/
|
||||
public addChild(child: ElementNode): void {
|
||||
if (this.text !== undefined) {
|
||||
this.text = undefined; // value cannot exist with children.
|
||||
}
|
||||
if (this.children === undefined) {
|
||||
this.children = [];
|
||||
}
|
||||
this.children.push(child);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function getNodesByTag(text: string, tag: string): ElementNode[] {
|
||||
const tokens: number[][] = Lexx(text);
|
||||
return getElementHierarchy(text, tokens, tag);
|
||||
}
|
||||
|
||||
export function getCurrentNode(text: string, offset: number): ElementNode | undefined {
|
||||
const tokens: number[][] = Lexx(text);
|
||||
return getElementHierarchy(text, tokens, offset);
|
||||
}
|
||||
|
||||
function getElementHierarchy(text: string, tokens: number[][], targetTag: string): ElementNode[];
|
||||
function getElementHierarchy(text: string, tokens: number[][], cursorOffset: number): ElementNode;
|
||||
// tslint:disable-next-line:cyclomatic-complexity
|
||||
function getElementHierarchy(text: string, tokens: number[][], tagOrOffset: number | string): ElementNode | ElementNode[] | undefined {
|
||||
let targetTag: string | undefined;
|
||||
let cursorOffset: number | undefined;
|
||||
if (typeof tagOrOffset === "string") {
|
||||
targetTag = tagOrOffset;
|
||||
} else if (typeof tagOrOffset === "number") {
|
||||
cursorOffset = tagOrOffset;
|
||||
}
|
||||
const n: number = tokens.length;
|
||||
const elementNodes: ElementNode[] = [];
|
||||
const tagNodes: ElementNode[] = [];
|
||||
let cursorNode: ElementNode | undefined;
|
||||
let pointer: number = 0;
|
||||
let i: number = 0;
|
||||
|
||||
while (i < n) {
|
||||
const token: number[] = tokens[i];
|
||||
const currentNode: ElementNode = elementNodes[elementNodes.length - 1];
|
||||
switch (token[0]) {
|
||||
case NodeTypes.XML_DECLARATION:
|
||||
case NodeTypes.ELEMENT_NODE: {
|
||||
// [_type, start, end] = token;
|
||||
const [start, end] = token.slice(1, 3);
|
||||
const newElement: ElementNode = new ElementNode(currentNode, text.substring(start, end));
|
||||
if (currentNode !== undefined) {
|
||||
currentNode.addChild(newElement);
|
||||
}
|
||||
|
||||
pointer = end + 1; // pass ">" mark.
|
||||
elementNodes.push(newElement);
|
||||
newElement.contentStart = pointer;
|
||||
break;
|
||||
}
|
||||
case NodeTypes.ATTRIBUTE_NODE: {
|
||||
// [_type, _keyStart, _keyEnd, _valueStart, valueEnd] = token;
|
||||
const valueEnd: number = token[4];
|
||||
// Attributes not handled yet.
|
||||
pointer = valueEnd + 1; // pass ">" mark.
|
||||
currentNode.contentStart = pointer;
|
||||
break;
|
||||
}
|
||||
case NodeTypes.TEXT_NODE: {
|
||||
// [_type, start, end] = token;
|
||||
const [start, end] = token.slice(1, 3);
|
||||
if (currentNode !== undefined) {
|
||||
currentNode.text = text.substring(start, end);
|
||||
}
|
||||
pointer = end;
|
||||
break;
|
||||
}
|
||||
case NodeTypes.CLOSE_ELEMENT: {
|
||||
currentNode.contentEnd = pointer;
|
||||
elementNodes.pop();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (targetTag !== undefined && currentNode !== undefined && targetTag === currentNode.tag && tagNodes.indexOf(currentNode) < 0) {
|
||||
tagNodes.push(currentNode);
|
||||
}
|
||||
if (cursorOffset !== undefined
|
||||
&& cursorNode === undefined
|
||||
&& currentNode !== undefined
|
||||
&& currentNode.contentStart !== undefined && currentNode.contentStart <= cursorOffset
|
||||
&& currentNode.contentEnd !== undefined && cursorOffset <= currentNode.contentEnd) {
|
||||
cursorNode = currentNode;
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
if (targetTag !== undefined) {
|
||||
return tagNodes;
|
||||
} else if (cursorOffset !== undefined) {
|
||||
return cursorNode !== undefined ? cursorNode : elementNodes[elementNodes.length - 1];
|
||||
}
|
||||
return undefined;
|
||||
}
|
|
@ -8,7 +8,7 @@ import {
|
|||
initializeFromJsonFile,
|
||||
instrumentOperation
|
||||
} from "vscode-extension-telemetry-wrapper";
|
||||
import { GenerateProjectHandler } from "./handler";
|
||||
import { AddStartersHandler, GenerateProjectHandler } from "./handler";
|
||||
import { getTargetPomXml, loadPackageInfo } from "./Utils";
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext): Promise<void> {
|
||||
|
@ -38,11 +38,11 @@ async function initializeExtension(_operationId: string, context: vscode.Extensi
|
|||
}
|
||||
}));
|
||||
|
||||
context.subscriptions.push(instrumentAndRegisterCommand("spring.initializr.editStarters", async (_oid: string, entry?: vscode.Uri) => {
|
||||
throw new Error("Not implemented");
|
||||
context.subscriptions.push(instrumentAndRegisterCommand("spring.initializr.addStarters", async (_oid: string, entry?: vscode.Uri) => {
|
||||
const targetFile: vscode.Uri = entry || await getTargetPomXml();
|
||||
if (targetFile) {
|
||||
// await vscode.window.showTextDocument(targetFile);
|
||||
await vscode.window.showTextDocument(targetFile);
|
||||
await new AddStartersHandler().run(_oid, targetFile);
|
||||
} else {
|
||||
vscode.window.showInformationMessage("No pom.xml found in the workspace.");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import * as fse from "fs-extra";
|
||||
import * as path from "path";
|
||||
import * as vscode from "vscode";
|
||||
import { setUserError } from "vscode-extension-telemetry-wrapper";
|
||||
import { DependencyManager, IDependenciesItem } from "../DependencyManager";
|
||||
import {
|
||||
getBootVersion,
|
||||
getDependencyNodes,
|
||||
getParentReletivePath,
|
||||
IMavenId,
|
||||
IStarters,
|
||||
serviceManager,
|
||||
XmlNode
|
||||
} from "../model";
|
||||
import { readXmlContent } from "../Utils";
|
||||
import { updatePom } from "../Utils/xml";
|
||||
import { BaseHandler } from "./BaseHandler";
|
||||
import { specifyServiceUrl } from "./utils";
|
||||
|
||||
export class AddStartersHandler extends BaseHandler {
|
||||
private serviceUrl: string;
|
||||
|
||||
protected get failureMessage(): string {
|
||||
return "Fail to edit starters.";
|
||||
}
|
||||
|
||||
public async runSteps(_operationId: string, entry: vscode.Uri): Promise<void> {
|
||||
const bootVersion: string = await searchForBootVersion(entry.fsPath);
|
||||
if (!bootVersion) {
|
||||
const ex = new Error("Not a valid Spring Boot project.");
|
||||
setUserError(ex);
|
||||
throw ex;
|
||||
}
|
||||
|
||||
const deps: string[] = []; // gid:aid
|
||||
// Read pom.xml for $dependencies(gid, aid)
|
||||
const content: string = vscode.window.activeTextEditor.document.getText();
|
||||
const xml: { project: XmlNode } = await readXmlContent(content);
|
||||
|
||||
getDependencyNodes(xml.project).forEach(elem => {
|
||||
deps.push(`${elem.groupId[0]}:${elem.artifactId[0]}`);
|
||||
});
|
||||
|
||||
this.serviceUrl = await specifyServiceUrl();
|
||||
if (this.serviceUrl === undefined) {
|
||||
return;
|
||||
}
|
||||
// [interaction] Step: Dependencies, with pre-selected deps
|
||||
const starters: IStarters = await vscode.window.withProgress<IStarters>(
|
||||
{ location: vscode.ProgressLocation.Window },
|
||||
async (p) => {
|
||||
p.report({ message: `Fetching metadata for version ${bootVersion} ...` });
|
||||
return await serviceManager.getStarters(this.serviceUrl, bootVersion);
|
||||
},
|
||||
);
|
||||
|
||||
const oldStarterIds: string[] = [];
|
||||
if (!starters.dependencies) {
|
||||
await vscode.window.showErrorMessage("Unable to retrieve information of available starters.");
|
||||
return;
|
||||
}
|
||||
|
||||
Object.keys(starters.dependencies).forEach(key => {
|
||||
const elem: IMavenId = starters.dependencies[key];
|
||||
if (deps.indexOf(`${elem.groupId}:${elem.artifactId}`) >= 0) {
|
||||
oldStarterIds.push(key);
|
||||
}
|
||||
});
|
||||
const dependencyManager = new DependencyManager();
|
||||
dependencyManager.selectedIds = [].concat(oldStarterIds);
|
||||
let current: IDependenciesItem = null;
|
||||
do {
|
||||
current = await vscode.window.showQuickPick(
|
||||
dependencyManager.getQuickPickItems(this.serviceUrl, bootVersion),
|
||||
{
|
||||
ignoreFocusOut: true,
|
||||
matchOnDescription: true,
|
||||
matchOnDetail: true,
|
||||
placeHolder: "Select dependencies to add.",
|
||||
},
|
||||
);
|
||||
if (current && current.itemType === "dependency" && oldStarterIds.indexOf(current.id) === -1) {
|
||||
dependencyManager.toggleDependency(current.id);
|
||||
}
|
||||
} while (current && current.itemType === "dependency");
|
||||
if (!current) { return; }
|
||||
|
||||
// Diff deps for adding
|
||||
const toAdd: string[] = dependencyManager.selectedIds.filter(elem => oldStarterIds.indexOf(elem) < 0);
|
||||
if (toAdd.length === 0) {
|
||||
vscode.window.showInformationMessage("No changes.");
|
||||
return;
|
||||
}
|
||||
|
||||
const msgAdd: string = (toAdd && toAdd.length) ? `Adding: [${toAdd.map(d => dependencyManager.dict[d] && dependencyManager.dict[d].name).filter(Boolean).join(", ")}].` : "";
|
||||
const choice: string = await vscode.window.showWarningMessage(`${msgAdd} Proceed?`, "Proceed", "Cancel");
|
||||
if (choice !== "Proceed") {
|
||||
return;
|
||||
}
|
||||
|
||||
const artifacts = toAdd.map(id => starters.dependencies[id]);
|
||||
const bomIds = toAdd.map(id => starters.dependencies[id].bom).filter(Boolean);
|
||||
const boms = bomIds.map(id => starters.boms[id]);
|
||||
|
||||
updatePom(entry.fsPath, artifacts, boms);
|
||||
vscode.window.showInformationMessage("Pom file successfully updated.");
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function searchForBootVersion(pomPath: string): Promise<string> {
|
||||
const content: Buffer = await fse.readFile(pomPath);
|
||||
const { project: projectNode } = await readXmlContent(content.toString());
|
||||
const bootVersion: string = getBootVersion(projectNode);
|
||||
if (bootVersion) {
|
||||
return bootVersion;
|
||||
}
|
||||
|
||||
// search recursively in parent pom
|
||||
const relativePath = getParentReletivePath(projectNode);
|
||||
if (relativePath) {
|
||||
let absolutePath = path.join(path.dirname(pomPath), relativePath);
|
||||
if ((await fse.stat(absolutePath)).isDirectory()) {
|
||||
absolutePath = path.join(absolutePath, "pom.xml");
|
||||
}
|
||||
if (await fse.pathExists(absolutePath)) {
|
||||
return await searchForBootVersion(absolutePath);
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { AddStartersHandler } from "./AddStartersHandler";
|
||||
import { GenerateProjectHandler } from "./GenerateProjectHandler";
|
||||
|
||||
export { GenerateProjectHandler };
|
||||
export { GenerateProjectHandler, AddStartersHandler };
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// Licensed under the MIT license.
|
||||
|
||||
import { IDependency } from ".";
|
||||
import { IDependency, IStarters } from ".";
|
||||
import { downloadFile } from "../Utils";
|
||||
import { matchRange } from "../Utils/VersionHelper";
|
||||
import { DependencyGroup, Metadata } from "./Metadata";
|
||||
|
@ -46,6 +46,22 @@ class ServiceManager {
|
|||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated `dependencies` endpoint will be removed from metadata v3
|
||||
* This function returns information needed for current implementation of "add starters", e.g. gid/aid/repository/bom etc.
|
||||
* Should be removed in future refactoring.
|
||||
*/
|
||||
public async getStarters(serviceUrl: string, bootVersion: string): Promise<IStarters> {
|
||||
const url = `${serviceUrl}dependencies?bootVersion=${bootVersion}`;
|
||||
const rawJSONString: string = await downloadFile(url, true, METADATA_HEADERS);
|
||||
try {
|
||||
const ret = JSON.parse(rawJSONString);
|
||||
return ret;
|
||||
} catch (error) {
|
||||
throw new Error(`failed to parse response from ${url}`);
|
||||
}
|
||||
}
|
||||
|
||||
private async fetch(serviceUrl: string): Promise<void> {
|
||||
try {
|
||||
const rawJSONString: string = await downloadFile(serviceUrl, true, METADATA_HEADERS);
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
"interface-name": false,
|
||||
"object-literal-sort-keys": false,
|
||||
"trailing-comma": false,
|
||||
"max-classes-per-file": false,
|
||||
"no-console": [
|
||||
true,
|
||||
"log"
|
||||
|
|
Загрузка…
Ссылка в новой задаче