Skip to content

Commit

Permalink
Merge pull request #32 from nguyenngoclongdev/import-command-from-source
Browse files Browse the repository at this point in the history
Import command from source
  • Loading branch information
nguyenngoclongdev authored Oct 8, 2024
2 parents 3e2a9f9 + dad1570 commit 40b6630
Show file tree
Hide file tree
Showing 15 changed files with 1,110 additions and 322 deletions.
5 changes: 5 additions & 0 deletions .changeset/shy-snails-jump.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"terminal-keeper": patch
---

feat: Allow the import of commands from another source that exists in the workspace.
13 changes: 13 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,26 @@
"gitlens.hovers.currentLine.over": "line",
"gitlens.codeLens.enabled": false,
"cSpell.words": [
"bombadil",
"callhierarchy",
"gradlew",
"GRUNTFILE",
"gulpfile",
"Ngoc",
"nguyenngoclong",
"nmake",
"nodir",
"NOTPARALLEL",
"ONESHELL",
"outdir",
"outfile",
"pipenv",
"pipfile",
"posttest",
"pree",
"registertask",
"SECONDEXPANSION",
"sgarciac",
"vscommands",
"vsix",
"wslpath"
Expand Down
687 changes: 369 additions & 318 deletions package-lock.json

Large diffs are not rendered by default.

94 changes: 91 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@
}
]
},
"submenus": [
{
"id": "import.submenu",
"label": "Import Session"
}
],
"commands": [
{
"command": "terminal-keeper.generate",
Expand Down Expand Up @@ -170,9 +176,81 @@
{
"command": "terminal-keeper.help-and-feedback-activity",
"title": "Help & Feedback"
},
{
"command": "terminal-keeper.import-from-npm",
"title": "From npm (e.g. package.json)"
},
{
"command": "terminal-keeper.import-from-composer",
"title": "From composer (e.g. composer.json)"
},
{
"command": "terminal-keeper.import-from-make",
"title": "From makefile (e.g. Makefile, makefile)"
},
{
"command": "terminal-keeper.import-from-gradle",
"title": "From gradle (e.g. build.gradle, test.gradle)"
},
{
"command": "terminal-keeper.import-from-pipenv",
"title": "From pipenv (e.g. Pipfile, pipfile)"
},
{
"command": "terminal-keeper.import-from-ant",
"title": "From ant (e.g. build.xml)"
},
{
"command": "terminal-keeper.import-from-grunt",
"title": "From grunt (e.g. GRUNTFILE.JS)"
},
{
"command": "terminal-keeper.import-from-gulp",
"title": "From gulp (e.g. gulpfile.mjs, gulpfile.js, GULPFILE.js)"
}
],
"menus": {
"import.submenu": [
{
"command": "terminal-keeper.import-from-npm",
"group": "source@1"
},
{
"command": "terminal-keeper.import-from-composer",
"group": "source@2"
},
{
"command": "terminal-keeper.import-from-make",
"group": "source@3"
},
{
"command": "terminal-keeper.import-from-gradle",
"group": "source@4"
},
{
"command": "terminal-keeper.import-from-pipenv",
"group": "source@5"
},
{
"command": "terminal-keeper.import-from-ant",
"group": "source@6"
},
{
"command": "terminal-keeper.import-from-grunt",
"group": "source@7"
},
{
"command": "terminal-keeper.import-from-gulp",
"group": "source@8"
}
],
"editor/context": [
{
"submenu": "import.submenu",
"group": "import"
}
],
"view/title": [
{
"command": "terminal-keeper.active",
Expand Down Expand Up @@ -210,15 +288,20 @@
"group": "activity@4"
},
{
"command": "terminal-keeper.save",
"submenu": "import.submenu",
"when": "view == terminalKeeperActivityView",
"group": "tk1@1"
},
{
"command": "terminal-keeper.remove",
"command": "terminal-keeper.save",
"when": "view == terminalKeeperActivityView",
"group": "tk1@2"
},
{
"command": "terminal-keeper.remove",
"when": "view == terminalKeeperActivityView",
"group": "tk1@3"
},
{
"command": "terminal-keeper.help-and-feedback-activity",
"when": "view == terminalKeeperActivityView",
Expand Down Expand Up @@ -335,6 +418,7 @@
"@types/mocha": "^10.0.1",
"@types/node": "^20.2.5",
"@types/vscode": "^1.64.0",
"@types/xml2js": "^0.4.14",
"@types/webpack-env": "^1.18.1",
"@typescript-eslint/eslint-plugin": "^5.59.8",
"@typescript-eslint/parser": "^5.59.8",
Expand All @@ -351,14 +435,18 @@
"path-browserify": "^1.0.1",
"process": "^0.11.10",
"stream-browserify": "^3.0.0",
"timers-browserify": "^2.0.12",
"ts-loader": "^9.4.3",
"typescript": "^5.0.4",
"webpack": "^5.84.1",
"webpack-cli": "^5.1.1"
},
"dependencies": {
"@vscode-utility/fs-browserify": "^1.0.8",
"@vscode-utility/terminal-browserify": "^1.0.2"
"@vscode-utility/terminal-browserify": "^1.0.2",
"@sgarciac/bombadil": "^2.3.0",
"glob": "11.0.0",
"xml2js": "^0.6.2"
},
"author": {
"name": "Nguyen Ngoc Long",
Expand Down
212 changes: 212 additions & 0 deletions src/commands/importAsync.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
import { TerminalItem } from '@vscode-utility/terminal-browserify';
import { glob } from 'glob';
import path from 'path/posix';
import { QuickPickItem, window, workspace, WorkspaceFolder } from 'vscode';
import { Configuration } from '../configuration/configuration';
import { configurationTemplate } from '../configuration/template';
import { constants } from '../utils/constants';
import { getSessionQuickPickItems, showErrorMessageWithDetail } from '../utils/utils';
import { extractAntCommands } from './modules/antParse';
import { extractGradleCommands } from './modules/gradleParse';
import { extractGruntCommands } from './modules/gruntParse';
import { extractGulpCommands } from './modules/gulpParse';
import { extractJsonScriptCommands } from './modules/jsonScriptParse';
import { extractMakeCommands } from './modules/makeParse';
import { extractPipenvCommands } from './modules/pipenvParse';

export type ImportFileType = 'npm' | 'composer' | 'make' | 'gradle' | 'pipenv' | 'ant' | 'grunt' | 'gulp';

const getFilenames = (fileType: ImportFileType): Array<string> | undefined => {
switch (fileType) {
case 'npm':
return ['package.json'];
case 'composer':
return ['composer.json'];
case 'make':
return ['Makefile', 'makefile'];
case 'gradle':
return ['build.gradle', 'test.gradle'];
case 'pipenv':
return ['Pipfile', 'pipfile'];
case 'ant':
return ['build.xml'];
case 'grunt':
return ['GRUNTFILE.JS'];
case 'gulp':
return ['GULPFILE.js', 'GULPFILE.mjs', 'gulpfile.js', 'gulpfile.mjs'];
default:
return undefined;
}
};

const getCommands = async (fileType: ImportFileType, filePath: string): Promise<Record<string, string> | undefined> => {
switch (fileType) {
case 'npm':
return extractJsonScriptCommands(filePath);
case 'composer':
return extractJsonScriptCommands(filePath);
case 'make':
return extractMakeCommands(filePath);
case 'gradle':
return extractGradleCommands(filePath);
case 'pipenv':
return extractPipenvCommands(filePath);
case 'ant':
return extractAntCommands(filePath);
case 'grunt':
return extractGruntCommands(filePath);
case 'gulp':
return extractGulpCommands(filePath);
default:
return undefined;
}
};

const getFilePaths = async (workspaceFolders: readonly WorkspaceFolder[], filenames: string[]): Promise<string[]> => {
const filePaths: string[] = [];
for (let i = 0; i < workspaceFolders.length; i++) {
const wsFolder = workspaceFolders[i];
for (let j = 0; j < filenames.length; j++) {
const filename = filenames[j];
const files = await glob(`**/${filename}`, { cwd: wsFolder.uri.fsPath, nodir: true, absolute: true });
filePaths.push(...files);
}
}
return filePaths;
};

const chooseFilePath = async (filePaths: string[]): Promise<string | undefined> => {
let selectedFilePath = filePaths[0];
if (filePaths.length >= 1) {
const options = filePaths.map((filePath): QuickPickItem => {
const filename = path.basename(filePath);
return { label: filename, detail: filePath.replace(filename, '') };
});
const quickPickItem = await window.showQuickPick(options, {
title: constants.selectFileTitle,
placeHolder: constants.selectFilePlaceHolder,
canPickMany: false,
ignoreFocusOut: true
});
return quickPickItem ? path.join(quickPickItem.detail || '', quickPickItem.label) : undefined;
}
return selectedFilePath;
};

const chooseSessionName = async (): Promise<string | undefined> => {
// Show choose session name box
const config = await Configuration.load();
const sessionsWithDescription: QuickPickItem[] = getSessionQuickPickItems(config.sessions);
sessionsWithDescription.forEach((sessionItem) => {
sessionItem.detail = `Overwrites scripts to session ${sessionItem.label}`;
});
const addNewSession: QuickPickItem = {
label: 'Add new session...',
detail: 'Create new session, and save scripts to it.',
alwaysShow: true
};
const quickPickItem = await window.showQuickPick([addNewSession].concat(sessionsWithDescription), {
title: 'Select the session you want to override or add new session',
placeHolder: 'Session name...',
ignoreFocusOut: true
});
if (!quickPickItem) {
return undefined;
}

// Show input box if select add new
let sessionName = quickPickItem.label;
if (sessionName === addNewSession.label) {
const sessionNameInput = await window.showInputBox({
title: 'Please enter the session name.',
placeHolder: 'e.g. build, migrate, start, deploy',
ignoreFocusOut: true,
validateInput: (value: string) => {
if (!value) {
return 'The session name cannot be null or empty.';
}
if (sessionsWithDescription.some((s) => s.label === value)) {
return 'The session name already exists.';
}
return ''; // input valid is OK
}
});
return sessionNameInput ? sessionNameInput : undefined;
}
return sessionName;
};

export const importAsync = async (fileType: ImportFileType): Promise<void> => {
try {
const { workspaceFolders } = workspace;
if (!workspaceFolders || workspaceFolders.length <= 0) {
window.showWarningMessage(constants.openWorkspace);
return;
}

// Get filename by fileType
const filenames = getFilenames(fileType);
if (!filenames || filenames.length <= 0) {
window.showWarningMessage(constants.notSupportFileType.replace('{fileType}', fileType));
return;
}

// Get all filepaths
const filePaths = await getFilePaths(workspaceFolders, filenames);
if (!filePaths || filePaths.length <= 0) {
window.showWarningMessage(
constants.notExistImportFile
.replace('{filename}', filenames.join(', '))
.replace('{workspace}', workspaceFolders.map((w) => w.uri.fsPath).join(', '))
);
return;
}

// Choose the file if more than one
let selectedFilePath = await chooseFilePath(filePaths);
if (!selectedFilePath) {
return;
}

// Read the script of file
const scripts = await getCommands(fileType, selectedFilePath);
if (!scripts) {
window.showWarningMessage(constants.notExistAnyCommands.replace('{filePath}', selectedFilePath));
return;
}

// Parse to terminal item from scripts
const terminalItems = Object.entries(scripts).map(([key, value]) => {
const item: TerminalItem = {
name: key,
commands: [value]
};
return item;
});

// Select name of sessions to override or add new session
let sessionName = await chooseSessionName();
if (!sessionName) {
return;
}

// Create new sessions.json if not exist, otherwise save to existing file
const isDefinedSessionFile = await Configuration.isDefinedSessionFile();
if (!isDefinedSessionFile) {
const content = configurationTemplate;
content.sessions = { default: [] };
await Configuration.save(content);
}

// Save scripts to sessions.json
const configSaved = await Configuration.load();
if (!configSaved.sessions) {
configSaved.sessions = { default: [] };
}
const previousTerminalItems = configSaved.sessions[sessionName] || [];
configSaved.sessions[sessionName] = previousTerminalItems.concat(terminalItems);
await Configuration.save(configSaved);
} catch (error) {
showErrorMessageWithDetail(constants.importFileFailed.replace('{fileType}', fileType), error);
}
};
Loading

0 comments on commit 40b6630

Please sign in to comment.