Skip to content

Commit 48d2fed

Browse files
aliases!
1 parent 41945fd commit 48d2fed

8 files changed

Lines changed: 148 additions & 8 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "ollieos",
3-
"version": "1.0.5",
3+
"version": "1.1.0",
44
"description": "",
55
"private": true,
66
"main": "server.js",

src/programs/@ALL.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ export { default as touch } from "./touch";
2727
export { default as mkdir } from "./mkdir";
2828
export { default as mv } from "./mv";
2929
export { default as window } from "./window";
30+
export { default as alias } from "./alias";
31+
export { default as unalias } from "./unalias";
3032

3133
// TODO: copy program
3234
// TODO: create an API for creating programs, mount any programs found in /bin/ (dont list in help)

src/programs/alias.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import type { Program } from "../types";
2+
import {alias} from "./@ALL";
3+
4+
export default {
5+
name: "alias",
6+
description: "Define or display aliases. (Use .ollierc to persist aliases)",
7+
usage_suffix: "[name[=value] ...]",
8+
arg_descriptions: {
9+
name: "The name of the alias to define or display. If no arguments are given, all aliases are displayed. Multiple alias arguments can be provided.",
10+
"name=value": "Defines an alias with the given name and value. End the value with a space to allow chaining."
11+
},
12+
main: async (data) => {
13+
// extract from data to make code less verbose
14+
const { term } = data;
15+
16+
// extract from ANSI to make code less verbose
17+
const { STYLE, PREFABS } = term.ansi;
18+
19+
if (data.args.length === 0) {
20+
// display all aliases
21+
const aliases = term.list_aliases();
22+
for (const [name, value] of aliases.entries()) {
23+
term.writeln(`alias ${name}='${value}'`);
24+
}
25+
26+
return 0;
27+
}
28+
29+
for (const arg of data.args) {
30+
if (arg.includes("=")) {
31+
// define alias
32+
const [name, ...value_parts] = arg.split("=");
33+
const value = value_parts.join("=");
34+
35+
// remove surrounding quotes if present
36+
let final_value = value;
37+
if ((final_value.startsWith("'") && final_value.endsWith("'")) ||
38+
(final_value.startsWith('"') && final_value.endsWith('"'))) {
39+
final_value = final_value.slice(1, -1);
40+
}
41+
42+
term.set_alias(name, final_value);
43+
} else {
44+
// display alias
45+
const value = term.get_alias(arg);
46+
if (value) {
47+
term.writeln(`alias ${arg}='${value}'`);
48+
} else {
49+
term.writeln(`${PREFABS.error}alias: ${arg}: not found${STYLE.reset_all}`);
50+
}
51+
}
52+
}
53+
54+
return 0;
55+
}
56+
} as Program;

src/programs/pkg/add.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export const add_subcommand = async (data: ProgramMainData, depended_by?: PkgAtV
110110
// uninstall old version
111111
term.writeln(`${FG.yellow}Uninstalling old ${pkg_name}@${pkg_version}...${STYLE.reset_all}`);
112112

113-
const remove_data = {term, args: ["remove", pkg_name], unsubbed_args: ["remove", pkg_name]};
113+
const remove_data = {term, args: ["remove", pkg_name], unsubbed_args: ["remove", pkg_name], raw_parts: [...data.raw_parts, "remove", pkg_name]};
114114
const remove_exit_code = await remove_subcommand(remove_data);
115115
if (remove_exit_code !== 0) {
116116
term.writeln(`${PREFABS.error}Failed to uninstall old version.${STYLE.reset_all}`);
@@ -134,7 +134,7 @@ export const add_subcommand = async (data: ProgramMainData, depended_by?: PkgAtV
134134
virtual_args.unshift("add");
135135

136136
// we need to also pass the name of the dependent package to the virtual call to let the graph know
137-
const virtual_data = {term, args: virtual_args, unsubbed_args: virtual_args};
137+
const virtual_data = {term, args: virtual_args, unsubbed_args: virtual_args, raw_parts: [...data.raw_parts, ...virtual_args]};
138138
const virtual_exit_code = await add_subcommand(virtual_data, pkg_at_version as PkgAtVersion);
139139

140140
if (virtual_exit_code !== 0) {

src/programs/unalias.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { Program } from "../types";
2+
3+
export default {
4+
name: "unalias",
5+
description: "Remove defined aliases.",
6+
usage_suffix: "name [name ...]",
7+
arg_descriptions: {
8+
name: "The name of the alias to remove. Multiple alias names can be provided."
9+
},
10+
main: async (data) => {
11+
// extract from data to make code less verbose
12+
const { term } = data;
13+
14+
// extract from ANSI to make code less verbose
15+
const { STYLE, PREFABS } = term.ansi;
16+
17+
if (data.args.length === 0) {
18+
term.writeln(`${PREFABS.error}unalias: usage: unalias name [name ...]${STYLE.reset_all}`);
19+
return 1;
20+
}
21+
22+
for (const arg of data.args) {
23+
const success = term.unset_alias(arg);
24+
if (!success) {
25+
term.writeln(`${PREFABS.error}unalias: ${arg}: not found${STYLE.reset_all}`);
26+
}
27+
}
28+
29+
return 0;
30+
}
31+
} as Program;

src/term_ctl.ts

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ export class WrappedTerminal extends Terminal {
115115
_is_handling_key_events = false;
116116

117117
_vars: Map<string, string> = new Map();
118+
_aliases: Map<string, string> = new Map();
118119

119120
// TODO: this exporting is a bit lazy, but it works for now
120121

@@ -163,8 +164,24 @@ export class WrappedTerminal extends Terminal {
163164
this._vars.set(name, value);
164165
}
165166

166-
unset_variable(name: string): void {
167-
this._vars.delete(name);
167+
unset_variable(name: string): boolean {
168+
return this._vars.delete(name);
169+
}
170+
171+
list_aliases(): Map<string, string> {
172+
return this._aliases;
173+
}
174+
175+
get_alias(name: string): string | undefined {
176+
return this._aliases.get(name);
177+
}
178+
179+
set_alias(name: string, value: string): void {
180+
this._aliases.set(name, value);
181+
}
182+
183+
unset_alias(name: string): boolean {
184+
return this._aliases.delete(name);
168185
}
169186

170187

@@ -248,6 +265,39 @@ export class WrappedTerminal extends Terminal {
248265
// remove leading and trailing whitespace and split by spaces, unless contained in single or double quotes
249266
// TODO: use a proper stack based parser for readability and maintainability
250267
const sub = line.trim().split(/ +(?=(?:(?:[^"']*["'][^"']*["'])*[^"']*$))/);
268+
const raw_parts = sub.slice();
269+
270+
// handle aliases
271+
// for each part, check if it's an alias, and if so, replace it with the value
272+
// if the value ends with a space, check the next part as well
273+
for (let i = 0; i < sub.length; i++) {
274+
const part = sub[i];
275+
const alias_value = this.get_alias(part);
276+
277+
if (!alias_value) {
278+
// not an alias, abort (alias only applies to the first word unless chaining)
279+
break;
280+
}
281+
282+
// split the alias value into parts
283+
const alias_parts = alias_value.split(/ +(?=(?:(?:[^"']*["'][^"']*["'])*[^"']*$))/);
284+
285+
// if ends with a space, remove the trailing empty part
286+
if (alias_value.endsWith(" ")) {
287+
alias_parts.pop();
288+
}
289+
290+
// remove the current part and insert the alias parts
291+
sub.splice(i, 1, ...alias_parts);
292+
293+
// adjust the index to account for the new parts
294+
i += alias_parts.length - 1;
295+
296+
// if the alias value ends with a space, check the next part as well
297+
if (!alias_value.endsWith(" ")) {
298+
break;
299+
}
300+
}
251301

252302
const skip_variable_sub_idxs = [];
253303

@@ -271,7 +321,6 @@ export class WrappedTerminal extends Terminal {
271321
}
272322
}
273323

274-
275324
// the first word is the command, the rest are arguments
276325
const command = sub[0];
277326

@@ -344,6 +393,7 @@ export class WrappedTerminal extends Terminal {
344393
term: this,
345394
args,
346395
unsubbed_args,
396+
raw_parts,
347397
}
348398

349399
let old_title = "";

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export interface ProgramMainData {
44
term: WrappedTerminal,
55
args: string[],
66
unsubbed_args: string[],
7+
raw_parts: string[], // raw command parts, including program name and unparsed args
78
}
89

910
export type ProgramMain = (data: ProgramMainData) => Promise<number>;

0 commit comments

Comments
 (0)