Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: maven command history #87

Merged
merged 3 commits into from
Jul 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 48 additions & 54 deletions src/Utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ import { MavenExplorerProvider } from "./explorer/MavenExplorerProvider";
import { MavenProjectNode } from "./explorer/model/MavenProjectNode";
import { VSCodeUI } from "./VSCodeUI";

interface ICommandHistory {
pomPath: string;
timestamps: { [command: string]: number };
}

export namespace Utils {
let EXTENSION_PUBLISHER: string;
let EXTENSION_NAME: string;
Expand Down Expand Up @@ -61,19 +66,20 @@ export namespace Utils {
return path.join(extensions.getExtension(getExtensionId()).extensionPath, ...args);
}

export async function parseXmlFile(filepath: string, options?: xml2js.OptionsV2): Promise<{}> {
if (await fse.exists(filepath)) {
const xmlString: string = await fse.readFile(filepath, "utf8");
export async function parseXmlFile(xmlFilePath: string, options?: xml2js.OptionsV2): Promise<{}> {
if (await fse.exists(xmlFilePath)) {
const xmlString: string = await fse.readFile(xmlFilePath, "utf8");
return parseXmlContent(xmlString, options);
} else {
return null;
}
}
export async function parseXmlContent(xml: string, options?: xml2js.OptionsV2): Promise<{}> {

export async function parseXmlContent(xmlString: string, options?: xml2js.OptionsV2): Promise<{}> {
const opts: {} = Object.assign({ explicitArray: true }, options);
return new Promise<{}>(
(resolve: (value: {}) => void, reject: (e: Error) => void): void => {
xml2js.parseString(xml, opts, (err: Error, res: {}) => {
xml2js.parseString(xmlString, opts, (err: Error, res: {}) => {
if (err) {
reject(err);
} else {
Expand All @@ -84,35 +90,12 @@ export namespace Utils {
);
}

export function withLRUItemAhead<T>(array: T[], lruItem: T): T[] {
const ret: T[] = array.filter((elem: T) => elem !== lruItem).reverse();
ret.push(lruItem);
return ret.reverse();
}

export async function loadCmdHistory(pomXmlFilePath: string): Promise<string[]> {
const filepath: string = getCommandHistoryCachePath(pomXmlFilePath);
if (await fse.pathExists(filepath)) {
const content: string = (await fse.readFile(filepath)).toString().trim();
if (content) {
return content.split("\n").map((line: string) => line.trim()).filter(Boolean);
}
}
return [];
}

export async function saveCmdHistory(pomXmlFilePath: string, cmdlist: string[]): Promise<void> {
const filepath: string = getCommandHistoryCachePath(pomXmlFilePath);
await fse.ensureFile(filepath);
await fse.writeFile(filepath, cmdlist.join("\n"));
}

export function getEffectivePomOutputPath(pomXmlFilePath: string): string {
return path.join(os.tmpdir(), EXTENSION_NAME, md5(pomXmlFilePath), "effective-pom.xml");
}

export function getCommandHistoryCachePath(pomXmlFilePath?: string): string {
return path.join(os.tmpdir(), EXTENSION_NAME, pomXmlFilePath ? md5(pomXmlFilePath) : "", "commandHistory.txt");
export function getCommandHistoryCachePath(pomXmlFilePath: string): string {
return path.join(os.tmpdir(), EXTENSION_NAME, md5(pomXmlFilePath), "commandHistory.json");
}

export async function readFileIfExists(filepath: string): Promise<string> {
Expand Down Expand Up @@ -235,29 +218,37 @@ export namespace Utils {
);
}

export async function getLRUCommands(pomfile?: string): Promise<{ command: string, pomfile: string }[]> {
const filepath: string = getCommandHistoryCachePath();
export async function getLRUCommands(pomPath: string): Promise<{}[]> {
const filepath: string = getCommandHistoryCachePath(pomPath);
if (await fse.pathExists(filepath)) {
const content: string = (await fse.readFile(filepath)).toString().trim();
if (content) {
return content.split("\n").map(
(line: string) => {
const items: string[] = line.split(",");
return { command: items[0], pomfile: items[1] };
}
).filter((item: { command: string, pomfile: string }) => !pomfile || pomfile === item.pomfile);
const content: string = (await fse.readFile(filepath)).toString();
let historyObject: ICommandHistory;
try {
historyObject = JSON.parse(content);
} catch (error) {
historyObject = { pomPath, timestamps: {} };
}
const timestamps: { [command: string]: number } = historyObject.timestamps;
const commandList: string[] = Object.keys(timestamps).sort((a, b) => timestamps[b] - timestamps[a]);
return commandList.map(command => Object.assign({command, pomPath, timestamp: timestamps[command]}));
}
return [];
}

async function updateLRUCommands(command: string, pomfile: string): Promise<void> {
const filepath: string = getCommandHistoryCachePath();
await fse.ensureFile(filepath);
const content: string = (await fse.readFile(filepath)).toString().trim();
const lines: string[] = withLRUItemAhead<string>(content.split("\n"), `${command},${pomfile}`);
const newContent: string = lines.filter(Boolean).slice(0, 20).join("\n");
await fse.writeFile(filepath, newContent);
async function updateLRUCommands(command: string, pomPath: string): Promise<void> {
const historyFilePath: string = getCommandHistoryCachePath(pomPath);
await fse.ensureFile(historyFilePath);
const content: string = (await fse.readFile(historyFilePath)).toString();
let historyObject: ICommandHistory;
try {
historyObject = JSON.parse(content);
historyObject.pomPath = pomPath;
} catch (error) {
historyObject = { pomPath, timestamps: {} };
} finally {
historyObject.timestamps[command] = Date.now();
}
await fse.writeFile(historyFilePath, JSON.stringify(historyObject));
}

export function currentWindowsShell(): string {
Expand Down Expand Up @@ -378,18 +369,21 @@ export namespace Utils {
}
}

export async function executeHistoricalGoals(projectPomPath?: string): Promise<void> {
const selected: { command: string, pomfile: string } = await VSCodeUI.getQuickPick(
Utils.getLRUCommands(projectPomPath),
export async function executeHistoricalGoals(projectPomPaths: string[]): Promise<void> {
const candidates: any[] = Array.prototype.concat.apply(
[],
await Promise.all(projectPomPaths.map(projectPomPath => Utils.getLRUCommands(projectPomPath)))
);
candidates.sort((a, b) => b.timestamp - a.timestamp);
const selected: { command: string, pomPath: string } = await VSCodeUI.getQuickPick(
candidates,
(x) => x.command,
null,
(x) => x.pomfile,
(x) => x.pomPath,
{ placeHolder: "Select from history ... " }
);
if (selected) {
const command: string = selected.command;
const pomfile: string = selected.pomfile;
Utils.executeInTerminal(command, pomfile);
Utils.executeInTerminal(selected.command, selected.pomPath);
}
}

Expand Down
6 changes: 5 additions & 1 deletion src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,11 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
}));

context.subscriptions.push(TelemetryWrapper.registerCommand("maven.history", async (item: MavenProjectNode | undefined) => {
await Utils.executeHistoricalGoals(item && item.pomPath);
if (item) {
await Utils.executeHistoricalGoals([item.pomPath]);
} else {
await Utils.executeHistoricalGoals(provider.mavenProjectNodes.map(_node => _node.pomPath));
}
}));

context.subscriptions.push(TelemetryWrapper.registerCommand("maven.goal.execute", async () => {
Expand Down