todo-jsonl 0.1.11 → 0.1.17
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.
- checksums.yaml +4 -4
- data/bin/todo.rb +325 -284
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 66be9ecc38d960f5207cc45621ea59daf90513823b96082f2d27c7062742c270
|
4
|
+
data.tar.gz: 77fdb42c6675a963a1360e9375e03b0b0d4c7fa2fca1d0721f3337da2276685c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af9f2d586cf93b6642e3f1f99f62e2460e2bbfe1d8a5c659c4835e4c84421733674ce58961581129c75d9b334aac581ae2f92a56f7022f2cdffdfb7f55779806
|
7
|
+
data.tar.gz: 91fe599991ade9a1eccc8fae0ef841b1845d1f47b98bef6f9bc98314120552a86cae1b6c411c715f79525557d8170c5d311f140c26c87fc234990c719a00949e
|
data/bin/todo.rb
CHANGED
@@ -27,325 +27,366 @@
|
|
27
27
|
require 'json'
|
28
28
|
require 'date'
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
NEXT_7_DAYS = (0..6).map do |day| (TODAY.to_date + day) end
|
67
|
-
DATE_FORMAT = '%Y-%m-%d'
|
68
|
-
DUE_DATE_DAYS = NEXT_7_DAYS.map do |day| day.strftime('%A').downcase end
|
69
|
-
DUE_DATES_FOR_QUERIES = NEXT_7_DAYS.map do |day| day.strftime(DATE_FORMAT) end
|
70
|
-
DUE_DATE_DAYS_SIMPLE = ['today', 'tomorrow']
|
71
|
-
|
72
|
-
QUERIES = {
|
73
|
-
':active' => 'state=(new|started|blocked)',
|
74
|
-
':done' => 'state=done',
|
75
|
-
':blocked' => 'state=blocked',
|
76
|
-
':started' => 'state=started',
|
77
|
-
':new' => 'state=new',
|
78
|
-
':all' => 'state=\w+',
|
79
|
-
':today' => "due=#{DUE_DATES_FOR_QUERIES[0]}",
|
80
|
-
':tomorrow' => "due=#{DUE_DATES_FOR_QUERIES[1]}",
|
81
|
-
':next7days' => "due=(#{DUE_DATES_FOR_QUERIES.join('|')})"
|
82
|
-
}
|
83
|
-
|
84
|
-
PRIORITY_FLAG = '*'
|
85
|
-
|
86
|
-
TODO_FILE = "#{ENV['HOME']}/todo.jsonl"
|
87
|
-
|
88
|
-
def usage
|
89
|
-
<<~USAGE
|
90
|
-
Usage: todo <command> <arguments>
|
91
|
-
|
92
|
-
Commands:
|
93
|
-
* add <text> add new task
|
94
|
-
* start <tasknumber> mark task as started
|
95
|
-
* done <tasknumber> mark task as completed
|
96
|
-
* block <tasknumber> mark task as blocked
|
97
|
-
* reset <tasknumber> reset task to new state
|
98
|
-
* prio <tasknumber> toggle high priority flag
|
99
|
-
* due <tasknumber> <date> set due date (in YYYY-MM-DD format)
|
100
|
-
|
101
|
-
* append <tasknumber> <text> append text to task title
|
102
|
-
* rename <tasknumber> <text> rename task
|
103
|
-
* del <tasknumber> delete task
|
104
|
-
* note <tasknumber> <text> add note to task
|
105
|
-
* delnote <tasknumber> delete all notes from task
|
106
|
-
|
107
|
-
* list <regex> [regex...] list tasks (only active tasks by default)
|
108
|
-
* show <tasknumber> show all task details
|
109
|
-
* repl enter read-eval-print loop mode
|
110
|
-
* help this help screen
|
111
|
-
|
112
|
-
With list command the following pre-defined regex patterns can be also used:
|
113
|
-
#{QUERIES.keys.join(', ')}
|
114
|
-
|
115
|
-
Legend:
|
116
|
-
#{STATES.select { |k, v| k != 'default' }.map { |k, v| "#{k} #{v}" }.join(', ') }, priority #{PRIORITY_FLAG}
|
117
|
-
|
118
|
-
Todo file: #{TODO_FILE}
|
119
|
-
USAGE
|
120
|
-
end
|
30
|
+
class Todo
|
31
|
+
|
32
|
+
COLOR_CODES = {
|
33
|
+
black: 30,
|
34
|
+
red: 31,
|
35
|
+
green: 32,
|
36
|
+
yellow: 33,
|
37
|
+
blue: 34,
|
38
|
+
magenta: 35,
|
39
|
+
cyan: 36,
|
40
|
+
white: 37
|
41
|
+
}
|
42
|
+
|
43
|
+
STATES = {
|
44
|
+
'new' => '[ ]',
|
45
|
+
'done' => '[x]',
|
46
|
+
'started' => '[>]',
|
47
|
+
'blocked' => '[!]',
|
48
|
+
'default' => '[?]'
|
49
|
+
}
|
50
|
+
|
51
|
+
ORDER = {
|
52
|
+
'new' => 3,
|
53
|
+
'done' => 4,
|
54
|
+
'started' => 2,
|
55
|
+
'blocked' => 1,
|
56
|
+
'default' => 100
|
57
|
+
}
|
58
|
+
|
59
|
+
COLORS = {
|
60
|
+
'new' => :white,
|
61
|
+
'done' => :blue,
|
62
|
+
'started' => :green,
|
63
|
+
'blocked' => :yellow,
|
64
|
+
'default' => :magenta
|
65
|
+
}
|
121
66
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
67
|
+
DATE_FORMAT = '%Y-%m-%d'
|
68
|
+
DUE_DATE_DAYS_SIMPLE = ['today', 'tomorrow']
|
69
|
+
DUE_DATE_TAG_PATTERN = /(^| )due:([a-zA-Z0-9-]+)/
|
70
|
+
CONTEXT_TAG_PATTERN = /(^| )[@+][\w-]+/
|
71
|
+
PRIORITY_FLAG = '*'
|
72
|
+
TODO_FILE = "#{ENV['HOME']}/todo.jsonl"
|
73
|
+
|
74
|
+
def execute(arguments)
|
75
|
+
begin
|
76
|
+
setup
|
77
|
+
action = arguments.first
|
78
|
+
args = arguments[1..-1] || []
|
79
|
+
case action
|
80
|
+
when 'add'
|
81
|
+
raise action + ' command requires at least one parameter' if args.nil? || args.empty?
|
82
|
+
add(args.join(' '))
|
83
|
+
when 'start'
|
84
|
+
args.length > 0 ? change_state(args.first.to_i, 'started', (args[1..-1] || []).join(' ')) : list(nil, [':started'])
|
85
|
+
when 'done'
|
86
|
+
args.length > 0 ? change_state(args.first.to_i, 'done', (args[1..-1] || []).join(' ')) : list(nil, [':done'])
|
87
|
+
when 'block'
|
88
|
+
args.length > 0 ? change_state(args.first.to_i, 'blocked', (args[1..-1] || []).join(' ')) : list(nil, [':blocked'])
|
89
|
+
when 'reset'
|
90
|
+
args.length > 0 ? change_state(args.first.to_i, 'new', (args[1..-1] || []).join(' ')) : list(nil, [':new'])
|
91
|
+
when 'prio'
|
92
|
+
raise action + ' command requires at least one parameter' if args.length < 1
|
93
|
+
set_priority(args.first.to_i, (args[1..-1] || []).join(' '))
|
94
|
+
when 'due'
|
95
|
+
raise action + ' command requires at least one parameter' if args.length < 1
|
96
|
+
due_date(args.first.to_i, (args[1..-1] || []).join(' '))
|
97
|
+
when 'append'
|
98
|
+
raise action + ' command requires at least two parameters' if args.length < 2
|
99
|
+
append(args.first.to_i, args[1..-1].join(' '))
|
100
|
+
when 'rename'
|
101
|
+
raise action + ' command requires at least two parameters' if args.length < 2
|
102
|
+
rename(args.first.to_i, args[1..-1].join(' '))
|
103
|
+
when 'del'
|
104
|
+
raise action + ' command requires exactly one parameter' if args.length != 1
|
105
|
+
delete(args.first.to_i)
|
106
|
+
when 'note'
|
107
|
+
raise action + ' command requires at least two parameters' if args.length < 2
|
108
|
+
add_note(args.first.to_i, args[1..-1].join(' '))
|
109
|
+
when 'delnote'
|
110
|
+
raise action + ' command requires exactly one parameter' if args.length != 1
|
111
|
+
delete_note(args.first.to_i)
|
112
|
+
when 'list'
|
113
|
+
list(nil, args)
|
114
|
+
when 'show'
|
115
|
+
raise action + ' command requires exactly one parameter' if args.length != 1
|
116
|
+
show(args.first.to_i)
|
117
|
+
when 'help'
|
118
|
+
puts usage
|
119
|
+
when 'repl'
|
120
|
+
start_repl
|
121
|
+
else
|
122
|
+
list(nil, arguments)
|
131
123
|
end
|
124
|
+
rescue => error
|
125
|
+
puts "#{colorize('ERROR:', :red)} #{error}"
|
132
126
|
end
|
133
127
|
end
|
134
|
-
|
135
|
-
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
def usage
|
132
|
+
<<~USAGE
|
133
|
+
Usage: todo <command> <arguments>
|
134
|
+
|
135
|
+
Commands:
|
136
|
+
* add <text> add new task
|
137
|
+
* start <tasknumber> [text] mark task as started, with optional note
|
138
|
+
* done <tasknumber> [text] mark task as completed, with optional note
|
139
|
+
* block <tasknumber> [text] mark task as blocked, with optional note
|
140
|
+
* reset <tasknumber> [text] reset task to new state, with optional note
|
141
|
+
* prio <tasknumber> [text] toggle high priority flag, with optional note
|
142
|
+
* due <tasknumber> [date] set/unset due date (in YYYY-MM-DD format)
|
143
|
+
|
144
|
+
* append <tasknumber> <text> append text to task title
|
145
|
+
* rename <tasknumber> <text> rename task
|
146
|
+
* del <tasknumber> delete task
|
147
|
+
* note <tasknumber> <text> add note to task
|
148
|
+
* delnote <tasknumber> delete all notes from task
|
149
|
+
|
150
|
+
* list <regex> [regex...] list tasks (only active tasks by default)
|
151
|
+
* show <tasknumber> show all task details
|
152
|
+
* repl enter read-eval-print loop mode
|
153
|
+
* help this help screen
|
154
|
+
|
155
|
+
With list command the following pre-defined regex patterns can be also used:
|
156
|
+
#{@queries.keys.join(', ')}
|
157
|
+
|
158
|
+
Due dates can be also added via tags in task title: "due:YYYY-MM-DD"
|
159
|
+
|
160
|
+
Legend:
|
161
|
+
#{STATES.select { |k, v| k != 'default' }.map { |k, v| "#{k} #{v}" }.join(', ') }, priority #{PRIORITY_FLAG}
|
162
|
+
|
163
|
+
Todo file: #{TODO_FILE}
|
164
|
+
USAGE
|
165
|
+
end
|
166
|
+
|
167
|
+
def setup
|
168
|
+
@today = Time.now
|
169
|
+
next_7_days = (0..6).map do |day| (@today.to_date + day) end
|
170
|
+
@due_date_days = next_7_days.map do |day| day.strftime('%A').downcase end
|
171
|
+
due_dates_for_queries = next_7_days.map do |day| day.strftime(DATE_FORMAT) end
|
172
|
+
|
173
|
+
@queries = {
|
174
|
+
':active' => 'state=(new|started|blocked)',
|
175
|
+
':done' => 'state=done',
|
176
|
+
':blocked' => 'state=blocked',
|
177
|
+
':started' => 'state=started',
|
178
|
+
':new' => 'state=new',
|
179
|
+
':all' => 'state=\w+',
|
180
|
+
':today' => "due=#{due_dates_for_queries[0]}",
|
181
|
+
':tomorrow' => "due=#{due_dates_for_queries[1]}",
|
182
|
+
':next7days' => "due=(#{due_dates_for_queries.join('|')})"
|
183
|
+
}
|
136
184
|
end
|
137
|
-
tasks
|
138
|
-
end
|
139
185
|
|
140
|
-
def
|
141
|
-
|
142
|
-
tasks
|
143
|
-
|
186
|
+
def load_tasks(item_to_check = nil)
|
187
|
+
count = 0
|
188
|
+
tasks = {}
|
189
|
+
if File.exist?(TODO_FILE)
|
190
|
+
File.open(TODO_FILE, 'r:UTF-8') do |file|
|
191
|
+
file.each_line do |line|
|
192
|
+
next if line.strip == ''
|
193
|
+
count += 1
|
194
|
+
tasks[count] = JSON.parse(line.chomp, :symbolize_names => true)
|
195
|
+
end
|
196
|
+
end
|
144
197
|
end
|
198
|
+
if item_to_check && !tasks.has_key?(item_to_check)
|
199
|
+
raise "#{item_to_check}: No such todo"
|
200
|
+
end
|
201
|
+
tasks
|
145
202
|
end
|
146
|
-
end
|
147
203
|
|
148
|
-
def
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
File.open(TODO_FILE, 'a:UTF-8') do |file|
|
155
|
-
file.write(JSON.generate(task) + "\n")
|
204
|
+
def write_tasks(tasks)
|
205
|
+
File.open(TODO_FILE, 'w:UTF-8') do |file|
|
206
|
+
tasks.keys.sort.each do |key|
|
207
|
+
file.write(JSON.generate(tasks[key]) + "\n")
|
208
|
+
end
|
209
|
+
end
|
156
210
|
end
|
157
|
-
list
|
158
|
-
end
|
159
211
|
|
160
|
-
def
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
212
|
+
def postprocess_tags(task)
|
213
|
+
match_data = task[:title].match(DUE_DATE_TAG_PATTERN)
|
214
|
+
if match_data
|
215
|
+
task[:title] = task[:title].gsub(DUE_DATE_TAG_PATTERN, '')
|
216
|
+
task[:due] = convert_due_date(match_data[2])
|
217
|
+
end
|
218
|
+
raise 'title must not be empty' if task[:title].empty?
|
219
|
+
end
|
167
220
|
|
168
|
-
def
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
221
|
+
def add(text)
|
222
|
+
task = {
|
223
|
+
state: 'new',
|
224
|
+
title: text,
|
225
|
+
modified: @today.strftime(DATE_FORMAT)
|
226
|
+
}
|
227
|
+
postprocess_tags(task)
|
228
|
+
File.open(TODO_FILE, 'a:UTF-8') do |file|
|
229
|
+
file.write(JSON.generate(task) + "\n")
|
230
|
+
end
|
231
|
+
list
|
232
|
+
end
|
175
233
|
|
176
|
-
def
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
234
|
+
def append(item, text = '')
|
235
|
+
tasks = load_tasks(item)
|
236
|
+
tasks[item][:title] = [tasks[item][:title], text].join(' ')
|
237
|
+
tasks[item][:modified] = @today.strftime(DATE_FORMAT)
|
238
|
+
postprocess_tags(tasks[item])
|
239
|
+
write_tasks(tasks)
|
240
|
+
list(tasks)
|
241
|
+
end
|
182
242
|
|
183
|
-
def
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
243
|
+
def rename(item, text)
|
244
|
+
tasks = load_tasks(item)
|
245
|
+
tasks[item][:title] = text
|
246
|
+
tasks[item][:modified] = @today.strftime(DATE_FORMAT)
|
247
|
+
postprocess_tags(tasks[item])
|
248
|
+
write_tasks(tasks)
|
249
|
+
list(tasks)
|
250
|
+
end
|
190
251
|
|
191
|
-
def
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
end
|
252
|
+
def delete(item)
|
253
|
+
tasks = load_tasks(item)
|
254
|
+
tasks.delete(item)
|
255
|
+
write_tasks(tasks)
|
256
|
+
list
|
257
|
+
end
|
198
258
|
|
199
|
-
def
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
259
|
+
def change_state(item, state, note = nil)
|
260
|
+
tasks = load_tasks(item)
|
261
|
+
tasks[item][:state] = state
|
262
|
+
tasks[item][:modified] = @today.strftime(DATE_FORMAT)
|
263
|
+
if !note.nil? && !note.empty?
|
264
|
+
tasks[item][:note] ||= []
|
265
|
+
tasks[item][:note].push(note)
|
266
|
+
end
|
267
|
+
write_tasks(tasks)
|
268
|
+
list(tasks)
|
206
269
|
end
|
207
|
-
tasks[item][:modified] = Time.now.strftime(DATE_FORMAT)
|
208
|
-
write_tasks(tasks)
|
209
|
-
list(tasks)
|
210
|
-
end
|
211
270
|
|
212
|
-
def
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
patterns.each do |pattern|
|
221
|
-
match = false unless /#{QUERIES[pattern] || pattern}/ix.match(normalized_task)
|
271
|
+
def set_priority(item, note = nil)
|
272
|
+
tasks = load_tasks(item)
|
273
|
+
tasks[item][:priority] = !tasks[item][:priority]
|
274
|
+
tasks[item].delete(:priority) if !tasks[item][:priority]
|
275
|
+
tasks[item][:modified] = @today.strftime(DATE_FORMAT)
|
276
|
+
if !note.nil? && !note.empty?
|
277
|
+
tasks[item][:note] ||= []
|
278
|
+
tasks[item][:note].push(note)
|
222
279
|
end
|
223
|
-
|
280
|
+
write_tasks(tasks)
|
281
|
+
list(tasks)
|
224
282
|
end
|
225
|
-
|
226
|
-
|
283
|
+
|
284
|
+
def due_date(item, date = '', task = nil)
|
285
|
+
tasks = load_tasks(item)
|
286
|
+
tasks[item][:due] = convert_due_date(date)
|
287
|
+
tasks[item].delete(:due) if tasks[item][:due].nil?
|
288
|
+
tasks[item][:modified] = @today.strftime(DATE_FORMAT)
|
289
|
+
write_tasks(tasks)
|
290
|
+
list(tasks)
|
227
291
|
end
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
elsif date_diff == 0 || date_diff == 1
|
240
|
-
due_date = colorize("(#{DUE_DATE_DAYS_SIMPLE[date_diff]})", :yellow)
|
241
|
-
else
|
242
|
-
due_date = colorize("(#{DUE_DATE_DAYS[date_diff] || task[:due]})", :magenta) if date_diff > 1
|
292
|
+
|
293
|
+
def list(tasks = nil, patterns = nil)
|
294
|
+
items = {}
|
295
|
+
tasks = tasks || load_tasks
|
296
|
+
task_indent = [tasks.keys.max.to_s.size, 4].max
|
297
|
+
patterns = patterns.nil? || patterns.empty? ? [@queries[':active']] : patterns
|
298
|
+
tasks.each do |num, task|
|
299
|
+
normalized_task = "state=#{task[:state]} due=#{task[:due]} #{task[:title]}"
|
300
|
+
match = true
|
301
|
+
patterns.each do |pattern|
|
302
|
+
match = false unless /#{@queries[pattern] || pattern}/ix.match(normalized_task)
|
243
303
|
end
|
244
|
-
|
304
|
+
items[num] = task if match
|
305
|
+
end
|
306
|
+
items = items.sort_by do |num, task|
|
307
|
+
[task[:priority] && task[:state] != 'done' ? 0 : 1, ORDER[task[:state] || 'default'], task[:due] || 'n/a', num]
|
245
308
|
end
|
246
|
-
|
309
|
+
items.each do |num, task|
|
310
|
+
state = task[:state] || 'default'
|
311
|
+
color = COLORS[state]
|
312
|
+
display_state = colorize(STATES[state], color)
|
313
|
+
title = task[:title].gsub(CONTEXT_TAG_PATTERN) do |tag|
|
314
|
+
(tag.start_with?(' ') ? ' ' : '') + colorize(tag.strip, :cyan)
|
315
|
+
end
|
316
|
+
priority_flag = task[:priority] ? colorize(PRIORITY_FLAG, :red) : ' '
|
317
|
+
due_date = ''
|
318
|
+
if task[:due] && state != 'done'
|
319
|
+
date_diff = (Date.parse(task[:due]) - @today.to_date).to_i
|
320
|
+
if date_diff < 0
|
321
|
+
due_date = colorize("(#{date_diff.abs}d overdue)", :red)
|
322
|
+
elsif date_diff == 0 || date_diff == 1
|
323
|
+
due_date = colorize("(#{DUE_DATE_DAYS_SIMPLE[date_diff]})", :yellow)
|
324
|
+
else
|
325
|
+
due_date = colorize("(#{@due_date_days[date_diff] || task[:due]})", :magenta) if date_diff > 1
|
326
|
+
end
|
327
|
+
due_date = ' ' + due_date
|
328
|
+
end
|
329
|
+
puts "#{num.to_s.rjust(task_indent, ' ')}:#{priority_flag}#{display_state} #{title}#{due_date}"
|
330
|
+
end
|
331
|
+
puts 'No todos found' if items.empty?
|
247
332
|
end
|
248
|
-
puts 'No todos found' if items.empty?
|
249
|
-
end
|
250
333
|
|
251
|
-
def add_note(item, text)
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
end
|
334
|
+
def add_note(item, text)
|
335
|
+
tasks = load_tasks(item)
|
336
|
+
tasks[item][:note] ||= []
|
337
|
+
tasks[item][:note].push(text)
|
338
|
+
tasks[item][:modified] = @today.strftime(DATE_FORMAT)
|
339
|
+
write_tasks(tasks)
|
340
|
+
show(item)
|
341
|
+
end
|
259
342
|
|
260
|
-
def delete_note(item)
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
end
|
343
|
+
def delete_note(item)
|
344
|
+
tasks = load_tasks(item)
|
345
|
+
tasks[item].delete(:note)
|
346
|
+
tasks[item][:modified] = @today.strftime(DATE_FORMAT)
|
347
|
+
write_tasks(tasks)
|
348
|
+
show(item)
|
349
|
+
end
|
267
350
|
|
268
|
-
def show(item)
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
351
|
+
def show(item)
|
352
|
+
tasks = load_tasks(item)
|
353
|
+
tasks[item].each do |key, value|
|
354
|
+
val = value.kind_of?(Array) ? "\n" + value.join("\n") : value
|
355
|
+
puts "#{colorize(key.to_s.rjust(10, ' ') + ':', :cyan)} #{val}"
|
356
|
+
end
|
273
357
|
end
|
274
|
-
end
|
275
358
|
|
276
|
-
def start_repl
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
359
|
+
def start_repl
|
360
|
+
command = ''
|
361
|
+
while !['exit', 'quit'].include?(command)
|
362
|
+
if ['clear', 'cls'].include?(command)
|
363
|
+
print "\e[H\e[2J"
|
364
|
+
else
|
365
|
+
execute(command == 'repl' ? [] : command.split(/\s+/))
|
366
|
+
end
|
367
|
+
print "\ntodo> "
|
368
|
+
command = STDIN.gets.chomp
|
283
369
|
end
|
284
|
-
print "\ntodo> "
|
285
|
-
command = STDIN.gets.chomp
|
286
370
|
end
|
287
|
-
end
|
288
371
|
|
289
|
-
def colorize(text, color)
|
290
|
-
|
291
|
-
end
|
372
|
+
def colorize(text, color)
|
373
|
+
"\e[#{COLOR_CODES[color]}m#{text}\e[0m"
|
374
|
+
end
|
292
375
|
|
293
|
-
def
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
add(args.join(' '))
|
301
|
-
when 'start'
|
302
|
-
raise action + ' command can receive only one task number' if args.length > 1
|
303
|
-
args.length == 1 ? change_state(args.first.to_i, 'started') : list(nil, [':started'])
|
304
|
-
when 'done'
|
305
|
-
raise action + ' command can receive only one task number' if args.length > 1
|
306
|
-
args.length == 1 ? change_state(args.first.to_i, 'done') : list(nil, [':done'])
|
307
|
-
when 'block'
|
308
|
-
raise action + ' command can receive only one task number' if args.length > 1
|
309
|
-
args.length == 1 ? change_state(args.first.to_i, 'blocked') : list(nil, [':blocked'])
|
310
|
-
when 'reset'
|
311
|
-
raise action + ' command can receive only one task number' if args.length > 1
|
312
|
-
args.length == 1 ? change_state(args.first.to_i, 'new') : list(nil, [':new'])
|
313
|
-
when 'prio'
|
314
|
-
raise action + ' command requires exactly one parameter' if args.length != 1
|
315
|
-
set_priority(args.first.to_i)
|
316
|
-
when 'due'
|
317
|
-
raise action + ' command requires at least one parameter' if args.length < 1
|
318
|
-
due_date(args.first.to_i, (args[1..-1] || []).join(' '))
|
319
|
-
when 'append'
|
320
|
-
raise action + ' command requires at least two parameters' if args.length < 2
|
321
|
-
append(args.first.to_i, args[1..-1].join(' '))
|
322
|
-
when 'rename'
|
323
|
-
raise action + ' command requires at least two parameters' if args.length < 2
|
324
|
-
rename(args.first.to_i, args[1..-1].join(' '))
|
325
|
-
when 'del'
|
326
|
-
raise action + ' command requires exactly one parameter' if args.length != 1
|
327
|
-
delete(args.first.to_i)
|
328
|
-
when 'note'
|
329
|
-
raise action + ' command requires at least two parameters' if args.length < 2
|
330
|
-
add_note(args.first.to_i, args[1..-1].join(' '))
|
331
|
-
when 'delnote'
|
332
|
-
raise action + ' command requires exactly one parameter' if args.length != 1
|
333
|
-
delete_note(args.first.to_i)
|
334
|
-
when 'list'
|
335
|
-
list(nil, args)
|
336
|
-
when 'show'
|
337
|
-
raise action + ' command requires exactly one parameter' if args.length != 1
|
338
|
-
show(args.first.to_i)
|
339
|
-
when 'help'
|
340
|
-
puts usage
|
341
|
-
when 'repl'
|
342
|
-
start_repl
|
376
|
+
def convert_due_date(date = '')
|
377
|
+
due = nil
|
378
|
+
day_index = @due_date_days.index(date.to_s.downcase) ||
|
379
|
+
DUE_DATE_DAYS_SIMPLE.index(date.to_s.downcase) ||
|
380
|
+
@due_date_days.map do |day| day[0..2] end.index(date.to_s.downcase)
|
381
|
+
if day_index
|
382
|
+
due = (@today.to_date + day_index).strftime(DATE_FORMAT)
|
343
383
|
else
|
344
|
-
|
384
|
+
due = date.nil? || date.empty? ? nil : Date.parse(date).strftime(DATE_FORMAT)
|
345
385
|
end
|
346
|
-
|
347
|
-
puts "#{colorize('ERROR:', :red)} #{error}"
|
386
|
+
return due
|
348
387
|
end
|
388
|
+
|
349
389
|
end
|
350
390
|
|
351
|
-
|
391
|
+
todo = Todo.new
|
392
|
+
todo.execute(ARGV)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: todo-jsonl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.17
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gabor Bata
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-03-01 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|