feat: implement bind command

Signed-off-by: Chapman Pendery <cpendery@vt.edu>
This commit is contained in:
Chapman Pendery
2023-10-08 00:37:44 -07:00
parent 61de6b4ac4
commit 1987da5fdb
4 changed files with 193 additions and 11 deletions
+1
View File
@@ -8,6 +8,7 @@
},
"files": [
"build/**",
"shell/**",
"*.md",
"LICENSE"
],
+7 -11
View File
@@ -1,17 +1,13 @@
import {Command} from "commander";
import { Command } from "commander";
import { supportedShells } from "../utils/bindings.js";
import { render } from "../ui/ui-bind.js";
const supportedShells = ["bash", "powershell", "windows-powershell"]
const action = (shell: string) => {
if (!supportedShells.includes(shell)) {
console.error(`Unsupported shell: ${shell}`);
process.exit(1);
}
console.log(`Adding keybindings to ${shell} shell`);
const action = async () => {
await render();
};
const cmd = new Command("bind");
cmd.description(`adds keybindings to the selected shell: ${supportedShells}`);
cmd.description(`adds keybindings to the selected shell: ${supportedShells.join(", ")}`);
cmd.action(action);
export default cmd
export default cmd;
+77
View File
@@ -0,0 +1,77 @@
import React, { useEffect, useState } from "react";
import { Box, Text, render as inkRender, useInput, useApp } from "ink";
import chalk from "chalk";
import { availableBindings, bind, supportedShells, Shell } from "../utils/bindings.js";
let uiResult = "";
function UI() {
const { exit } = useApp();
const [selectionIdx, setSelectionIdx] = useState(0);
const [availableShells, setAvailableShells] = useState<Shell[]>([]);
useEffect(() => {
availableBindings().then((bindings) => {
if (bindings.length == 0) {
exit();
}
setAvailableShells(bindings);
});
}, []);
useInput(async (_, key) => {
if (key.upArrow) {
setSelectionIdx(Math.max(0, selectionIdx - 1));
} else if (key.downArrow) {
setSelectionIdx(Math.min(availableShells.length - 1, selectionIdx + 1));
} else if (key.return) {
await bind(availableShells[selectionIdx]);
uiResult = availableShells[selectionIdx];
exit();
}
});
return (
<Box flexDirection="column">
<Box>
<Text bold>Select your desired shell for keybinding creation</Text>
</Box>
<Box flexDirection="column">
{availableShells.map((shell, idx) => {
if (idx == selectionIdx) {
return (
<Text color="cyan" underline key={idx}>
{">"} {shell}
</Text>
);
}
return (
<Text key={idx}>
{" "}
{shell}
</Text>
);
})}
{supportedShells
.filter((s) => !availableShells.includes(s))
.map((shell, idx) => (
<Text color="gray" key={idx}>
{" "}
{shell} (already bound)
</Text>
))}
</Box>
</Box>
);
}
export const render = async () => {
const { waitUntilExit } = inkRender(<UI />);
await waitUntilExit();
if (uiResult.length !== 0) {
process.stdout.write("\n" + chalk.green("✓") + " successfully created new bindings \n");
} else {
process.stdout.write("\n");
}
};
+108
View File
@@ -0,0 +1,108 @@
import os from "node:os";
import path from "node:path";
import fsAsync from "node:fs/promises";
import fs from "node:fs";
import process from "node:process";
import url from "node:url";
const __filename = url.fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export enum Shell {
Bash = "bash",
Powershell = "powershell",
Pwsh = "pwsh",
}
export const supportedShells = [Shell.Bash, Shell.Powershell, Shell.Pwsh];
const bashScriptCommand = (): string => {
return `[ -f ~/.sa/key-bindings.bash ] && source ~/.sa/key-bindings.bash`;
};
const powershellScriptCommand = (): string => {
const bindingsPath = path.join(os.homedir(), ".sa", "key-bindings-powershell.ps1");
return `if(Test-Path '${bindingsPath}' -PathType Leaf){. ${bindingsPath}}`;
};
const pwshScriptCommand = (): string => {
const bindingsPath = path.join(os.homedir(), ".sa", "key-bindings-pwsh.ps1");
return `if(Test-Path '${bindingsPath}' -PathType Leaf){. ${bindingsPath}}`;
};
const pwshConfigPath = (): string => {
switch (process.platform) {
case "win32":
return path.join(os.homedir(), "Documents", "Powershell", "Microsoft.PowerShell_profile.ps1");
case "linux":
case "darwin":
return path.join(os.homedir(), ".config", "powershell", "Microsoft.PowerShell_profile.ps1");
default:
throw new Error("Unsupported platform");
}
};
export const availableBindings = async (): Promise<Shell[]> => {
const saConfigPath = path.join(os.homedir(), ".sa");
if (!fs.existsSync(saConfigPath)) {
await fsAsync.mkdir(saConfigPath);
}
const bindings = [];
const bashConfigPath = path.join(os.homedir(), ".bashrc");
if (!fs.existsSync(bashConfigPath)) {
bindings.push(Shell.Bash);
} else {
const bashConfigContent = fsAsync.readFile(bashConfigPath, { encoding: "utf-8" });
if (!(await bashConfigContent).includes(bashScriptCommand())) {
bindings.push(Shell.Bash);
}
}
const powershellConfigPath = path.join(os.homedir(), "Documents", "WindowsPowershell", "Microsoft.PowerShell_profile.ps1");
if (!fs.existsSync(powershellConfigPath)) {
bindings.push(Shell.Powershell);
} else {
const powershellConfigContent = fsAsync.readFile(powershellConfigPath, { encoding: "utf-8" });
if (!(await powershellConfigContent).includes(powershellScriptCommand())) {
bindings.push(Shell.Powershell);
}
}
if (!fs.existsSync(pwshConfigPath())) {
bindings.push(Shell.Pwsh);
} else {
const pwshConfigContent = fsAsync.readFile(pwshConfigPath(), { encoding: "utf-8" });
if (!(await pwshConfigContent).includes(pwshScriptCommand())) {
bindings.push(Shell.Pwsh);
}
}
return bindings;
};
export const bind = async (shell: Shell): Promise<void> => {
const saConfigPath = path.join(os.homedir(), ".sa");
if (!fs.existsSync(saConfigPath)) {
await fsAsync.mkdir(saConfigPath);
}
switch (shell) {
case Shell.Bash:
const bashConfigPath = path.join(os.homedir(), ".bashrc");
await fsAsync.appendFile(bashConfigPath, `\n${bashScriptCommand()}`);
await fsAsync.copyFile(path.join(__dirname, "..", "..", "shell", "key-bindings.bash"), path.join(os.homedir(), ".sa", "key-bindings.bash"));
break;
case Shell.Powershell:
const powershellConfigPath = path.join(os.homedir(), "Documents", "WindowsPowershell", "Microsoft.PowerShell_profile.ps1");
await fsAsync.appendFile(powershellConfigPath, `\n${powershellScriptCommand()}`);
await fsAsync.copyFile(
path.join(__dirname, "..", "..", "shell", "key-bindings-powershell.ps1"),
path.join(os.homedir(), ".sa", "key-bindings-powershell.ps1")
);
break;
case Shell.Pwsh:
await fsAsync.appendFile(pwshConfigPath(), `\n${pwshScriptCommand()}`);
await fsAsync.copyFile(path.join(__dirname, "..", "..", "shell", "key-bindings-pwsh.ps1"), path.join(os.homedir(), ".sa", "key-bindings-pwsh.ps1"));
break;
}
};