Skip to content

Commit

Permalink
a few comments
Browse files Browse the repository at this point in the history
  • Loading branch information
jacoscaz committed Nov 4, 2024
1 parent 1f6014f commit cd1dba5
Show file tree
Hide file tree
Showing 8 changed files with 144 additions and 98 deletions.
79 changes: 6 additions & 73 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,18 @@
},
"dependencies": {
"argparse": "^2.0.1",
"columnify": "^1.6.0",
"csv-stringify": "^6.5.1",
"js-yaml": "^4.1.0",
"matcher": "^5.0.0",
"mdast-util-from-markdown": "^2.0.1",
"mdast-util-frontmatter": "^2.0.1",
"mdast-util-gfm": "^3.0.0",
"micromark-extension-frontmatter": "^2.0.0",
"micromark-extension-gfm": "^3.0.0"
"micromark-extension-gfm": "^3.0.0",
"simple-wcswidth": "^1.0.1"
},
"devDependencies": {
"@types/argparse": "^2.0.16",
"@types/columnify": "^1.5.4",
"@types/js-yaml": "^4.0.9",
"@types/node": "^22.7.6",
"typescript": "^5.6.3"
Expand Down
16 changes: 12 additions & 4 deletions src/bin.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env node

import type { Item } from './types.js';
import type { Item, RenderOpts } from './types.js';

import { cwd } from 'node:process';
import { resolve } from 'node:path';
Expand Down Expand Up @@ -49,6 +49,10 @@ arg_parser.add_argument('-o', '--out', {
choices: ['tabular', 'csv', 'json'],
help: 'set output format'
});
arg_parser.add_argument('-C', '--column-width', {
required: false,
help: 'set wrapping or truncation for tag: foo(25t)'
});
arg_parser.add_argument('path', {
default: cwd(),
help: 'working directory',
Expand All @@ -67,6 +71,10 @@ const show_tags = cli_args.tags
? ['text', 'hours', 'file', 'date']
: ['text', 'done', 'file', 'date'];

const render_opts: RenderOpts = {

};

const renderItems = (items: Set<Item>) => {
let as_arr = Array.from(items);
if (filter) {
Expand All @@ -77,14 +85,14 @@ const renderItems = (items: Set<Item>) => {
}
switch (cli_args.out) {
case 'json':
console.log(renderJSON(as_arr, show_tags));
console.log(renderJSON(as_arr, show_tags, render_opts));
break;
case 'csv':
console.log(renderCSV(as_arr, show_tags));
console.log(renderCSV(as_arr, show_tags, render_opts));
break;
case 'tabular':
default:
console.log(renderTabular(as_arr, show_tags));
console.log(renderTabular(as_arr, show_tags, render_opts));
}
};

Expand Down
4 changes: 2 additions & 2 deletions src/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const parseTextNode = (node: Text, ctx: ParseFileContext, curr_task: Task | null
let match;
if (!('text' in curr_wlog.internal_tags) && (match = node.value.match(WL_REGEXP))) {
const [full, hours] = match;
const text = node.value.slice(full.length);
const text = node.value.slice(full.length).trim();
curr_wlog.internal_tags.hours = hours;
curr_wlog.internal_tags.text = text;
extractTagsFromText(text, curr_wlog.tags);
Expand All @@ -54,7 +54,7 @@ const parseTextNode = (node: Text, ctx: ParseFileContext, curr_task: Task | null
}
if (curr_task) {
if (!('text' in curr_task.internal_tags)) {
curr_task.internal_tags.text = node.value;
curr_task.internal_tags.text = node.value.trim();
}
extractTagsFromText(node.value, curr_task.tags);
}
Expand Down
77 changes: 66 additions & 11 deletions src/render.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@

import type { Item, RenderItemsFn } from './types.js';

import { EOL } from 'node:os';
import { wcswidth } from 'simple-wcswidth';
import { stringify } from 'csv-stringify/sync';
import columnify from 'columnify';

import { wcslice } from './wcslice.js';

type Row = Record<string, string>;

Expand All @@ -17,17 +20,69 @@ const toRows = (items: Item[], show_tags: string[]): Row[] => {
return items.map(item => toRow(item, show_tags));
};

export const renderTabular: RenderItemsFn = (items, show_tags) => {
const rows = toRows(items, show_tags);
rows.unshift(show_tags.reduce((acc, col) => {
acc[col] = ''.padStart(col.length, '-');
return acc;
}, {} as any));
return columnify(rows, {
columns: show_tags,
columnSplitter: ' | ',
headingTransform: heading => heading,
const row_delimiter = '-';
const column_delimiter = ' | ';

/**
* This rendering function renders items in tabular / columnar form.
* If the "text" tag is selected for display, longer values are truncated and
* ellipsed in a best-effort to fit each line within the width of the terminal.
*/
export const renderTabular: RenderItemsFn = (items, show_tags, opts) => {

// Total number of items to go through
let items_qty = items.length;

// Maximum value lengths per tag to be displayed
const lengths: number[] = show_tags.map(() => 0);

// Array of values per tag to be displayed
const values: string[][] = show_tags.map(() => new Array(items_qty));

// Populate lengths and values
items.forEach((item, i) => {
show_tags.forEach((tag, t) => {
const value = item.tags[tag]?.replaceAll(/\r?\n/g, '') ?? '';
lengths[t] = Math.max(wcswidth(value), lengths[t]);
values[t][i] = value;
});
});

// Additional rows for headers and header delimiter
show_tags.forEach((tag, t) => {
const tag_width = wcswidth(tag);
lengths[t] = Math.max(tag_width, lengths[t]);
values[t].unshift(''.padStart(tag_width, row_delimiter));
values[t].unshift(tag);
});
items_qty += 2;

// Special processing for values of the "text" tag
const text_tag_i = show_tags.findIndex(t => t === 'text');
if (text_tag_i > -1) {
const line_length = lengths.reduce((acc, l) => acc + l, 0)
+ (column_delimiter.length * (lengths.length - 1));
if (line_length >= process.stdout.columns) {
const text_length = Math.max('text'.length, lengths[text_tag_i]
- (line_length - process.stdout.columns));
values[text_tag_i] = values[text_tag_i]
.map(v => wcswidth(v) > text_length ? wcslice(v, 0, text_length - 1) + '…' : v);
lengths[text_tag_i] = text_length;
}
}

const lines = new Array(items_qty);

for (let i = 0; i < items_qty; i += 1) {
lines[i] = wcslice(
show_tags.map((tag, t) => values[t][i].padEnd(lengths[t], ' '))
.join(column_delimiter),
0,
process.stdout.columns,
);
}

return lines.join(EOL);
};

export const renderCSV: RenderItemsFn = (items, show_tags) => {
Expand Down
6 changes: 4 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ export interface TagSortExpression extends BaseTagExpression {
order: 'asc' | 'desc';
}

export interface RenderOpts {}

export interface RenderItemsFn {
(items: Item[], show_tags: string[]): string;
}
(items: Item[], show_tags: string[], opts: RenderOpts): string;
}
49 changes: 49 additions & 0 deletions src/wcslice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@

// Initially vendored from https://github.com/frouriojs/wcslice (MIT)
// at commit f2d133593b1df46aedbcd92ac9af37886a48c90e
// https://github.com/frouriojs/wcslice/blob/f2d133593b1df46aedbcd92ac9af37886a48c90e/src/index.ts

import { wcswidth } from 'simple-wcswidth';

/**
* This treats last zero-length chars as infinitesimal length.
*/
export const wcslice = (str: string, start?: number | undefined, end?: number | undefined) => {
const wclens = str.split('').reduce(
(c, e) => {
c.push(c[c.length - 1] + wcswidth(e));
return c;
},
[0],
);
const wclen = wclens[wclens.length - 1];
if (start === undefined) {
start = 0;
}
if (end === undefined) {
end = wclen + 1;
}
if (end < 0) end = 0;
const strStart = (() => {
let lo = -1;
let hi = str.length;
while (lo + 1 < hi) {
const mi = (lo + hi + 1) >> 1;
if (wclens[mi] >= start) hi = mi;
else lo = mi;
}
return hi;
})();
const strEnd = (() => {
let lo = -1;
let hi = str.length;
while (lo + 1 < hi) {
const mi = (lo + hi + 1) >> 1;
if (wclens[mi] >= end) hi = mi;
else lo = mi;
}
if (wcswidth(str.slice(0, hi)) > end) return hi - 1;
return hi;
})();
return str.slice(strStart, strEnd);
};
6 changes: 3 additions & 3 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */

/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
"allowJs": false, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
"checkJs": false, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */

/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
"declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
Expand Down

0 comments on commit cd1dba5

Please sign in to comment.