short format of ls command is done and working, could use a rewrite and cleanup but later, some flags implemented for the short format.
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import { Bash, ExitCode, type Permission, type TimeStamps } from '../bash';
|
import { Bash, ExitCode, type Permission, type TimeStamps } from '../bash';
|
||||||
import { Type, type NodePerms, type TreeNode } from '../fs';
|
import { Type, VirtualFS, type NodePerms, type TreeNode } from '../fs';
|
||||||
import { Sort, SortNodeBy } from '../sort';
|
import { Sort, SortNodeBy } from '../sort';
|
||||||
import type { CommandArgs, ICommand, Result, resultData } from '../static';
|
import type { CommandArgs, ICommand, Result, resultData } from '../static';
|
||||||
|
|
||||||
@@ -38,6 +38,7 @@ const months: readonly string[] = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const cmd_ls = function (this: Bash, args: CommandArgs): Result {
|
export const cmd_ls = function (this: Bash, args: CommandArgs): Result {
|
||||||
|
const Fs = this.getFs();
|
||||||
const resultData: resultData = { cmd: 'ls', data: null, args: args };
|
const resultData: resultData = { cmd: 'ls', data: null, args: args };
|
||||||
const result: Result = { exitCode: ExitCode.ERROR, path: this.getCwd(), data: resultData };
|
const result: Result = { exitCode: ExitCode.ERROR, path: this.getCwd(), data: resultData };
|
||||||
const nodes: TreeNode[] = [];
|
const nodes: TreeNode[] = [];
|
||||||
@@ -55,10 +56,10 @@ export const cmd_ls = function (this: Bash, args: CommandArgs): Result {
|
|||||||
this.throwError(result); //No such flag/s
|
this.throwError(result); //No such flag/s
|
||||||
}
|
}
|
||||||
|
|
||||||
if (args.args.length === 0) nodes.push(this.getFs().getNodeByINode(this.getFs().cwd));
|
if (args.args.length === 0) nodes.push(Fs.getNodeByINode(Fs.cwd));
|
||||||
|
|
||||||
for (let i = 0; i < args.args.length; i++) {
|
for (let i = 0; i < args.args.length; i++) {
|
||||||
const node = this.getFs().resolvePath(args.args[i]);
|
const node = Fs.resolvePath(args.args[i]);
|
||||||
if (node === null) this.throwError(result); //no such path (i think this will never occur as backed methods have error cases implemented - which is wrong)
|
if (node === null) this.throwError(result); //no such path (i think this will never occur as backed methods have error cases implemented - which is wrong)
|
||||||
|
|
||||||
nodes.push(node);
|
nodes.push(node);
|
||||||
@@ -71,16 +72,18 @@ export const cmd_ls = function (this: Bash, args: CommandArgs): Result {
|
|||||||
|
|
||||||
function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement {
|
function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement {
|
||||||
const dummysonoerror: HTMLElement = document.createElement('div');
|
const dummysonoerror: HTMLElement = document.createElement('div');
|
||||||
|
const Fs: VirtualFS = this.getFs();
|
||||||
|
|
||||||
|
|
||||||
const flagInfo = checkFlags(args.flags);
|
const flagInfo = checkFlags(args.flags);
|
||||||
const nodes: TreeNode[] = data;
|
let nodes: TreeNode[] = data;
|
||||||
|
|
||||||
const f_a: boolean = flagInfo.has('a') || flagInfo.has('all');
|
const f_a: boolean = flagInfo.has('a') || flagInfo.has('all');
|
||||||
const f_A: boolean = flagInfo.has('A') || flagInfo.has('almost-all');
|
const f_A: boolean = flagInfo.has('A') || flagInfo.has('almost-all');
|
||||||
const f_G: boolean = flagInfo.has('G') || flagInfo.has('no-group');
|
const f_G: boolean = flagInfo.has('G') || flagInfo.has('no-group');
|
||||||
const f_h: boolean = flagInfo.has('h') || flagInfo.has('human-readable');
|
const f_h: boolean = flagInfo.has('h') || flagInfo.has('human-readable');
|
||||||
const f_r: boolean = flagInfo.has('r') || flagInfo.has('reverse');
|
const f_r: boolean = flagInfo.has('r') || flagInfo.has('reverse');
|
||||||
|
const f_R: boolean = flagInfo.has('R') || flagInfo.has('recursive');
|
||||||
const f_Q: boolean = flagInfo.has('Q') || flagInfo.has('quote-name');
|
const f_Q: boolean = flagInfo.has('Q') || flagInfo.has('quote-name');
|
||||||
const f_n: boolean = flagInfo.has('n') || flagInfo.has('numeric-uid-gid');
|
const f_n: boolean = flagInfo.has('n') || flagInfo.has('numeric-uid-gid');
|
||||||
const f_N: boolean = flagInfo.has('N') || flagInfo.has('literal');
|
const f_N: boolean = flagInfo.has('N') || flagInfo.has('literal');
|
||||||
@@ -104,11 +107,29 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement {
|
|||||||
|
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
const elem: HTMLElement = document.createElement('div');
|
const elem: HTMLElement = document.createElement('div');
|
||||||
const children: TreeNode[] = node.children.map((child) => this.getFs().getNodeByINode(child));
|
const childrenMap: TreeNode[] = node.children.map((child) => Fs.getNodeByINode(child));
|
||||||
|
|
||||||
|
const children: TreeNode[] = (f_a || f_A)
|
||||||
|
? childrenMap
|
||||||
|
: childrenMap.filter((child) => !child.name.startsWith('.'));
|
||||||
|
|
||||||
const timeArg = valuedArgs.find((flag) => flag.startsWith('time'));
|
const timeArg = valuedArgs.find((flag) => flag.startsWith('time'));
|
||||||
const shouldNamesShift: boolean = children.some((child) => child.name.match(/\s/) !== null);
|
const shouldNamesShift: boolean = children.some((child) => child.name.match(/\s/) !== null);
|
||||||
let timestamp: SortNodeBy.ATIME | SortNodeBy.CTIME | SortNodeBy.MTIME = SortNodeBy.MTIME;
|
let timestamp: SortNodeBy.ATIME | SortNodeBy.CTIME | SortNodeBy.MTIME = SortNodeBy.MTIME;
|
||||||
|
|
||||||
|
if(f_a && !f_A) {
|
||||||
|
const current: TreeNode = node;
|
||||||
|
current.name = '.';
|
||||||
|
|
||||||
|
let parent: TreeNode = current;
|
||||||
|
if(node.parent) {
|
||||||
|
parent = Fs.getNodeByINode(node.parent);
|
||||||
|
parent.name = '..';
|
||||||
|
}
|
||||||
|
|
||||||
|
children.unshift(current, parent);
|
||||||
|
}
|
||||||
|
|
||||||
if(timeArg) {
|
if(timeArg) {
|
||||||
let value: string = timeArg.split('=')[1];
|
let value: string = timeArg.split('=')[1];
|
||||||
if (value && isValidNodeTimestamp(value)) timestamp = value;
|
if (value && isValidNodeTimestamp(value)) timestamp = value;
|
||||||
@@ -138,8 +159,6 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement {
|
|||||||
...children.map((child) => child.size.toString().length));
|
...children.map((child) => child.size.toString().length));
|
||||||
|
|
||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
if (child.name.startsWith('.') && !(f_a || f_A)) continue;
|
|
||||||
|
|
||||||
const entry: LsEntry = {
|
const entry: LsEntry = {
|
||||||
inode: null,
|
inode: null,
|
||||||
perms: formatPermission(child),
|
perms: formatPermission(child),
|
||||||
@@ -155,63 +174,19 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement {
|
|||||||
rows.push(LsEntryUtils.toString(entry));
|
rows.push(LsEntryUtils.toString(entry));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (f_a && !f_A) {
|
|
||||||
const current: LsEntry = {
|
|
||||||
inode: null,
|
|
||||||
perms: formatPermission(node),
|
|
||||||
children: formatChildren(node),
|
|
||||||
owners: formatOwners.call(this, node, flagInfo),
|
|
||||||
size: formatSize.call(this, f_h, node, maxSizeWidth, f_si),
|
|
||||||
modt: formatModtime(node, timestamp),
|
|
||||||
name: shouldNamesShift ? ' .' : '.'
|
|
||||||
};
|
|
||||||
let parent: LsEntry = {
|
|
||||||
...current,
|
|
||||||
name: shouldNamesShift ? ' ..' : '..'
|
|
||||||
};
|
|
||||||
if (node.parent) {
|
|
||||||
const parentNode: TreeNode = this.getFs().getNodeByINode(node.parent);
|
|
||||||
parent = {
|
|
||||||
inode: null,
|
|
||||||
perms: formatPermission(parentNode),
|
|
||||||
children: formatChildren(parentNode),
|
|
||||||
owners: formatOwners.call(this, parentNode, flagInfo),
|
|
||||||
size: formatSize.call(this, f_h, parentNode, maxSizeWidth, f_si),
|
|
||||||
modt: formatModtime(parentNode, timestamp),
|
|
||||||
name: shouldNamesShift ? ' ..' : '..'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (f_i) {
|
|
||||||
current.inode = node.inode;
|
|
||||||
parent.inode = node.parent ? node.parent : node.inode;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (f_r) {
|
|
||||||
rows.push(LsEntryUtils.toString(parent), LsEntryUtils.toString(current));
|
|
||||||
} else {
|
|
||||||
rows.unshift(LsEntryUtils.toString(current), LsEntryUtils.toString(parent));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO: Calculate the total size of contents in the node
|
//TODO: Calculate the total size of contents in the node
|
||||||
rows.unshift('total ' + node.children.length.toString());
|
rows.unshift('total ' + node.children.length.toString());
|
||||||
|
|
||||||
if (nodes.length > 1) {
|
if (nodes.length > 1) {
|
||||||
const nodePath: string =
|
const nodePath: string =
|
||||||
node.name === '/' ? '/:' : `${this.getFs().getPathByInode(node.inode).slice(1)}:`;
|
node.name === '/' ? '/:' : `${Fs.getPathByInode(node.inode).slice(1)}:`;
|
||||||
rows.unshift(nodePath);
|
rows.unshift(nodePath);
|
||||||
rows.push('\n');
|
rows.push('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
for (let i = 0; i < rows.length; i++) {
|
for (let i = 0; i < rows.length; i++) {
|
||||||
const p: HTMLElement = document.createElement('p');
|
const p: HTMLElement = document.createElement('p');
|
||||||
|
|
||||||
|
|
||||||
p.innerText = rows[i];
|
p.innerText = rows[i];
|
||||||
|
|
||||||
elem.appendChild(p);
|
elem.appendChild(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,55 +196,61 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement {
|
|||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
const maxWidth: number = Math.ceil((this.getTerminalWidth() / this.getTerminalFontSize()) * 0.8);
|
const maxWidth: number = Math.ceil((this.getTerminalWidth() / this.getTerminalFontSize()) * 0.8);
|
||||||
const nameLengths: number[] = children.map(node => node.name.length);
|
|
||||||
|
|
||||||
let columns: string[][] = [];
|
let columns: string[][] = [];
|
||||||
let colWidths: number[];
|
let colWidths: number[];
|
||||||
let lowBound: number = 1;
|
let lowBound: number = 1;
|
||||||
let highBound: number = Math.floor(maxWidth / 3);
|
let highBound: number = children.length;
|
||||||
|
|
||||||
|
|
||||||
while(lowBound + 1 < highBound) {
|
while(lowBound + 1 < highBound) {
|
||||||
let c: number = Math.ceil((lowBound + highBound) / 2);
|
const c = lowBound + (highBound - lowBound) / 2;
|
||||||
|
|
||||||
columns = [];
|
columns = [];
|
||||||
colWidths = [];
|
colWidths = [];
|
||||||
|
let fileIndex = 0;
|
||||||
|
|
||||||
for(let i = 0; i < c; i++) {
|
for(let i = 0; i < c; i++) {
|
||||||
|
if(fileIndex >= children.length) break;
|
||||||
columns[i] = [];
|
columns[i] = [];
|
||||||
for(let j = 0; j < Math.ceil((children.length / c)); j++){
|
for(let j = 0; j < Math.ceil(children.length / c); j++) {
|
||||||
const fileIndex: number = i * Math.floor((children.length / c)) + j;
|
if(fileIndex >= children.length) break;
|
||||||
columns[i].push(children[fileIndex].name);
|
columns[i].push(children[fileIndex].name);
|
||||||
|
fileIndex++;
|
||||||
}
|
}
|
||||||
colWidths.push(Math.max(...columns[i].map((string) => string.length)));
|
colWidths.push(Math.max(...columns[i].map((name) => name.length)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let calcWidth: number =
|
const calcWidth: number = colWidths.reduce((prev, curr) => prev + curr) + ((c-1) * 2);
|
||||||
Math.ceil(colWidths.reduce((result, value) => result + value) + ((c-1) * 2));
|
|
||||||
|
|
||||||
if(calcWidth <= maxWidth) {
|
if(calcWidth < maxWidth) lowBound = c + 1;
|
||||||
lowBound = c;
|
else if(calcWidth > maxWidth) highBound = c -1
|
||||||
}
|
|
||||||
else highBound = c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const wrapper: HTMLElement = document.createElement('div');
|
const wrapper: HTMLElement = document.createElement('div');
|
||||||
wrapper.style.display = 'flex';
|
wrapper.style.display = 'flex';
|
||||||
wrapper.style.columnGap = `${this.getTerminalFontSize() * 2}px`;
|
wrapper.style.columnGap = `${this.getTerminalFontSize() * 2}px`;
|
||||||
|
|
||||||
for(let i = 0; i < columns.length -1; i++) {
|
let fileIndex = 0;
|
||||||
|
|
||||||
|
for(let i = 0; i < lowBound; i++) {
|
||||||
|
if(fileIndex >= children.length) break;
|
||||||
const col: HTMLElement = document.createElement('div');
|
const col: HTMLElement = document.createElement('div');
|
||||||
for(let j = 0; j < columns[i].length -1; j++) {
|
|
||||||
const name: HTMLElement = document.createElement('p');
|
for(let j = 0; j < Math.ceil(children.length / lowBound); j++) {
|
||||||
name.innerText = columns[i][j];
|
if(fileIndex >= children.length) break;
|
||||||
col.appendChild(name);
|
|
||||||
}
|
const entry: HTMLElement = document.createElement('p');
|
||||||
|
|
||||||
|
entry.innerText = formatName(children[fileIndex], flagInfo, shouldNamesShift);
|
||||||
|
col.appendChild(entry);
|
||||||
|
fileIndex++;
|
||||||
|
}
|
||||||
wrapper.appendChild(col);
|
wrapper.appendChild(col);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nodes.length > 1) {
|
if (nodes.length > 1) {
|
||||||
const nodePath: HTMLElement = document.createElement('p');
|
const nodePath: HTMLElement = document.createElement('p');
|
||||||
nodePath.innerText = node.name === '/' ? '/:' : `${this.getFs().getPathByInode(node.inode).slice(1)}:`;
|
nodePath.innerText = node.name === '/' ? '/:' : `${Fs.getPathByInode(node.inode).slice(1)}:`;
|
||||||
w.appendChild(nodePath);
|
w.appendChild(nodePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,36 +259,6 @@ function result_ls(this: Bash, data: any, args: CommandArgs): HTMLElement {
|
|||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
function nodeBinarySearch(
|
|
||||||
n: number,
|
|
||||||
nameLengths: number[],
|
|
||||||
low: number,
|
|
||||||
high: number,
|
|
||||||
max: number): number {
|
|
||||||
|
|
||||||
let cols: number = Math.floor((low + high) / 2);
|
|
||||||
|
|
||||||
console.log("new bounds", low, high);
|
|
||||||
|
|
||||||
if(low + 1 < high) {
|
|
||||||
|
|
||||||
|
|
||||||
const calcWidth: number =
|
|
||||||
Math.ceil(nameLengths.reduce((result, value) => result + value) + ((cols-1) * 2));
|
|
||||||
|
|
||||||
console.log(calcWidth, "calcWidth");
|
|
||||||
console.log(cols);
|
|
||||||
|
|
||||||
if (calcWidth <= max) {
|
|
||||||
low = cols;
|
|
||||||
} else high = cols;
|
|
||||||
|
|
||||||
nodeBinarySearch(n, nameLengths, low, high, max);
|
|
||||||
}
|
|
||||||
console.log("out values", low, cols);
|
|
||||||
return low
|
|
||||||
}
|
|
||||||
|
|
||||||
function isValidNodeSortMethod(value: string): value is SortNodeBy {
|
function isValidNodeSortMethod(value: string): value is SortNodeBy {
|
||||||
return Object.values(SortNodeBy).includes(value as SortNodeBy);
|
return Object.values(SortNodeBy).includes(value as SortNodeBy);
|
||||||
}
|
}
|
||||||
@@ -412,6 +363,25 @@ function formatName(node: TreeNode, flag: any, shouldShift: boolean) {
|
|||||||
return flag.has('p') && node.type === Type.Directory ? `${name}/` : name;
|
return flag.has('p') && node.type === Type.Directory ? `${name}/` : name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* function formatOutputShort(node: TreeNode, flag: any) {
|
||||||
|
let output: string = node.name;
|
||||||
|
const char: string = flag.has('Q') ? '"' : "'";
|
||||||
|
|
||||||
|
if (/\s/.test(node.name)) {
|
||||||
|
if(flag.has('i')) {
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
output = `${char}${node.name}${char}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(flag.has('i')) {
|
||||||
|
output = `${node.inode} ${output}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return flag.has('p') && node.type === Type.Directory ? ` ${output}/ ` : output;
|
||||||
|
} */
|
||||||
|
|
||||||
const checkFlags = (pFlags: string[]) => {
|
const checkFlags = (pFlags: string[]) => {
|
||||||
const flagSet = new Set(pFlags);
|
const flagSet = new Set(pFlags);
|
||||||
|
|
||||||
@@ -436,6 +406,7 @@ export const ls: ICommand = {
|
|||||||
't',
|
't',
|
||||||
'S',
|
'S',
|
||||||
'r',
|
'r',
|
||||||
|
'R',
|
||||||
'Q',
|
'Q',
|
||||||
'p',
|
'p',
|
||||||
'o',
|
'o',
|
||||||
@@ -452,6 +423,7 @@ export const ls: ICommand = {
|
|||||||
'no-group',
|
'no-group',
|
||||||
'human-readable',
|
'human-readable',
|
||||||
'reverse',
|
'reverse',
|
||||||
|
'recursive',
|
||||||
'quote-name',
|
'quote-name',
|
||||||
'indicator-style',
|
'indicator-style',
|
||||||
'literal',
|
'literal',
|
||||||
|
|||||||
Reference in New Issue
Block a user