todoosy 0.3.2 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/formatter.d.ts +5 -0
- package/dist/cjs/formatter.js +202 -0
- package/dist/cjs/index.d.ts +13 -0
- package/dist/cjs/index.js +29 -0
- package/dist/cjs/linter.d.ts +6 -0
- package/dist/cjs/linter.js +518 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/parser.d.ts +11 -0
- package/dist/cjs/parser.js +719 -0
- package/dist/cjs/query.d.ts +22 -0
- package/dist/cjs/query.js +153 -0
- package/dist/cjs/scheme.d.ts +7 -0
- package/dist/cjs/scheme.js +14 -0
- package/dist/cjs/sequence.d.ts +53 -0
- package/dist/cjs/sequence.js +233 -0
- package/dist/cjs/settings.d.ts +65 -0
- package/dist/cjs/settings.js +262 -0
- package/dist/cjs/types.d.ts +102 -0
- package/dist/cjs/types.js +5 -0
- package/dist/formatter.d.ts.map +1 -1
- package/dist/formatter.js +15 -6
- package/dist/formatter.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/linter.d.ts.map +1 -1
- package/dist/linter.js +56 -0
- package/dist/linter.js.map +1 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +6 -0
- package/dist/parser.js.map +1 -1
- package/dist/sequence.d.ts +54 -0
- package/dist/sequence.d.ts.map +1 -0
- package/dist/sequence.js +226 -0
- package/dist/sequence.js.map +1 -0
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +10 -4
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Todoosy Formatter
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.format = format;
|
|
7
|
+
const parser_js_1 = require("./parser.js");
|
|
8
|
+
function parseMiscLocation(misc) {
|
|
9
|
+
const slashIndex = misc.indexOf('/');
|
|
10
|
+
if (slashIndex === -1) {
|
|
11
|
+
return { filename: misc, heading: 'Misc' };
|
|
12
|
+
}
|
|
13
|
+
return {
|
|
14
|
+
filename: misc.substring(0, slashIndex),
|
|
15
|
+
heading: misc.substring(slashIndex + 1),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function formatMetadata(metadata) {
|
|
19
|
+
const parts = [];
|
|
20
|
+
if (metadata.due) {
|
|
21
|
+
const softPrefix = metadata.due_soft ? '~' : '';
|
|
22
|
+
parts.push(`due ${softPrefix}${metadata.due}`);
|
|
23
|
+
}
|
|
24
|
+
if (metadata.progress) {
|
|
25
|
+
parts.push(metadata.progress);
|
|
26
|
+
}
|
|
27
|
+
if (metadata.priority !== null) {
|
|
28
|
+
parts.push(`p${metadata.priority}`);
|
|
29
|
+
}
|
|
30
|
+
if (metadata.estimate_minutes !== null) {
|
|
31
|
+
const minutes = metadata.estimate_minutes;
|
|
32
|
+
if (minutes % 480 === 0 && minutes >= 480) {
|
|
33
|
+
parts.push(`${minutes / 480}d`);
|
|
34
|
+
}
|
|
35
|
+
else if (minutes % 60 === 0 && minutes >= 60) {
|
|
36
|
+
parts.push(`${minutes / 60}h`);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
parts.push(`${minutes}m`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Output direct hashtags (not effective_hashtags) at end
|
|
43
|
+
for (const tag of metadata.hashtags) {
|
|
44
|
+
parts.push(`#${tag}`);
|
|
45
|
+
}
|
|
46
|
+
return parts.length > 0 ? `(${parts.join(' ')})` : '';
|
|
47
|
+
}
|
|
48
|
+
function formatItemLine(item, indent = 0, sequenceNum) {
|
|
49
|
+
const indentStr = ' '.repeat(indent);
|
|
50
|
+
const metaStr = formatMetadata(item.metadata);
|
|
51
|
+
const titleWithMeta = metaStr ? `${item.title_text} ${metaStr}` : item.title_text;
|
|
52
|
+
if (item.type === 'heading') {
|
|
53
|
+
const hashes = '#'.repeat(item.level || 1);
|
|
54
|
+
return `${hashes} ${titleWithMeta}`;
|
|
55
|
+
}
|
|
56
|
+
// Use numbered marker if item is numbered
|
|
57
|
+
if (item.marker_type === 'numbered' && sequenceNum !== undefined) {
|
|
58
|
+
return `${indentStr}${sequenceNum}. ${titleWithMeta}`;
|
|
59
|
+
}
|
|
60
|
+
return `${indentStr}- ${titleWithMeta}`;
|
|
61
|
+
}
|
|
62
|
+
function formatComments(comments, isListItem, indent) {
|
|
63
|
+
if (comments.length === 0)
|
|
64
|
+
return [];
|
|
65
|
+
if (isListItem) {
|
|
66
|
+
// Indent is the list item's indent level; comments need +1 level
|
|
67
|
+
const indentStr = ' '.repeat(indent + 1);
|
|
68
|
+
return comments.map(c => `${indentStr}${c}`);
|
|
69
|
+
}
|
|
70
|
+
// Heading comments are not indented
|
|
71
|
+
return comments;
|
|
72
|
+
}
|
|
73
|
+
function format(text, scheme, filename) {
|
|
74
|
+
const { ast } = (0, parser_js_1.parse)(text);
|
|
75
|
+
const lines = [];
|
|
76
|
+
const itemMap = new Map();
|
|
77
|
+
for (const item of ast.items) {
|
|
78
|
+
itemMap.set(item.id, item);
|
|
79
|
+
}
|
|
80
|
+
// Determine misc location from scheme or use default
|
|
81
|
+
const miscLocation = parseMiscLocation(scheme?.misc ?? 'todoosy.md/Misc');
|
|
82
|
+
// If no filename provided, assume it could be the misc file (backward compatibility)
|
|
83
|
+
const isMiscFile = filename === undefined || filename === miscLocation.filename;
|
|
84
|
+
// Determine formatting style: roomy (default), balanced, or tight
|
|
85
|
+
const formattingStyle = scheme?.formatting_style ?? 'roomy';
|
|
86
|
+
// Track Misc section
|
|
87
|
+
let miscSectionId = null;
|
|
88
|
+
// Find existing Misc section (using configured heading name)
|
|
89
|
+
for (const item of ast.items) {
|
|
90
|
+
if (item.type === 'heading' && item.title_text === miscLocation.heading && item.level === 1) {
|
|
91
|
+
miscSectionId = item.id;
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Helper to determine if we should add blank line before a heading
|
|
96
|
+
function shouldAddBlankBefore(item) {
|
|
97
|
+
if (item.type !== 'heading')
|
|
98
|
+
return false;
|
|
99
|
+
if (formattingStyle === 'tight')
|
|
100
|
+
return false;
|
|
101
|
+
if (formattingStyle === 'balanced')
|
|
102
|
+
return item.level === 1;
|
|
103
|
+
return true; // roomy
|
|
104
|
+
}
|
|
105
|
+
// Helper to determine if we should add blank line after a heading
|
|
106
|
+
function shouldAddBlankAfter(item) {
|
|
107
|
+
if (item.type !== 'heading')
|
|
108
|
+
return false;
|
|
109
|
+
if (formattingStyle === 'tight')
|
|
110
|
+
return false;
|
|
111
|
+
if (formattingStyle === 'balanced')
|
|
112
|
+
return item.level === 1;
|
|
113
|
+
return true; // roomy
|
|
114
|
+
}
|
|
115
|
+
// Helper to format an item and its subtree
|
|
116
|
+
function formatItem(id, listIndent = 0, isUnderMisc = false, sequenceNum) {
|
|
117
|
+
const item = itemMap.get(id);
|
|
118
|
+
// Skip Misc section during normal iteration (we'll add it at the end)
|
|
119
|
+
if (item.id === miscSectionId && !isUnderMisc) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
// Add blank line before headings (except at start), based on style
|
|
123
|
+
if (shouldAddBlankBefore(item) && lines.length > 0) {
|
|
124
|
+
if (lines[lines.length - 1] !== '') {
|
|
125
|
+
lines.push('');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
lines.push(formatItemLine(item, listIndent, sequenceNum));
|
|
129
|
+
// Add blank line after heading before comments or children, based on style
|
|
130
|
+
if (shouldAddBlankAfter(item)) {
|
|
131
|
+
lines.push('');
|
|
132
|
+
}
|
|
133
|
+
// Add comments
|
|
134
|
+
const formattedComments = formatComments(item.comments, item.type === 'list', listIndent);
|
|
135
|
+
lines.push(...formattedComments);
|
|
136
|
+
// Add blank line after heading comments before children
|
|
137
|
+
if (item.type === 'heading' && item.comments.length > 0 && item.children.length > 0) {
|
|
138
|
+
if (shouldAddBlankAfter(item)) {
|
|
139
|
+
lines.push('');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
// Format children - renumber sequenced items
|
|
143
|
+
let childSequenceNum = 1;
|
|
144
|
+
for (const childId of item.children) {
|
|
145
|
+
const child = itemMap.get(childId);
|
|
146
|
+
if (child.type === 'list') {
|
|
147
|
+
// List items under a heading start at indent 0
|
|
148
|
+
// Nested list items increment indent
|
|
149
|
+
const nextIndent = item.type === 'heading' ? 0 : listIndent + 1;
|
|
150
|
+
// Pass sequence number for numbered items and increment
|
|
151
|
+
const childSeq = child.marker_type === 'numbered' ? childSequenceNum++ : undefined;
|
|
152
|
+
formatItem(childId, nextIndent, isUnderMisc, childSeq);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
formatItem(childId, 0, isUnderMisc);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Format all root items except Misc
|
|
160
|
+
for (const rootId of ast.root_ids) {
|
|
161
|
+
if (rootId !== miscSectionId) {
|
|
162
|
+
formatItem(rootId, 0, false);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Add Misc section at the end (only for the misc file)
|
|
166
|
+
if (isMiscFile) {
|
|
167
|
+
// Add blank line before Misc heading based on style
|
|
168
|
+
const miscItem = miscSectionId ? itemMap.get(miscSectionId) : null;
|
|
169
|
+
const shouldAddBlankBeforeMisc = formattingStyle !== 'tight' && (formattingStyle === 'roomy' || formattingStyle === 'balanced');
|
|
170
|
+
if (lines.length > 0 && lines[lines.length - 1] !== '' && shouldAddBlankBeforeMisc) {
|
|
171
|
+
lines.push('');
|
|
172
|
+
}
|
|
173
|
+
lines.push(`# ${miscLocation.heading}`);
|
|
174
|
+
// Add blank line after Misc heading based on style
|
|
175
|
+
const shouldAddBlankAfterMisc = formattingStyle !== 'tight' && (formattingStyle === 'roomy' || formattingStyle === 'balanced');
|
|
176
|
+
// Add Misc items if they exist
|
|
177
|
+
if (miscSectionId) {
|
|
178
|
+
const miscItemNode = itemMap.get(miscSectionId);
|
|
179
|
+
if (miscItemNode.comments.length > 0) {
|
|
180
|
+
if (shouldAddBlankAfterMisc) {
|
|
181
|
+
lines.push('');
|
|
182
|
+
}
|
|
183
|
+
lines.push(...miscItemNode.comments);
|
|
184
|
+
}
|
|
185
|
+
if (miscItemNode.children.length > 0) {
|
|
186
|
+
if (shouldAddBlankAfterMisc) {
|
|
187
|
+
lines.push('');
|
|
188
|
+
}
|
|
189
|
+
let miscChildSeqNum = 1;
|
|
190
|
+
for (const childId of miscItemNode.children) {
|
|
191
|
+
// Format misc children - they start at indent 0
|
|
192
|
+
const child = itemMap.get(childId);
|
|
193
|
+
const childSeq = child.marker_type === 'numbered' ? miscChildSeqNum++ : undefined;
|
|
194
|
+
lines.push(formatItemLine(child, 0, childSeq));
|
|
195
|
+
const formattedComments = formatComments(child.comments, child.type === 'list', 0);
|
|
196
|
+
lines.push(...formattedComments);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return lines.join('\n') + '\n';
|
|
202
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Todoosy - Markdown-based todo system
|
|
3
|
+
*/
|
|
4
|
+
export { parse, parseTokensInParenGroup, extractParenGroups } from './parser.js';
|
|
5
|
+
export type { ParseResult } from './parser.js';
|
|
6
|
+
export { format } from './formatter.js';
|
|
7
|
+
export { lint } from './linter.js';
|
|
8
|
+
export { queryUpcoming, queryMisc, queryByHashtag, listHashtags } from './query.js';
|
|
9
|
+
export type { HashtagItem, HashtagResult, HashtagListResult } from './query.js';
|
|
10
|
+
export { parseScheme, parseSettings } from './settings.js';
|
|
11
|
+
export { analyzeSequence, renumberChildren, insertSequencedItem, removeSequencedItem, convertToSequence, convertToBullets, } from './sequence.js';
|
|
12
|
+
export type { SequenceInfo } from './sequence.js';
|
|
13
|
+
export type { AST, ItemNode, ItemMetadata, Warning, LintResult, UpcomingItem, UpcomingResult, MiscItem, MiscResult, Scheme, Settings, SettingValue, ParsedToken, ParenGroup, } from './types.js';
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Todoosy - Markdown-based todo system
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.convertToBullets = exports.convertToSequence = exports.removeSequencedItem = exports.insertSequencedItem = exports.renumberChildren = exports.analyzeSequence = exports.parseSettings = exports.parseScheme = exports.listHashtags = exports.queryByHashtag = exports.queryMisc = exports.queryUpcoming = exports.lint = exports.format = exports.extractParenGroups = exports.parseTokensInParenGroup = exports.parse = void 0;
|
|
7
|
+
var parser_js_1 = require("./parser.js");
|
|
8
|
+
Object.defineProperty(exports, "parse", { enumerable: true, get: function () { return parser_js_1.parse; } });
|
|
9
|
+
Object.defineProperty(exports, "parseTokensInParenGroup", { enumerable: true, get: function () { return parser_js_1.parseTokensInParenGroup; } });
|
|
10
|
+
Object.defineProperty(exports, "extractParenGroups", { enumerable: true, get: function () { return parser_js_1.extractParenGroups; } });
|
|
11
|
+
var formatter_js_1 = require("./formatter.js");
|
|
12
|
+
Object.defineProperty(exports, "format", { enumerable: true, get: function () { return formatter_js_1.format; } });
|
|
13
|
+
var linter_js_1 = require("./linter.js");
|
|
14
|
+
Object.defineProperty(exports, "lint", { enumerable: true, get: function () { return linter_js_1.lint; } });
|
|
15
|
+
var query_js_1 = require("./query.js");
|
|
16
|
+
Object.defineProperty(exports, "queryUpcoming", { enumerable: true, get: function () { return query_js_1.queryUpcoming; } });
|
|
17
|
+
Object.defineProperty(exports, "queryMisc", { enumerable: true, get: function () { return query_js_1.queryMisc; } });
|
|
18
|
+
Object.defineProperty(exports, "queryByHashtag", { enumerable: true, get: function () { return query_js_1.queryByHashtag; } });
|
|
19
|
+
Object.defineProperty(exports, "listHashtags", { enumerable: true, get: function () { return query_js_1.listHashtags; } });
|
|
20
|
+
var settings_js_1 = require("./settings.js");
|
|
21
|
+
Object.defineProperty(exports, "parseScheme", { enumerable: true, get: function () { return settings_js_1.parseScheme; } });
|
|
22
|
+
Object.defineProperty(exports, "parseSettings", { enumerable: true, get: function () { return settings_js_1.parseSettings; } });
|
|
23
|
+
var sequence_js_1 = require("./sequence.js");
|
|
24
|
+
Object.defineProperty(exports, "analyzeSequence", { enumerable: true, get: function () { return sequence_js_1.analyzeSequence; } });
|
|
25
|
+
Object.defineProperty(exports, "renumberChildren", { enumerable: true, get: function () { return sequence_js_1.renumberChildren; } });
|
|
26
|
+
Object.defineProperty(exports, "insertSequencedItem", { enumerable: true, get: function () { return sequence_js_1.insertSequencedItem; } });
|
|
27
|
+
Object.defineProperty(exports, "removeSequencedItem", { enumerable: true, get: function () { return sequence_js_1.removeSequencedItem; } });
|
|
28
|
+
Object.defineProperty(exports, "convertToSequence", { enumerable: true, get: function () { return sequence_js_1.convertToSequence; } });
|
|
29
|
+
Object.defineProperty(exports, "convertToBullets", { enumerable: true, get: function () { return sequence_js_1.convertToBullets; } });
|