doing 1.0.52 → 1.0.57
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +35 -13
- data/bin/doing +751 -577
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +221 -86
- 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: 749d71c6201256bf524d3ba0f543e11b7bcc421ee97bf951bd003242edba3847
|
4
|
+
data.tar.gz: 2ec5d4cade4b81614bf336fe6ddf63b35b9bbe0064c5c910b9f275e7f8e0945f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2b7d5d7f24ff24701ce94d61311cd4b37ae547258a26193e28d16847d49743b5334e1e4c79145be43819fee1ee67f30d75979d7d26447bbd192a8211c2f63d80
|
7
|
+
data.tar.gz: 2b0f4326f8b53a6c0da004a323881bbfff3dad10701a19fd7dc10e5f852e22a541644ac13d6a7c860ab0bac5118520df4366272dd7d93376c1adecbe96e2a152
|
data/README.md
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
|
5
5
|
_If you're one of the rare people like me who find this useful, feel free to [buy me some coffee](http://brettterpstra.com/donate/)._
|
6
6
|
|
7
|
-
|
8
7
|
## Contents
|
9
8
|
|
10
9
|
- [What and why](#what-and-why)
|
@@ -16,7 +15,7 @@ _If you're one of the rare people like me who find this useful, feel free to [bu
|
|
16
15
|
- [Troubleshooting](#troubleshooting)
|
17
16
|
- [Changelog](#changelog)
|
18
17
|
|
19
|
-
<!--
|
18
|
+
<!--README-->
|
20
19
|
|
21
20
|
## What and why
|
22
21
|
|
@@ -30,11 +29,9 @@ _Side note:_ I actually use the library behind this utility as part of another s
|
|
30
29
|
|
31
30
|
## Installation
|
32
31
|
|
33
|
-
|
34
|
-
|
35
|
-
To install the _latest_ version, use `--pre`:
|
32
|
+
The current version of `doing` is <!--VER-->1.0.56<!--END VER-->.
|
36
33
|
|
37
|
-
$ [sudo] gem install
|
34
|
+
$ [sudo] gem install doing
|
38
35
|
|
39
36
|
Only use `sudo` if your environment requires it. If you're using the system Ruby on a Mac, for example, it will likely be necessary. If `gem install doing` fails, then run `sudo gem install doing` and provide your administrator password.
|
40
37
|
|
@@ -67,8 +64,10 @@ A basic configuration looks like this:
|
|
67
64
|
default_date_format: '%Y-%m-%d %H:%M'
|
68
65
|
marker_tag: flagged
|
69
66
|
marker_color: yellow
|
67
|
+
tags_color: boldcyan
|
70
68
|
default_tags: []
|
71
|
-
editor_app:
|
69
|
+
editor_app: TaskPaper
|
70
|
+
config_editor_app: Sublime Text
|
72
71
|
:include_notes: true
|
73
72
|
views:
|
74
73
|
color:
|
@@ -137,7 +136,9 @@ You can rename the section that holds your current tasks. By default, this is `C
|
|
137
136
|
|
138
137
|
### Default editors
|
139
138
|
|
140
|
-
The setting `editor_app` only applies to Mac OS X users. It's the default application that the command `doing open` will open your WWID file in. If this is blank, it will be opened by whatever the system default is, or you can use `-a app_name` or `-b bundle_id` to override.
|
139
|
+
The setting `editor_app` only applies to Mac OS X users. It's the default application that the command `doing open` will open your WWID file in. Personally, I use `editor_app: TaskPaper`. If this is blank, it will be opened by whatever the system default is for the doing file extension (default is `.md`), or you can use `-a app_name` or `-b bundle_id` to override.
|
140
|
+
|
141
|
+
You can define a separate app to open the config file in when running `doing config`. The key for this is `config_editor_app`.
|
141
142
|
|
142
143
|
In the case of the `doing now -e` command, your `$EDITOR` environment variable will be used to complete the entry text and notes. Set it in your `~/.bash_profile` or whatever is appropriate for your system:
|
143
144
|
|
@@ -413,6 +414,8 @@ When used with `doing done`, `--back` and `--took` allow time intervals to be ac
|
|
413
414
|
|
414
415
|
All of these commands accept a `-e` argument. This opens your command line editor (as defined in the environment variable `$EDITOR`). Add your entry, save the temp file, and close it. The new entry is added. Anything after the first line is included as a note on the entry.
|
415
416
|
|
417
|
+
`doing again` (or `doing resume`) will duplicate the last @done entry (most recently completed) with a new start date (and without the @done tag). To resume the last entry matching specific tags, use `--tag=TAG`. You can specify multiple tags by separating with a comma. Multiple tags are combined with 'AND' by default (all tags must exist on the entry to match), but you can use `--bool=` to set it to 'OR' or 'NOT'. By default the new entry will be added to the same section as the matching entry, but you can specify a section with `--in=SECTION`.
|
418
|
+
|
416
419
|
`doing meanwhile` is a special command for creating and finishing tasks that may have other entries come before they're complete. When you create an entry with `doing meanwhile [entry text]`, it will automatically complete the last _@meanwhile_ item (dated _@done_ tag) and add the _@meanwhile_ tag to the new item. This allows time tracking on a more general basis, and still lets you keep track of the smaller things you do while working on an overarching project. The `meanwhile` command accepts `--back [time]` and will backdate the _@done_ tag and start date of the new task at the same time. Running `meanwhile` with no arguments will simply complete the last _@meanwhile_ task.
|
417
420
|
|
418
421
|
See `doing help meanwhile` for more options.
|
@@ -438,9 +441,9 @@ You can finish the last entry containing a specific tag or combination of tags u
|
|
438
441
|
|
439
442
|
doing finish --tag=work,project1
|
440
443
|
|
441
|
-
You can change the boolean using `--
|
444
|
+
You can change the boolean using `--bool=OR` (last entry containing any of the specified tags) or `--bool=NOT` (last entry containing none of the tags).
|
442
445
|
|
443
|
-
You can also include a `--no-date` switch to add `@done` without a finish date, meaning no time is tracked for the task. `doing cancel` is an alias for this. Like `finish`, `cancel` accepts a count to act on the last X entries, as well as `--archive` and `--section` options.
|
446
|
+
You can also include a `--no-date` switch to add `@done` without a finish date, meaning no time is tracked for the task. `doing cancel` is an alias for this. Like `finish`, `cancel` accepts a count to act on the last X entries, as well as `--archive` and `--section` options. `cancel` also accepts the `--tag` and `--bool` flags for tag filtering.
|
444
447
|
|
445
448
|
|
446
449
|
##### Tagging and Autotagging
|
@@ -492,6 +495,8 @@ This creates a search pattern looking for a string of word characters followed b
|
|
492
495
|
|
493
496
|
You can also add notes at the time of entry by using the `-n` or `--note` flag with `doing now`, `doing later`, or `doing done`. If you pass text to any of the creation commands which has multiple lines, everything after the first line break will become the note.
|
494
497
|
|
498
|
+
If a string passed to `now`, `later`, or `done` has a parenthetical at the end, the parenthetical will be removed from the title and its contents added as a note. So `doing now Working on @project1 (Adding some unit tests)` would create an entry titled "Working on @project1" with a note "Adding some unit tests." This is the equivalent of `doing now Working on @project1 -n "Adding some unit tests"`.
|
499
|
+
|
495
500
|
#### Displaying entries:
|
496
501
|
|
497
502
|
show - List all entries
|
@@ -517,6 +522,18 @@ If you have a use for it, you can use `-o csv` on the show or view commands to o
|
|
517
522
|
|
518
523
|
You can also show entries matching a search string with `doing grep` (synonym `doing search`). If you want to search with regular expressions or for an exact match, surround your search query with forward slashes, e.g. `doing search /project name/`. If you pass a search string without slashes, it's treated as a fuzzy search string, meaning matches can be found as long as the characters in the search string are in order and with no more than three other characters between each. By default searches are across all sections, but you can limit it to one with the `-s SECTION_NAME` flag. Searches can be displayed with the default template, or output as HTML, CSV, or JSON.
|
519
524
|
|
525
|
+
##### Modifying the last entry
|
526
|
+
|
527
|
+
If you want to make a change to the last entry added, use `doing last -e`. The `-e` flag opens the last entry (including note) in your editor, and when you close your editor, your doing file will be updated with any changes you made to the entry.
|
528
|
+
|
529
|
+
You can choose the last entry in a specific section by including the `-s` flag, so `doing last -s Later -e` would edit the most recent entry in the Later section.
|
530
|
+
|
531
|
+
You can also use text search or a tag filter to get an entry earlier than the most recent one. A tag search with `doing last --tag=project1 -e` will edit the last entry tagged `@project1`. Multiple tags can be combined with commas, and you can use `--bool` to specify whether the search is `AND` (matches all tags given), `OR` (matches any tag given), or `NOT` (matches none of the tags).
|
532
|
+
|
533
|
+
You can edit the last entry that matches a search string with `--search=QUERY`. `QUERY` can either be a raw string, or you can surround it with slashes to search by regex (`doing last --search="/project./" -e`). If the string is raw text, fuzzy matching will be used, so the characters must be in order but can be separated by up to three other characters.
|
534
|
+
|
535
|
+
Both `--tag` and `--search` can be constrained to a single section with `-s SECTION`.
|
536
|
+
|
520
537
|
#### Views
|
521
538
|
|
522
539
|
view - Display a user-created view
|
@@ -576,11 +593,14 @@ __Fish:__ See the file [`doing.fish`](https://github.com/ttscoff/doing/blob/mast
|
|
576
593
|
|
577
594
|
The LaunchBar action requires that `doing` be available in `/usr/local/bin/doing`. If it's not (because you're using RVM or similar), you'll need to symlink it there. Running the action with Return will show the latest 9 items from Currently, along with any time intervals recorded, and includes a submenu of Timers for each tag.
|
578
595
|
|
579
|
-
Pressing Spacebar and typing allows you to add a new entry to currently. You an also trigger a custom show command by typing "show [section/tag]" and hitting return.
|
596
|
+
Pressing Spacebar and typing allows you to add a new entry to currently. You an also trigger a custom show command by typing "show [section/tag]" and hitting return. Include any command line flags at the end of the string, and if you add text in parenthesis, it will be processed as a note on the entry.
|
580
597
|
|
581
598
|
Point of interest, the LaunchBar Action makes use of the `-o json` flag for outputting JSON to the action's script for parsing.
|
582
599
|
|
583
|
-
See <https://brettterpstra.com/projects/doing/> for the download
|
600
|
+
<!--GITHUB-->See <https://brettterpstra.com/projects/doing/> for the download.<!--END GITHUB-->
|
601
|
+
<!--JEKYLL
|
602
|
+
{% download 117 %}
|
603
|
+
-->
|
584
604
|
|
585
605
|
Evan Lovely has [created an Alfred workflow as well](http://www.evanlovely.com/blog/technology/alfred-for-terpstras-doing/).
|
586
606
|
|
@@ -621,7 +641,9 @@ Please try not to email me directly about GitHub projects.
|
|
621
641
|
|
622
642
|
Feel free to [poke around](http://github.com/ttscoff/doing/), I'll try to add more comments in the future (and retroactively).
|
623
643
|
|
624
|
-
|
644
|
+
<!--END README-->
|
645
|
+
|
646
|
+
PayPal link: [paypal.me/ttscoff](https://paypal.me/ttscoff)
|
625
647
|
|
626
648
|
## Changelog
|
627
649
|
|
data/bin/doing
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
$LOAD_PATH.unshift File.join(__dir__, '..', 'lib')
|
3
5
|
require 'gli'
|
4
6
|
require 'doing'
|
@@ -7,9 +9,9 @@ require 'pp'
|
|
7
9
|
|
8
10
|
def class_exists?(class_name)
|
9
11
|
klass = Module.const_get(class_name)
|
10
|
-
|
12
|
+
klass.is_a?(Class)
|
11
13
|
rescue NameError
|
12
|
-
|
14
|
+
false
|
13
15
|
end
|
14
16
|
|
15
17
|
if class_exists? 'Encoding'
|
@@ -17,358 +19,352 @@ if class_exists? 'Encoding'
|
|
17
19
|
Encoding.default_internal = Encoding::UTF_8 if Encoding.respond_to?('default_internal')
|
18
20
|
end
|
19
21
|
|
20
|
-
|
21
22
|
include GLI::App
|
22
23
|
version Doing::VERSION
|
23
24
|
|
24
25
|
wwid = WWID.new
|
25
|
-
if Dir.respond_to?('home')
|
26
|
-
|
27
|
-
else
|
28
|
-
|
29
|
-
end
|
26
|
+
wwid.user_home = if Dir.respond_to?('home')
|
27
|
+
Dir.home
|
28
|
+
else
|
29
|
+
File.expand_path('~')
|
30
|
+
end
|
30
31
|
wwid.configure
|
31
32
|
|
32
|
-
|
33
33
|
program_desc 'A CLI for a What Was I Doing system'
|
34
34
|
|
35
35
|
default_command :recent
|
36
36
|
# sort_help :manually
|
37
37
|
|
38
38
|
desc 'Output notes if included in the template'
|
39
|
-
default_value true
|
40
|
-
switch [:notes], :default_value => true, :negatable => true
|
39
|
+
switch [:notes], default_value: true, negatable: true
|
41
40
|
|
42
41
|
desc 'Send results report to STDOUT instead of STDERR'
|
43
|
-
default_value false
|
44
|
-
switch [:stdout], :default_value => false, :negatable => false
|
42
|
+
switch [:stdout], default_value: false, negatable: false
|
45
43
|
|
46
44
|
desc 'Exclude auto tags and default tags'
|
47
|
-
switch [
|
45
|
+
switch %i[x noauto], default_value: false
|
48
46
|
|
49
47
|
desc 'Use a specific configuration file'
|
50
|
-
default_value
|
51
|
-
flag [:config_file]
|
52
|
-
|
53
|
-
|
54
|
-
# desc 'Wrap notes at X chars (0 for no wrap)'
|
55
|
-
# flag [:w, :wrapwidth], :must_match => /^\d+$/, :type => Integer
|
48
|
+
flag [:config_file], default_value: wwid.config_file
|
56
49
|
|
57
50
|
desc 'Specify a different doing_file'
|
58
|
-
flag [
|
51
|
+
flag %i[f doing_file]
|
59
52
|
|
60
53
|
desc 'Add an entry'
|
61
|
-
arg_name '
|
54
|
+
arg_name 'ENTRY'
|
62
55
|
command [:now, :next] do |c|
|
63
56
|
c.desc 'Section'
|
64
|
-
c.arg_name '
|
65
|
-
c.flag [
|
57
|
+
c.arg_name 'NAME'
|
58
|
+
c.flag %i[s section], default_value: wwid.current_section
|
66
59
|
|
67
60
|
c.desc "Edit entry with #{ENV['EDITOR']}"
|
68
|
-
c.switch [
|
61
|
+
c.switch %i[e editor], negatable: false, default_value: false
|
69
62
|
|
70
63
|
c.desc 'Backdate start time [4pm|20m|2h|yesterday noon]'
|
71
|
-
c.
|
64
|
+
c.arg_name 'DATE_STRING'
|
65
|
+
c.flag %i[b back]
|
72
66
|
|
73
67
|
c.desc 'Timed entry, marks last entry in section as @done'
|
74
|
-
c.switch [
|
68
|
+
c.switch %i[f finish_last], negatable: false, default_value: false
|
75
69
|
|
76
70
|
c.desc 'Note'
|
77
|
-
c.arg_name '
|
78
|
-
c.flag [
|
71
|
+
c.arg_name 'TEXT'
|
72
|
+
c.flag %i[n note]
|
79
73
|
|
80
74
|
# c.desc "Edit entry with specified app"
|
81
75
|
# c.arg_name 'editor_app'
|
82
76
|
# # c.flag [:a, :app]
|
83
77
|
|
84
|
-
c.action do |
|
78
|
+
c.action do |_global_options, options, args|
|
85
79
|
if options[:back]
|
86
80
|
date = wwid.chronify(options[:back])
|
87
81
|
|
88
|
-
raise
|
82
|
+
raise 'Unable to parse date string' if date.nil?
|
89
83
|
else
|
90
84
|
date = Time.now
|
91
85
|
end
|
92
86
|
|
93
87
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
94
88
|
|
95
|
-
if options[:e] || (args.
|
96
|
-
raise
|
97
|
-
|
98
|
-
input
|
99
|
-
input
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
89
|
+
if options[:e] || (args.empty? && $stdin.stat.size.zero?)
|
90
|
+
raise 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
91
|
+
|
92
|
+
input = ''
|
93
|
+
input += args.join(' ') unless args.empty?
|
94
|
+
input = wwid.fork_editor(input).strip
|
95
|
+
|
96
|
+
raise 'No content' if input.empty?
|
97
|
+
|
98
|
+
title, note = wwid.format_input(input)
|
99
|
+
note.push(options[:n]) if options[:n]
|
100
|
+
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:f] })
|
101
|
+
wwid.write(wwid.doing_file)
|
102
|
+
elsif args.length.positive?
|
103
|
+
title, note = wwid.format_input(args.join(' '))
|
104
|
+
note.push(options[:n]) if options[:n]
|
105
|
+
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:f] })
|
106
|
+
wwid.write(wwid.doing_file)
|
107
|
+
elsif $stdin.stat.size.positive?
|
108
|
+
title, note = wwid.format_input($stdin.read)
|
109
|
+
note.push(options[:n]) if options[:n]
|
110
|
+
wwid.add_item(title.cap_first, section, { note: note, back: date, timed: options[:f] })
|
111
|
+
wwid.write(wwid.doing_file)
|
108
112
|
else
|
109
|
-
|
110
|
-
title, note = wwid.format_input(args.join(" "))
|
111
|
-
note.push(options[:n]) if options[:n]
|
112
|
-
wwid.add_item(title.cap_first, section, {:note => note, :back => date, :timed => options[:f]})
|
113
|
-
wwid.write(wwid.doing_file)
|
114
|
-
elsif STDIN.stat.size > 0
|
115
|
-
title, note = wwid.format_input(STDIN.read)
|
116
|
-
note.push(options[:n]) if options[:n]
|
117
|
-
wwid.add_item(title.cap_first, section, {:note => note, :back => date, :timed => options[:f]})
|
118
|
-
wwid.write(wwid.doing_file)
|
119
|
-
else
|
120
|
-
raise "You must provide content when creating a new entry"
|
121
|
-
end
|
113
|
+
raise 'You must provide content when creating a new entry'
|
122
114
|
end
|
123
115
|
end
|
124
116
|
end
|
125
117
|
|
126
118
|
desc 'Add a note to the last entry'
|
127
|
-
long_desc %
|
119
|
+
long_desc %(
|
128
120
|
If -r is provided with no other arguments, the last note is removed. If new content is specified through arguments or STDIN, any previous note will be replaced with the new one.
|
129
121
|
|
130
122
|
Use -e to load the last entry in a text editor where you can append a note.
|
131
|
-
|
132
|
-
arg_name '
|
123
|
+
)
|
124
|
+
arg_name 'NOTE_TEXT'
|
133
125
|
command :note do |c|
|
134
126
|
c.desc 'Section'
|
135
|
-
c.arg_name '
|
136
|
-
c.flag [
|
127
|
+
c.arg_name 'NAME'
|
128
|
+
c.flag %i[s section], default_value: 'All'
|
137
129
|
|
138
130
|
c.desc "Edit entry with #{ENV['EDITOR']}"
|
139
|
-
c.switch [
|
131
|
+
c.switch %i[e editor], negatable: false, default_value: false
|
140
132
|
|
141
133
|
c.desc "Replace/Remove last entry's note (default append)"
|
142
|
-
c.switch [
|
134
|
+
c.switch %i[r remove], negatable: false, default_value: false
|
143
135
|
|
144
|
-
c.action do |
|
136
|
+
c.action do |_global_options, options, args|
|
145
137
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
146
138
|
|
147
|
-
if options[:e] || (args.
|
148
|
-
raise
|
139
|
+
if options[:e] || (args.empty? && $stdin.stat.size.zero? && !options[:r])
|
140
|
+
raise 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
149
141
|
|
150
|
-
input = args.
|
142
|
+
input = !args.empty? ? args.join(' ') : ''
|
151
143
|
|
152
|
-
prev_input = wwid.last_note(section) ||
|
153
|
-
|
154
|
-
prev_input = prev_input.join("\n")
|
155
|
-
end
|
144
|
+
prev_input = wwid.last_note(section) || ''
|
145
|
+
prev_input = prev_input.join("\n") if prev_input.instance_of?(Array)
|
156
146
|
input = prev_input + input
|
157
147
|
|
158
|
-
input = wwid.fork_editor(input)
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
148
|
+
input = wwid.fork_editor(input).strip
|
149
|
+
raise 'No content, cancelled' unless input
|
150
|
+
|
151
|
+
_title, note = wwid.format_input(input)
|
152
|
+
|
153
|
+
raise 'No note content' unless note
|
154
|
+
|
155
|
+
wwid.note_last(section, note, replace: true)
|
156
|
+
elsif !args.empty?
|
157
|
+
title, note = wwid.format_input(args.join(' '))
|
158
|
+
note.insert(0, title)
|
159
|
+
wwid.note_last(section, note, replace: options[:r])
|
160
|
+
elsif $stdin.stat.size.positive?
|
161
|
+
title, note = wwid.format_input($stdin.read)
|
162
|
+
note.insert(0, title)
|
163
|
+
wwid.note_last(section, note, replace: options[:r])
|
164
|
+
elsif options[:r]
|
165
|
+
wwid.note_last(section, [], replace: true)
|
169
166
|
else
|
170
|
-
|
171
|
-
title, note = wwid.format_input(args.join(" "))
|
172
|
-
note.insert(0, title)
|
173
|
-
wwid.note_last(section, note, replace: options[:r])
|
174
|
-
elsif STDIN.stat.size > 0
|
175
|
-
title, note = wwid.format_input(STDIN.read)
|
176
|
-
note.insert(0, title)
|
177
|
-
wwid.note_last(section, note, replace: options[:r])
|
178
|
-
else
|
179
|
-
if options[:r]
|
180
|
-
wwid.note_last(section, [], replace: true)
|
181
|
-
else
|
182
|
-
raise "You must provide content when adding a note"
|
183
|
-
end
|
184
|
-
end
|
167
|
+
raise 'You must provide content when adding a note'
|
185
168
|
end
|
186
169
|
wwid.write(wwid.doing_file)
|
187
170
|
end
|
188
171
|
end
|
189
172
|
|
190
173
|
desc 'Finish any running @meanwhile tasks and optionally create a new one'
|
191
|
-
arg_name '
|
174
|
+
arg_name 'ENTRY'
|
192
175
|
command :meanwhile do |c|
|
193
176
|
c.desc 'Section'
|
194
|
-
c.arg_name '
|
195
|
-
c.flag [
|
177
|
+
c.arg_name 'NAME'
|
178
|
+
c.flag %i[s section], default_value: wwid.current_section
|
196
179
|
|
197
180
|
c.desc "Edit entry with #{ENV['EDITOR']}"
|
198
|
-
c.switch [
|
181
|
+
c.switch %i[e editor], negatable: false, default_value: false
|
199
182
|
|
200
|
-
c.desc
|
201
|
-
c.switch [
|
183
|
+
c.desc 'Archive previous @meanwhile entry'
|
184
|
+
c.switch %i[a archive], default_value: false
|
202
185
|
|
203
186
|
c.desc 'Backdate start date for new entry to date string [4pm|20m|2h|yesterday noon]'
|
204
|
-
c.
|
187
|
+
c.arg_name 'DATE_STRING'
|
188
|
+
c.flag %i[b back]
|
205
189
|
|
206
190
|
c.desc 'Note'
|
207
|
-
c.arg_name '
|
208
|
-
c.flag [
|
191
|
+
c.arg_name 'TEXT'
|
192
|
+
c.flag %i[n note]
|
209
193
|
|
210
|
-
c.action do |
|
194
|
+
c.action do |_global_options, options, args|
|
211
195
|
if options[:back]
|
212
196
|
date = wwid.chronify(options[:back])
|
213
197
|
|
214
|
-
raise
|
198
|
+
raise 'Unable to parse date string' if date.nil?
|
215
199
|
else
|
216
200
|
date = Time.now
|
217
201
|
end
|
218
202
|
|
219
203
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
220
|
-
input =
|
204
|
+
input = ''
|
221
205
|
|
222
206
|
if options[:e]
|
223
|
-
raise
|
224
|
-
|
225
|
-
input
|
207
|
+
raise 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
208
|
+
|
209
|
+
input += args.join(' ') unless args.empty?
|
210
|
+
input = wwid.fork_editor(input).strip
|
211
|
+
elsif !args.empty?
|
212
|
+
input = args.join(' ')
|
213
|
+
elsif $stdin.stat.size.positive?
|
214
|
+
input = $stdin.read
|
215
|
+
end
|
216
|
+
|
217
|
+
if input && !input.empty?
|
218
|
+
input, note = wwid.format_input(input)
|
226
219
|
else
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
220
|
+
input = nil
|
221
|
+
note = []
|
222
|
+
end
|
223
|
+
|
224
|
+
if options[:n]
|
225
|
+
note.push(options[:n])
|
226
|
+
elsif note.empty?
|
227
|
+
note = nil
|
232
228
|
end
|
233
|
-
input = false unless input && input.length > 0
|
234
229
|
|
235
|
-
|
236
|
-
wwid.stop_start('meanwhile', {:new_item => input, :back => date, :section => section, :archive => options[:a], :note => note})
|
230
|
+
wwid.stop_start('meanwhile', { new_item: input, back: date, section: section, archive: options[:a], note: note })
|
237
231
|
wwid.write(wwid.doing_file)
|
238
232
|
end
|
239
233
|
end
|
240
234
|
|
241
|
-
desc 'Output HTML templates for customization'
|
242
|
-
long_desc %
|
243
|
-
Templates are printed to STDOUT for piping to a file.
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
c.action do |
|
251
|
-
|
252
|
-
|
235
|
+
desc 'Output HTML and CSS templates for customization'
|
236
|
+
long_desc %(
|
237
|
+
Templates are printed to STDOUT for piping to a file.
|
238
|
+
Save them and use them in the configuration file under html_template.
|
239
|
+
|
240
|
+
Example `doing template HAML > ~/styles/my_doing.haml`
|
241
|
+
)
|
242
|
+
arg_name 'TYPE', must_match: /^(html|haml|css)/i
|
243
|
+
command :template do |c|
|
244
|
+
c.action do |_global_options, options, args|
|
245
|
+
raise 'No type specified, use `doing template [HAML|CSS]`' if args.empty?
|
246
|
+
|
247
|
+
case options[:t]
|
248
|
+
when /html|haml/i
|
249
|
+
$stdout.puts wwid.haml_template
|
250
|
+
when /css/i
|
251
|
+
$stdout.puts wwid.css_template
|
253
252
|
else
|
254
|
-
|
255
|
-
$stdout.puts wwid.haml_template
|
256
|
-
elsif options[:t] =~ /css/i
|
257
|
-
$stdout.puts wwid.css_template
|
258
|
-
else
|
259
|
-
raise "Invalid type specified, use --type=[HAML|CSS]"
|
260
|
-
end
|
253
|
+
raise 'Invalid type specified, must be HAML or CSS'
|
261
254
|
end
|
262
255
|
end
|
263
256
|
end
|
264
257
|
|
265
|
-
|
266
258
|
desc 'Add an item to the Later section'
|
267
|
-
arg_name '
|
259
|
+
arg_name 'ENTRY'
|
268
260
|
command :later do |c|
|
269
261
|
c.desc "Edit entry with #{ENV['EDITOR']}"
|
270
|
-
c.switch [
|
262
|
+
c.switch %i[e editor], negatable: false, default_value: false
|
271
263
|
|
272
|
-
c.desc
|
273
|
-
c.arg_name '
|
274
|
-
c.flag [
|
264
|
+
c.desc 'Edit entry with specified app'
|
265
|
+
c.arg_name 'APP'
|
266
|
+
c.flag %i[a app]
|
275
267
|
|
276
268
|
c.desc 'Backdate start time to date string [4pm|20m|2h|yesterday noon]'
|
277
|
-
c.
|
269
|
+
c.arg_name 'DATE_STRING'
|
270
|
+
c.flag %i[b back]
|
278
271
|
|
279
272
|
c.desc 'Note'
|
280
|
-
c.arg_name '
|
281
|
-
c.flag [
|
273
|
+
c.arg_name 'TEXT'
|
274
|
+
c.flag %i[n note]
|
282
275
|
|
283
|
-
c.action do |
|
276
|
+
c.action do |_global_options, options, args|
|
284
277
|
if options[:back]
|
285
278
|
date = wwid.chronify(options[:back])
|
286
|
-
raise
|
279
|
+
raise 'Unable to parse date string' if date.nil?
|
287
280
|
else
|
288
281
|
date = Time.now
|
289
282
|
end
|
290
283
|
|
291
|
-
if options[:e] || (args.
|
292
|
-
raise
|
293
|
-
|
294
|
-
input
|
295
|
-
input = wwid.fork_editor(input)
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
284
|
+
if options[:e] || (args.empty? && $stdin.stat.size.zero?)
|
285
|
+
raise 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
286
|
+
|
287
|
+
input = args.empty? ? '' : args.join(' ')
|
288
|
+
input = wwid.fork_editor(input).strip
|
289
|
+
raise 'No content' unless input && !input.empty?
|
290
|
+
|
291
|
+
title, note = wwid.format_input(input)
|
292
|
+
note.push(options[:n]) if options[:n]
|
293
|
+
wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
|
294
|
+
wwid.write(wwid.doing_file)
|
295
|
+
elsif !args.empty?
|
296
|
+
title, note = wwid.format_input(args.join(' '))
|
297
|
+
note.push(options[:n]) if options[:n]
|
298
|
+
wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
|
299
|
+
wwid.write(wwid.doing_file)
|
300
|
+
elsif $stdin.stat.size.positive?
|
301
|
+
title, note = wwid.format_input($stdin.read)
|
302
|
+
note.push(options[:n]) if options[:n]
|
303
|
+
wwid.add_item(title.cap_first, 'Later', { note: note, back: date })
|
304
|
+
wwid.write(wwid.doing_file)
|
304
305
|
else
|
305
|
-
|
306
|
-
title, note = wwid.format_input(args.join(" "))
|
307
|
-
note.push(options[:n]) if options[:n]
|
308
|
-
wwid.add_item(title.cap_first, "Later", {:note => note, :back => date})
|
309
|
-
wwid.write(wwid.doing_file)
|
310
|
-
elsif STDIN.stat.size > 0
|
311
|
-
title, note = wwid.format_input(STDIN.read)
|
312
|
-
note.push(options[:n]) if options[:n]
|
313
|
-
wwid.add_item(title.cap_first, "Later", {:note => note, :back => date})
|
314
|
-
wwid.write(wwid.doing_file)
|
315
|
-
else
|
316
|
-
raise "You must provide content when creating a new entry"
|
317
|
-
end
|
306
|
+
raise 'You must provide content when creating a new entry'
|
318
307
|
end
|
319
308
|
end
|
320
309
|
end
|
321
310
|
|
322
311
|
desc 'Add a completed item with @done(date). No argument finishes last entry.'
|
323
|
-
arg_name '
|
312
|
+
arg_name 'ENTRY'
|
324
313
|
command [:done, :did] do |c|
|
325
314
|
c.desc 'Remove @done tag'
|
326
|
-
c.switch [
|
315
|
+
c.switch %i[r remove], negatable: false, default_value: false
|
327
316
|
|
328
317
|
c.desc 'Include date'
|
329
|
-
c.switch [:date], :
|
318
|
+
c.switch [:date], negatable: true, default_value: true
|
330
319
|
|
331
320
|
c.desc 'Immediately archive the entry'
|
332
|
-
c.switch [
|
321
|
+
c.switch %i[a archive], negatable: false, default_value: false
|
333
322
|
|
334
|
-
c.desc
|
323
|
+
c.desc %(Set finish date to specific date/time (natural langauge parsed, e.g. --at=1:30pm).
|
324
|
+
If used, ignores --back. Used with --took, backdates start date)
|
325
|
+
c.arg_name 'DATE_STRING'
|
335
326
|
c.flag [:at]
|
336
327
|
|
337
328
|
c.desc 'Backdate start date by interval [4pm|20m|2h|yesterday noon]'
|
338
|
-
c.
|
329
|
+
c.arg_name 'DATE_STRING'
|
330
|
+
c.flag %i[b back]
|
339
331
|
|
340
|
-
c.desc
|
341
|
-
|
332
|
+
c.desc %(Set completion date to start date plus interval (XX[mhd] or HH:MM).
|
333
|
+
If used without the --back option, the start date will be moved back to allow
|
334
|
+
the completion date to be the current time.)
|
335
|
+
c.arg_name 'INTERVAL'
|
336
|
+
c.flag %i[t took]
|
342
337
|
|
343
338
|
c.desc 'Section'
|
344
|
-
c.
|
339
|
+
c.arg_name 'NAME'
|
340
|
+
c.flag %i[s section], default_value: wwid.current_section
|
345
341
|
|
346
342
|
c.desc "Edit entry with #{ENV['EDITOR']}"
|
347
|
-
c.switch [
|
343
|
+
c.switch %i[e editor], negatable: false, default_value: false
|
348
344
|
|
349
345
|
# c.desc "Edit entry with specified app"
|
350
346
|
# c.arg_name 'editor_app'
|
351
347
|
# # c.flag [:a, :app]
|
352
348
|
|
353
|
-
c.action do |
|
349
|
+
c.action do |_global_options, options, args|
|
354
350
|
took = 0
|
355
351
|
|
356
|
-
|
357
352
|
if options[:took]
|
358
353
|
took = wwid.chronify_qty(options[:took])
|
359
|
-
raise
|
354
|
+
raise 'Unable to parse date string for --took' if took.nil?
|
360
355
|
end
|
361
356
|
|
362
357
|
if options[:back]
|
363
358
|
date = wwid.chronify(options[:back])
|
364
|
-
raise
|
359
|
+
raise 'Unable to parse date string for --back' if date.nil?
|
365
360
|
else
|
366
361
|
date = options[:took] ? Time.now - took : Time.now
|
367
362
|
end
|
368
363
|
|
369
364
|
if options[:at]
|
370
365
|
finish_date = wwid.chronify(options[:at])
|
371
|
-
raise
|
366
|
+
raise 'Unable to parse date string for --at' if finish_date.nil?
|
367
|
+
|
372
368
|
date = options[:took] ? finish_date - took : finish_date
|
373
369
|
elsif options[:took]
|
374
370
|
finish_date = date + took
|
@@ -379,113 +375,147 @@ command [:done, :did] do |c|
|
|
379
375
|
end
|
380
376
|
|
381
377
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
382
|
-
donedate = options[:date] ? "(#{finish_date.strftime('%F %R')})" :
|
378
|
+
donedate = options[:date] ? "(#{finish_date.strftime('%F %R')})" : ''
|
383
379
|
|
384
380
|
if options[:e]
|
385
|
-
raise
|
386
|
-
|
387
|
-
input
|
388
|
-
input
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
elsif args.length == 0 && STDIN.stat.size == 0
|
381
|
+
raise 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
382
|
+
|
383
|
+
input = ''
|
384
|
+
input += args.join(' ') unless args.empty?
|
385
|
+
input = wwid.fork_editor(input).strip
|
386
|
+
raise 'No content' unless input && !input.empty?
|
387
|
+
|
388
|
+
title, note = wwid.format_input(input)
|
389
|
+
title += " @done#{donedate}"
|
390
|
+
section = 'Archive' if options[:a]
|
391
|
+
wwid.add_item(title.cap_first, section.cap_first, { note: note, back: date })
|
392
|
+
wwid.write(wwid.doing_file)
|
393
|
+
elsif args.empty? && $stdin.stat.size.zero?
|
399
394
|
if options[:r]
|
400
|
-
wwid.tag_last({:
|
395
|
+
wwid.tag_last({ tags: ['done'], count: 1, section: section, remove: true })
|
401
396
|
else
|
402
|
-
wwid.tag_last({:
|
397
|
+
wwid.tag_last({ tags: ['done'], count: 1, section: section, archive: options[:a], back: finish_date,
|
398
|
+
date: options[:date] })
|
403
399
|
end
|
400
|
+
elsif !args.empty?
|
401
|
+
title, note = wwid.format_input(args.join(' '))
|
402
|
+
title.chomp!
|
403
|
+
title += " @done#{donedate}"
|
404
|
+
section = 'Archive' if options[:a]
|
405
|
+
wwid.add_item(title.cap_first, section.cap_first, { note: note, back: date })
|
406
|
+
wwid.write(wwid.doing_file)
|
407
|
+
elsif $stdin.stat.size.positive?
|
408
|
+
title, note = wwid.format_input($stdin.read)
|
409
|
+
title += " @done#{donedate}"
|
410
|
+
section = options[:a] ? 'Archive' : section
|
411
|
+
wwid.add_item(title.cap_first, section.cap_first, { note: note, back: date })
|
412
|
+
wwid.write(wwid.doing_file)
|
404
413
|
else
|
405
|
-
|
406
|
-
title, note = wwid.format_input(args.join(" "))
|
407
|
-
title.chomp!
|
408
|
-
title += " @done#{donedate}"
|
409
|
-
section = "Archive" if options[:a]
|
410
|
-
wwid.add_item(title.cap_first, section.cap_first, {:note => note, :back => date})
|
411
|
-
wwid.write(wwid.doing_file)
|
412
|
-
elsif STDIN.stat.size > 0
|
413
|
-
title, note = wwid.format_input(STDIN.read)
|
414
|
-
title += " @done#{donedate}"
|
415
|
-
section = options[:a] ? "Archive" : section
|
416
|
-
wwid.add_item(title.cap_first, section.cap_first, {:note => note, :back => date})
|
417
|
-
wwid.write(wwid.doing_file)
|
418
|
-
else
|
419
|
-
raise "You must provide content when creating a new entry"
|
420
|
-
end
|
414
|
+
raise 'You must provide content when creating a new entry'
|
421
415
|
end
|
422
416
|
end
|
423
417
|
end
|
424
418
|
|
425
419
|
desc 'End last X entries with no time tracked'
|
426
420
|
long_desc 'Adds @done tag without datestamp so no elapsed time is recorded. Alias for `doing finish --no-date`.'
|
427
|
-
arg_name '
|
421
|
+
arg_name 'COUNT'
|
428
422
|
command :cancel do |c|
|
429
423
|
c.desc 'Archive entries'
|
430
|
-
c.switch [
|
424
|
+
c.switch %i[a archive], negatable: false, default_value: false
|
431
425
|
|
432
426
|
c.desc 'Section'
|
433
|
-
c.
|
427
|
+
c.arg_name 'NAME'
|
428
|
+
c.flag %i[s section], default_value: wwid.current_section
|
434
429
|
|
435
|
-
c.
|
430
|
+
c.desc 'Cancel the last X entries containing TAG. Separate multiple tags with comma (--tag=tag1,tag2)'
|
431
|
+
c.arg_name 'TAG'
|
432
|
+
c.flag [:tag]
|
433
|
+
|
434
|
+
c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
|
435
|
+
c.arg_name 'BOOLEAN'
|
436
|
+
c.flag [:bool], must_match: /^(and|or|not)$/i, default_value: 'AND'
|
436
437
|
|
438
|
+
c.action do |_global_options, options, args|
|
437
439
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
438
440
|
|
439
|
-
if
|
440
|
-
|
441
|
-
elsif args.length == 0 || args[0] =~ /\d+/
|
442
|
-
count = args[0] ? args[0].to_i : 1
|
443
|
-
wwid.tag_last({:tags => ["done"], :count => count, :section => section, :archive => options[:a], :sequential => false, :date => false })
|
441
|
+
if options[:tag].nil?
|
442
|
+
tags = []
|
444
443
|
else
|
445
|
-
|
444
|
+
tags = options[:tag].split(/ *, */).map { |t| t.strip.sub(/^@/, '') }
|
445
|
+
options[:bool] = options[:bool] =~ /^(and|or|not)$/i ? options[:bool].upcase : 'AND'
|
446
446
|
end
|
447
|
+
|
448
|
+
raise 'Only one argument allowed' if args.length > 1
|
449
|
+
|
450
|
+
raise 'Invalid argument (specify number of recent items to mark @done)' unless args.empty? || args[0] =~ /\d+/
|
451
|
+
|
452
|
+
count = args[0] ? args[0].to_i : 1
|
453
|
+
opts = {
|
454
|
+
archive: options[:a],
|
455
|
+
count: count,
|
456
|
+
date: false,
|
457
|
+
section: section,
|
458
|
+
sequential: false,
|
459
|
+
tag: tags,
|
460
|
+
tag_bool: options[:bool],
|
461
|
+
tags: ['done']
|
462
|
+
}
|
463
|
+
wwid.tag_last(opts)
|
447
464
|
end
|
448
465
|
end
|
449
466
|
|
450
467
|
desc 'Mark last X entries as @done'
|
451
468
|
long_desc 'Marks the last X entries with a @done tag and current date. Does not alter already completed entries.'
|
452
|
-
arg_name '
|
469
|
+
arg_name 'COUNT'
|
453
470
|
command :finish do |c|
|
454
471
|
c.desc 'Include date'
|
455
|
-
c.switch [:date], :
|
472
|
+
c.switch [:date], negatable: true, default_value: true
|
456
473
|
|
457
474
|
c.desc 'Backdate completed date to date string [4pm|20m|2h|yesterday noon]'
|
458
|
-
c.
|
475
|
+
c.arg_name 'DATE_STRING'
|
476
|
+
c.flag %i[b back]
|
459
477
|
|
460
478
|
c.desc 'Set the completed date to the start date plus XX[hmd]'
|
461
|
-
c.
|
479
|
+
c.arg_name 'INTERVAL'
|
480
|
+
c.flag %i[t took]
|
462
481
|
|
463
|
-
c.desc 'Finish the last X entries containing TAG.
|
482
|
+
c.desc 'Finish the last X entries containing TAG.
|
483
|
+
Separate multiple tags with comma (--tag=tag1,tag2), combine with --bool'
|
484
|
+
c.arg_name 'TAG'
|
464
485
|
c.flag [:tag]
|
465
486
|
|
487
|
+
c.desc 'Finish the last X entries matching search filter, surround with slashes for regex (e.g. "/query.*/")'
|
488
|
+
c.arg_name 'QUERY'
|
489
|
+
c.flag [:search]
|
490
|
+
|
466
491
|
c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters'
|
467
|
-
c.
|
492
|
+
c.arg_name 'BOOLEAN'
|
493
|
+
c.flag [:bool], must_match: /^(and|or|not)$/i, default_value: 'AND'
|
468
494
|
|
469
|
-
c.desc
|
470
|
-
|
495
|
+
c.desc %(Auto-generate finish dates from next entry's start time.
|
496
|
+
Automatically generate completion dates 1 minute before next start date.
|
497
|
+
--auto overrides the --date and --back parameters.)
|
498
|
+
c.switch [:auto], negatable: false, default_value: false
|
471
499
|
|
472
500
|
c.desc 'Archive entries'
|
473
|
-
c.switch [
|
501
|
+
c.switch %i[a archive], negatable: false, default_value: false
|
474
502
|
|
475
503
|
c.desc 'Section'
|
476
|
-
c.
|
477
|
-
|
478
|
-
c.action do |global_options,options,args|
|
504
|
+
c.arg_name 'NAME'
|
505
|
+
c.flag %i[s section], default_value: wwid.current_section
|
479
506
|
|
507
|
+
c.action do |_global_options, options, args|
|
480
508
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
481
509
|
|
482
510
|
unless options[:auto]
|
483
|
-
raise
|
511
|
+
raise '--back and --took cannot be used together' if options[:back] && options[:took]
|
512
|
+
|
513
|
+
raise '--search and --tag cannot be used together' if options[:search] && options[:tag]
|
484
514
|
|
485
515
|
if options[:back]
|
486
516
|
date = wwid.chronify(options[:back])
|
487
517
|
|
488
|
-
raise
|
518
|
+
raise 'Unable to parse date string' if date.nil?
|
489
519
|
elsif options[:took]
|
490
520
|
date = wwid.chronify_qty(options[:took])
|
491
521
|
else
|
@@ -494,193 +524,244 @@ command :finish do |c|
|
|
494
524
|
end
|
495
525
|
|
496
526
|
if options[:tag].nil?
|
497
|
-
|
527
|
+
tags = []
|
498
528
|
else
|
499
|
-
|
500
|
-
|
501
|
-
options[:tag_bool] = options[:tag_bool].upcase
|
502
|
-
else
|
503
|
-
options[:tag_bool] = 'AND'
|
504
|
-
end
|
529
|
+
tags = options[:tag].split(/ *, */).map { |t| t.strip.sub(/^@/, '') }
|
530
|
+
options[:bool] = options[:bool] =~ /^(and|or|not)$/i ? options[:bool].upcase : 'AND'
|
505
531
|
end
|
506
532
|
|
507
|
-
if args.length > 1
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
|
513
|
-
|
514
|
-
|
533
|
+
raise 'Only one argument allowed' if args.length > 1
|
534
|
+
|
535
|
+
raise 'Invalid argument (specify number of recent items to mark @done)' unless args.length == 0 || args[0] =~ /\d+/
|
536
|
+
|
537
|
+
count = args[0] ? args[0].to_i : 1
|
538
|
+
opts = {
|
539
|
+
archive: options[:a],
|
540
|
+
back: date,
|
541
|
+
count: count,
|
542
|
+
date: options[:date],
|
543
|
+
search: options[:search],
|
544
|
+
section: section,
|
545
|
+
sequential: options[:auto],
|
546
|
+
tag: tags,
|
547
|
+
tag_bool: options[:bool],
|
548
|
+
tags: ['done']
|
549
|
+
}
|
550
|
+
wwid.tag_last(opts)
|
515
551
|
end
|
516
552
|
end
|
517
553
|
|
518
554
|
desc 'Repeat last entry as new entry'
|
519
|
-
arg_name 'section'
|
520
555
|
command [:again, :resume] do |c|
|
521
556
|
c.desc 'Section'
|
522
|
-
c.
|
557
|
+
c.arg_name 'NAME'
|
558
|
+
c.flag %i[s section], default_value: 'All'
|
523
559
|
|
524
|
-
c.desc '
|
525
|
-
c.arg_name '
|
526
|
-
c.flag [:
|
560
|
+
c.desc 'Add new entry to section (default: same section as repeated entry)'
|
561
|
+
c.arg_name 'SECTION_NAME'
|
562
|
+
c.flag [:in]
|
527
563
|
|
528
|
-
c.
|
529
|
-
|
564
|
+
c.desc 'Repeat last entry matching tags. Combine multiple tags with a comma.'
|
565
|
+
c.arg_name 'TAG'
|
566
|
+
c.flag [:tag]
|
567
|
+
|
568
|
+
c.desc 'Repeat last entry matching search. Surround with
|
569
|
+
slashes for regex (e.g. "/query/").'
|
570
|
+
c.arg_name 'QUERY'
|
571
|
+
c.flag [:search]
|
572
|
+
|
573
|
+
c.desc 'Boolean used to combine multiple tags'
|
574
|
+
c.arg_name 'BOOLEAN'
|
575
|
+
c.flag [:bool], must_match: /^(and|or|not)$/i, default_value: 'ALL'
|
576
|
+
|
577
|
+
c.desc 'Note'
|
578
|
+
c.arg_name 'TEXT'
|
579
|
+
c.flag %i[n note]
|
580
|
+
|
581
|
+
c.action do |_global_options, options, _args|
|
582
|
+
tags = options[:tag].nil? ? [] : options[:tag].split(/ *, */).map { |t| t.sub(/^@/, '').strip }
|
583
|
+
opts = {
|
584
|
+
in: options[:in],
|
585
|
+
note: options[:n],
|
586
|
+
search: options[:search],
|
587
|
+
section: options[:s],
|
588
|
+
tag: tags,
|
589
|
+
tag_bool: options[:bool]
|
590
|
+
}
|
591
|
+
wwid.restart_last(opts)
|
530
592
|
end
|
531
593
|
end
|
532
594
|
|
533
|
-
desc '
|
534
|
-
arg_name '
|
595
|
+
desc 'Add tag(s) to last entry'
|
596
|
+
arg_name 'TAG', :multiple
|
535
597
|
command :tag do |c|
|
536
598
|
c.desc 'Section'
|
537
|
-
c.
|
599
|
+
c.arg_name 'SECTION_NAME'
|
600
|
+
c.flag %i[s section], default_value: 'All'
|
538
601
|
|
539
602
|
c.desc 'How many recent entries to tag (0 for all)'
|
540
|
-
c.
|
603
|
+
c.arg_name 'COUNT'
|
604
|
+
c.flag %i[c count], default_value: 1
|
541
605
|
|
542
606
|
c.desc 'Include current date/time with tag'
|
543
|
-
c.switch [
|
607
|
+
c.switch %i[d date], negatable: false, default_value: false
|
544
608
|
|
545
609
|
c.desc 'Remove given tag(s)'
|
546
|
-
c.switch [
|
610
|
+
c.switch %i[r remove], negatable: false, default_value: false
|
547
611
|
|
548
612
|
c.desc 'Autotag entries based on autotag configuration in ~/.doingrc'
|
549
|
-
c.switch [
|
613
|
+
c.switch %i[a autotag], negatable: false, default_value: false
|
550
614
|
|
551
|
-
c.action do |
|
552
|
-
if args.
|
553
|
-
raise "You must specify at least one tag"
|
554
|
-
else
|
555
|
-
|
556
|
-
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
615
|
+
c.action do |_global_options, options, args|
|
616
|
+
raise 'You must specify at least one tag' if args.empty? && !options[:a]
|
557
617
|
|
558
|
-
|
559
|
-
if args.join("") =~ /,/
|
560
|
-
tags = args.join("").split(/,/)
|
561
|
-
else
|
562
|
-
tags = args.join(" ").split(" ") # in case tags are quoted as one arg
|
563
|
-
end
|
618
|
+
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
564
619
|
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
|
620
|
+
if options[:a]
|
621
|
+
tags = []
|
622
|
+
else
|
623
|
+
tags = if args.join('') =~ /,/
|
624
|
+
args.join('').split(/,/)
|
625
|
+
else
|
626
|
+
args.join(' ').split(' ') # in case tags are quoted as one arg
|
627
|
+
end
|
569
628
|
|
570
|
-
|
629
|
+
tags.map! { |tag| tag.sub(/^@/, '').strip }
|
630
|
+
end
|
571
631
|
|
572
|
-
|
573
|
-
section_q = section == 'All' ? "" : " in section #{section}"
|
632
|
+
count = options[:c].to_i
|
574
633
|
|
575
|
-
|
576
|
-
|
577
|
-
elsif options[:r]
|
578
|
-
question = "Are you sure you want to remove #{tags.join(" and ")} from all records#{section_q}"
|
579
|
-
else
|
580
|
-
question = "Are you sure you want to add #{tags.join(" and ")} to all records#{section_q}"
|
581
|
-
end
|
634
|
+
if count.zero?
|
635
|
+
section_q = section == 'All' ? '' : " in section #{section}"
|
582
636
|
|
583
|
-
|
637
|
+
question = if options[:a]
|
638
|
+
"Are you sure you want to autotag all records#{section_q}"
|
639
|
+
elsif options[:r]
|
640
|
+
"Are you sure you want to remove #{tags.join(' and ')} from all records#{section_q}"
|
641
|
+
else
|
642
|
+
"Are you sure you want to add #{tags.join(' and ')} to all records#{section_q}"
|
643
|
+
end
|
584
644
|
|
585
|
-
|
586
|
-
raise "Cancelled"
|
587
|
-
end
|
588
|
-
end
|
645
|
+
res = wwid.yn(question, default_response: false)
|
589
646
|
|
590
|
-
|
647
|
+
raise 'Cancelled' unless res
|
591
648
|
end
|
649
|
+
opts = {
|
650
|
+
autotag: options[:a],
|
651
|
+
count: count,
|
652
|
+
date: options[:date],
|
653
|
+
remove: options[:r],
|
654
|
+
section: section,
|
655
|
+
tags: tags
|
656
|
+
}
|
657
|
+
wwid.tag_last(opts)
|
592
658
|
end
|
593
659
|
end
|
594
660
|
|
595
661
|
desc 'Mark last entry as highlighted'
|
596
662
|
command [:mark, :flag] do |c|
|
597
663
|
c.desc 'Section'
|
598
|
-
c.
|
664
|
+
c.arg_name 'NAME'
|
665
|
+
c.flag %i[s section], default_value: wwid.current_section
|
599
666
|
|
600
667
|
c.desc 'Remove mark'
|
601
|
-
c.switch [
|
602
|
-
|
668
|
+
c.switch %i[r remove], negatable: false, default_value: false
|
603
669
|
|
604
|
-
c.action do |
|
605
|
-
mark = wwid.config['marker_tag'] ||
|
606
|
-
wwid.tag_last({:
|
670
|
+
c.action do |_global_options, options, _args|
|
671
|
+
mark = wwid.config['marker_tag'] || 'flagged'
|
672
|
+
wwid.tag_last({ tags: [mark], section: options[:s], remove: options[:r] })
|
607
673
|
end
|
608
674
|
end
|
609
675
|
|
610
676
|
desc 'List all entries'
|
611
|
-
long_desc
|
612
|
-
|
677
|
+
long_desc %(
|
678
|
+
The argument can be a section name, @tag(s) or both.
|
679
|
+
"pick" or "choose" as an argument will offer a section menu.
|
680
|
+
)
|
681
|
+
arg_name '[SECTION|@TAGS]'
|
613
682
|
command :show do |c|
|
683
|
+
c.desc 'Tag filter, combine multiple tags with a comma. Added for compatibility with other commands.'
|
684
|
+
c.arg_name 'TAG'
|
685
|
+
c.flag [:tag]
|
686
|
+
|
614
687
|
c.desc 'Tag boolean (AND,OR,NONE)'
|
615
|
-
c.
|
688
|
+
c.arg_name 'BOOLEAN'
|
689
|
+
c.flag %i[b bool], must_match: /^(and|or|not)$/i, default_value: 'OR'
|
616
690
|
|
617
691
|
c.desc 'Max count to show'
|
618
|
-
c.
|
692
|
+
c.arg_name 'MAX'
|
693
|
+
c.flag %i[c count], default_value: 0
|
619
694
|
|
620
695
|
c.desc 'Age (oldest/newest)'
|
621
|
-
c.
|
696
|
+
c.arg_name 'AGE'
|
697
|
+
c.flag %i[a age], default_value: 'newest'
|
622
698
|
|
623
699
|
c.desc 'Sort order (asc/desc)'
|
624
|
-
c.
|
700
|
+
c.arg_name 'ORDER'
|
701
|
+
c.flag %i[s sort], must_match: /^(a|d)/i, default_value: 'ASC'
|
625
702
|
|
626
|
-
c.desc %
|
703
|
+
c.desc %(
|
627
704
|
Date range to show, or a single day to filter date on.
|
628
705
|
Date range argument should be quoted. Date specifications can be natural language.
|
629
706
|
To specify a range, use "to" or "through": `doing show --from "monday to friday"`
|
630
|
-
|
631
|
-
c.
|
707
|
+
)
|
708
|
+
c.arg_name 'DATE_OR_RANGE'
|
709
|
+
c.flag %i[f from]
|
632
710
|
|
633
711
|
c.desc 'Show time intervals on @done tasks'
|
634
|
-
c.switch [
|
712
|
+
c.switch %i[t times], default_value: true
|
635
713
|
|
636
714
|
c.desc 'Show intervals with totals at the end of output'
|
637
|
-
c.switch [:totals], :
|
715
|
+
c.switch [:totals], default_value: false, negatable: true
|
638
716
|
|
639
717
|
c.desc 'Sort tags by (name|time)'
|
640
718
|
default = 'time'
|
641
|
-
if wwid.config.
|
642
|
-
|
643
|
-
|
644
|
-
c.flag [:tag_sort], :default_value => default
|
719
|
+
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
720
|
+
c.arg_name 'KEY'
|
721
|
+
c.flag [:tag_sort], must_match: /^(name|time)/i, default_value: default
|
645
722
|
|
646
723
|
c.desc 'Only show items with recorded time intervals'
|
647
|
-
c.switch [:only_timed], :
|
648
|
-
|
649
|
-
c.desc 'Output to export format (csv|html|json)'
|
650
|
-
c.flag [:o, :output]
|
651
|
-
c.action do |global_options,options,args|
|
724
|
+
c.switch [:only_timed], default_value: false, negatable: false
|
652
725
|
|
726
|
+
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
727
|
+
c.arg_name 'FORMAT'
|
728
|
+
c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
|
729
|
+
c.action do |_global_options, options, args|
|
653
730
|
tag_filter = false
|
654
731
|
tags = []
|
655
|
-
if args.length
|
656
|
-
|
657
|
-
|
732
|
+
if args.length.positive?
|
733
|
+
case args[0]
|
734
|
+
when /^all$/i
|
735
|
+
section = 'All'
|
658
736
|
args.shift
|
659
|
-
|
737
|
+
when /^(choose|pick)$/i
|
660
738
|
section = wwid.choose_section
|
661
739
|
args.shift
|
662
|
-
|
663
|
-
section =
|
740
|
+
when /^@/
|
741
|
+
section = 'All'
|
664
742
|
else
|
665
743
|
section = wwid.guess_section(args[0])
|
666
744
|
raise "No such section: #{args[0]}" unless section
|
745
|
+
|
667
746
|
args.shift
|
668
747
|
end
|
669
|
-
if args.length
|
670
|
-
args.each
|
748
|
+
if args.length.positive?
|
749
|
+
args.each do |arg|
|
671
750
|
if arg =~ /,/
|
672
|
-
arg.split(/,/).each
|
673
|
-
tags.push(tag.strip.sub(/^@/,''))
|
674
|
-
|
751
|
+
arg.split(/,/).each do |tag|
|
752
|
+
tags.push(tag.strip.sub(/^@/, ''))
|
753
|
+
end
|
675
754
|
else
|
676
|
-
tags.push(arg.strip.sub(/^@/,''))
|
755
|
+
tags.push(arg.strip.sub(/^@/, ''))
|
677
756
|
end
|
678
|
-
|
757
|
+
end
|
679
758
|
end
|
680
759
|
else
|
681
760
|
section = wwid.current_section
|
682
761
|
end
|
683
762
|
|
763
|
+
tags.concat(options[:tag].split(/ *, */).map { |t| t.sub(/^@/, '').strip }) if options[:tag]
|
764
|
+
|
684
765
|
unless tags.empty?
|
685
766
|
tag_filter = {
|
686
767
|
'tags' => tags,
|
@@ -698,96 +779,127 @@ command :show do |c|
|
|
698
779
|
start = wwid.chronify(date_string)
|
699
780
|
finish = false
|
700
781
|
end
|
701
|
-
exit_now!
|
702
|
-
dates = [start,finish]
|
782
|
+
exit_now! 'Unrecognized date string' unless start
|
783
|
+
dates = [start, finish]
|
703
784
|
end
|
704
785
|
|
705
786
|
options[:t] = true if options[:totals]
|
706
787
|
|
707
|
-
|
708
|
-
|
709
|
-
puts wwid.list_section({:section => section, :date_filter => dates, :count => options[:c].to_i, :tag_filter => tag_filter, :age => options[:a], :order => options[:s], :output => options[:output], :times => options[:t], :totals => options[:totals], :sort_tags => options[:sort_tags], :highlight => true, :only_timed => options[:only_timed]})
|
788
|
+
tags_color = wwid.config.key?('tags_color') ? wwid.config['tags_color'] : nil
|
710
789
|
|
790
|
+
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
791
|
+
opts = {
|
792
|
+
age: options[:a],
|
793
|
+
count: options[:c].to_i,
|
794
|
+
date_filter: dates,
|
795
|
+
highlight: true,
|
796
|
+
only_timed: options[:only_timed],
|
797
|
+
order: options[:s],
|
798
|
+
output: options[:output],
|
799
|
+
section: section,
|
800
|
+
sort_tags: options[:sort_tags],
|
801
|
+
tag_filter: tag_filter,
|
802
|
+
tags_color: tags_color,
|
803
|
+
times: options[:t],
|
804
|
+
totals: options[:totals]
|
805
|
+
}
|
806
|
+
puts wwid.list_section(opts)
|
711
807
|
end
|
712
808
|
end
|
713
809
|
|
714
810
|
desc 'Search for entries'
|
715
|
-
long_desc
|
716
|
-
Search all sections (or limit to a single section) for entries matching text or regular expression. Normal strings are fuzzy matched.
|
811
|
+
long_desc <<~'EODESC'
|
812
|
+
Search all sections (or limit to a single section) for entries matching text or regular expression. Normal strings are fuzzy matched.
|
717
813
|
|
718
|
-
To search with regular expressions, single quote the string and surround with slashes: `doing search '/\bm.*?x\b/'`
|
814
|
+
To search with regular expressions, single quote the string and surround with slashes: `doing search '/\bm.*?x\b/'`
|
719
815
|
EODESC
|
720
816
|
|
721
|
-
arg_name '
|
817
|
+
arg_name 'SEARCH_PATTERN'
|
722
818
|
command [:grep, :search] do |c|
|
723
819
|
c.desc 'Section'
|
724
|
-
c.
|
820
|
+
c.arg_name 'NAME'
|
821
|
+
c.flag %i[s section], default_value: 'All'
|
725
822
|
|
726
|
-
c.desc 'Output to export format (csv|html|json)'
|
727
|
-
c.
|
823
|
+
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
824
|
+
c.arg_name 'FORMAT'
|
825
|
+
c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
|
728
826
|
|
729
827
|
c.desc 'Show time intervals on @done tasks'
|
730
|
-
c.switch [
|
828
|
+
c.switch %i[t times], default_value: true
|
731
829
|
|
732
830
|
c.desc 'Show intervals with totals at the end of output'
|
733
|
-
c.switch [:totals], :
|
831
|
+
c.switch [:totals], default_value: false, negatable: true
|
734
832
|
|
735
833
|
c.desc 'Sort tags by (name|time)'
|
736
834
|
default = 'time'
|
737
|
-
if wwid.config.
|
738
|
-
|
739
|
-
|
740
|
-
c.flag [:tag_sort], :default_value => default
|
835
|
+
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
836
|
+
c.arg_name 'KEY'
|
837
|
+
c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
|
741
838
|
|
742
839
|
c.desc 'Only show items with recorded time intervals'
|
743
|
-
c.switch [:only_timed], :
|
840
|
+
c.switch [:only_timed], default_value: false, negatable: false
|
744
841
|
|
745
|
-
c.action do |
|
842
|
+
c.action do |_global_options, options, args|
|
843
|
+
tags_color = wwid.config.key?('tags_color') ? wwid.config['tags_color'] : nil
|
746
844
|
|
747
845
|
section = wwid.guess_section(options[:s]) if options[:s]
|
748
846
|
|
749
847
|
options[:t] = true if options[:totals]
|
750
848
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
751
849
|
|
752
|
-
|
850
|
+
opts = {
|
851
|
+
highlight: true,
|
852
|
+
only_timed: options[:only_timed],
|
853
|
+
output: options[:output],
|
854
|
+
search: args.join(' '),
|
855
|
+
section: section,
|
856
|
+
sort_tags: options[:sort_tags],
|
857
|
+
tags_color: tags_color,
|
858
|
+
times: options[:t],
|
859
|
+
totals: options[:totals]
|
860
|
+
}
|
753
861
|
|
862
|
+
puts wwid.list_section(opts)
|
754
863
|
end
|
755
864
|
end
|
756
865
|
|
757
866
|
desc 'List recent entries'
|
758
867
|
default_value 10
|
759
|
-
arg_name '
|
868
|
+
arg_name 'COUNT'
|
760
869
|
command :recent do |c|
|
761
870
|
c.desc 'Section'
|
762
|
-
c.
|
871
|
+
c.arg_name 'NAME'
|
872
|
+
c.flag %i[s section], default_value: 'All'
|
763
873
|
|
764
874
|
c.desc 'Show time intervals on @done tasks'
|
765
|
-
c.switch [
|
875
|
+
c.switch %i[t times], default_value: true
|
766
876
|
|
767
877
|
c.desc 'Show intervals with totals at the end of output'
|
768
|
-
c.switch [:totals], :
|
878
|
+
c.switch [:totals], default_value: false, negatable: true
|
769
879
|
|
770
880
|
c.desc 'Sort tags by (name|time)'
|
771
881
|
default = 'time'
|
772
|
-
if wwid.config.
|
773
|
-
|
774
|
-
|
775
|
-
c.flag [:tag_sort], :default_value => default
|
776
|
-
|
777
|
-
c.action do |global_options,options,args|
|
882
|
+
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
883
|
+
c.arg_name 'KEY'
|
884
|
+
c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
|
778
885
|
|
886
|
+
c.action do |global_options, options, args|
|
779
887
|
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
780
888
|
|
781
889
|
unless global_options[:version]
|
782
|
-
|
783
|
-
count = args[0].to_i
|
784
|
-
else
|
785
|
-
count = 10
|
786
|
-
end
|
890
|
+
count = args.empty? ? 10 : args[0].to_i
|
787
891
|
options[:t] = true if options[:totals]
|
788
892
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
893
|
+
tags_color = wwid.config.key?('tags_color') ? wwid.config['tags_color'] : nil
|
894
|
+
|
895
|
+
opts = {
|
896
|
+
sort_tags: options[:sort_tags],
|
897
|
+
tags_color: tags_color,
|
898
|
+
times: options[:t],
|
899
|
+
totals: options[:totals]
|
900
|
+
}
|
789
901
|
|
790
|
-
puts wwid.recent(count,section.cap_first,
|
902
|
+
puts wwid.recent(count, section.cap_first, opts)
|
791
903
|
|
792
904
|
end
|
793
905
|
end
|
@@ -796,62 +908,64 @@ end
|
|
796
908
|
desc 'List entries from today'
|
797
909
|
command :today do |c|
|
798
910
|
c.desc 'Specify a section'
|
799
|
-
c.arg_name '
|
800
|
-
c.flag [
|
911
|
+
c.arg_name 'NAME'
|
912
|
+
c.flag %i[s section], default_value: 'All'
|
801
913
|
|
802
914
|
c.desc 'Show time intervals on @done tasks'
|
803
|
-
c.switch [
|
915
|
+
c.switch %i[t times], default_value: true
|
804
916
|
|
805
917
|
c.desc 'Show time totals at the end of output'
|
806
|
-
c.switch [:totals], :
|
918
|
+
c.switch [:totals], default_value: false, negatable: true
|
807
919
|
|
808
920
|
c.desc 'Sort tags by (name|time)'
|
809
921
|
default = 'time'
|
810
|
-
if wwid.config.
|
811
|
-
|
812
|
-
|
813
|
-
c.flag [:tag_sort], :default_value => default
|
814
|
-
|
815
|
-
c.desc 'Output to export format (csv|html|json)'
|
816
|
-
c.flag [:o, :output]
|
922
|
+
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
923
|
+
c.arg_name 'KEY'
|
924
|
+
c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
|
817
925
|
|
818
|
-
c.
|
926
|
+
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
927
|
+
c.arg_name 'FORMAT'
|
928
|
+
c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
|
819
929
|
|
930
|
+
c.action do |_global_options, options, _args|
|
820
931
|
options[:t] = true if options[:totals]
|
821
932
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
822
933
|
|
823
|
-
puts wwid.today(options[:t],options[:output],
|
824
|
-
|
934
|
+
puts wwid.today(options[:t], options[:output],
|
935
|
+
{ totals: options[:totals], section: options[:s], sort_tags: options[:sort_tags] }).chomp
|
825
936
|
end
|
826
937
|
end
|
827
938
|
|
828
939
|
desc 'List entries for a date'
|
829
|
-
long_desc
|
830
|
-
|
940
|
+
long_desc %(Date argument can be natural language. "thursday" would be interpreted as "last thursday,"
|
941
|
+
and "2d" would be interpreted as "two days ago." If you use "to" or "through" between two dates,
|
942
|
+
it will create a range.)
|
943
|
+
arg_name 'DATE_STRING'
|
831
944
|
command :on do |c|
|
832
945
|
c.desc 'Section'
|
833
|
-
c.arg_name '
|
834
|
-
c.flag [
|
946
|
+
c.arg_name 'NAME'
|
947
|
+
c.flag %i[s section], default_value: 'All'
|
835
948
|
|
836
949
|
c.desc 'Show time intervals on @done tasks'
|
837
|
-
c.switch [
|
950
|
+
c.switch %i[t times], default_value: true
|
838
951
|
|
839
952
|
c.desc 'Show time totals at the end of output'
|
840
|
-
c.switch [:totals], :
|
953
|
+
c.switch [:totals], default_value: false, negatable: true
|
841
954
|
|
842
955
|
c.desc 'Sort tags by (name|time)'
|
843
956
|
default = 'time'
|
844
|
-
if wwid.config.
|
845
|
-
|
846
|
-
|
847
|
-
c.flag [:tag_sort], :default_value => default
|
957
|
+
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
958
|
+
c.arg_name 'KEY'
|
959
|
+
c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
|
848
960
|
|
849
|
-
c.desc 'Output to export format (csv|html|json)'
|
850
|
-
c.
|
961
|
+
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
962
|
+
c.arg_name 'FORMAT'
|
963
|
+
c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
|
851
964
|
|
852
|
-
c.action do |
|
965
|
+
c.action do |_global_options, options, args|
|
966
|
+
exit_now! 'Missing date argument' if args.empty?
|
853
967
|
|
854
|
-
date_string = args.join(
|
968
|
+
date_string = args.join(' ')
|
855
969
|
|
856
970
|
if date_string =~ / (to|through|thru) /
|
857
971
|
dates = date_string.split(/ (to|through|thru) /)
|
@@ -862,7 +976,7 @@ command :on do |c|
|
|
862
976
|
finish = false
|
863
977
|
end
|
864
978
|
|
865
|
-
exit_now!
|
979
|
+
exit_now! 'Unrecognized date string' unless start
|
866
980
|
|
867
981
|
message = "Date interpreted as #{start}"
|
868
982
|
message += " to #{finish}" if finish
|
@@ -871,56 +985,86 @@ command :on do |c|
|
|
871
985
|
options[:t] = true if options[:totals]
|
872
986
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
873
987
|
|
874
|
-
puts wwid.list_date([start, finish], options[:s], options[:t], options[:output],
|
875
|
-
|
988
|
+
puts wwid.list_date([start, finish], options[:s], options[:t], options[:output],
|
989
|
+
{ totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
876
990
|
end
|
877
991
|
end
|
878
992
|
|
879
993
|
desc 'List entries from yesterday'
|
880
994
|
command :yesterday do |c|
|
881
995
|
c.desc 'Specify a section'
|
882
|
-
c.arg_name '
|
883
|
-
c.flag [
|
996
|
+
c.arg_name 'NAME'
|
997
|
+
c.flag %i[s section], default_value: 'All'
|
884
998
|
|
885
|
-
c.desc 'Output to export format (csv|html|json)'
|
886
|
-
c.
|
999
|
+
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
1000
|
+
c.arg_name 'FORMAT'
|
1001
|
+
c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
|
887
1002
|
|
888
1003
|
c.desc 'Show time intervals on @done tasks'
|
889
|
-
c.switch [
|
1004
|
+
c.switch %i[t times], default_value: true
|
890
1005
|
|
891
1006
|
c.desc 'Show time totals at the end of output'
|
892
|
-
c.switch [:totals], :
|
1007
|
+
c.switch [:totals], default_value: false, negatable: true
|
893
1008
|
|
894
1009
|
c.desc 'Sort tags by (name|time)'
|
895
1010
|
default = 'time'
|
896
|
-
if wwid.config.
|
897
|
-
|
898
|
-
|
899
|
-
c.flag [:tag_sort], :default_value => default
|
1011
|
+
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
1012
|
+
c.arg_name 'KEY'
|
1013
|
+
c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
|
900
1014
|
|
901
|
-
c.action do |
|
1015
|
+
c.action do |_global_options, options, _args|
|
902
1016
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
903
|
-
puts wwid.yesterday(options[:s],options[:t],options[:o],
|
904
|
-
|
1017
|
+
puts wwid.yesterday(options[:s], options[:t], options[:o],
|
1018
|
+
{ totals: options[:totals], sort_tags: options[:sort_tags] }).chomp
|
905
1019
|
end
|
906
1020
|
end
|
907
1021
|
|
908
|
-
desc 'Show the last entry'
|
1022
|
+
desc 'Show the last entry, optionally edit'
|
909
1023
|
command :last do |c|
|
910
1024
|
c.desc 'Specify a section'
|
911
|
-
c.
|
1025
|
+
c.arg_name 'NAME'
|
1026
|
+
c.flag %i[s section], default_value: 'All'
|
1027
|
+
|
1028
|
+
c.desc "Edit entry with #{ENV['EDITOR']}"
|
1029
|
+
c.switch %i[e editor], negatable: false, default_value: false
|
1030
|
+
|
1031
|
+
c.desc 'Tag filter, combine multiple tags with a comma.'
|
1032
|
+
c.arg_name 'TAG'
|
1033
|
+
c.flag [:tag]
|
1034
|
+
|
1035
|
+
c.desc 'Tag boolean'
|
1036
|
+
c.arg_name 'BOOLEAN'
|
1037
|
+
c.flag [:bool], must_match: /(and|or|not)/i, default_value: 'AND'
|
1038
|
+
|
1039
|
+
c.desc 'Search filter, surround with slashes for regex (/query/)'
|
1040
|
+
c.arg_name 'QUERY'
|
1041
|
+
c.flag [:search]
|
912
1042
|
|
913
|
-
c.action do |
|
914
|
-
|
1043
|
+
c.action do |_global_options, options, _args|
|
1044
|
+
raise '--tag and --search cannot be used together' if options[:tag] && options[:search]
|
1045
|
+
|
1046
|
+
if options[:tag].nil?
|
1047
|
+
tags = []
|
1048
|
+
else
|
1049
|
+
tags = options[:tag].split(/ *, */).map { |t| t.strip.sub(/^@/, '') }
|
1050
|
+
options[:bool] = options[:bool] =~ /^(and|or|not)$/i ? options[:bool].upcase : 'AND'
|
1051
|
+
end
|
1052
|
+
|
1053
|
+
if options[:e]
|
1054
|
+
wwid.edit_last(section: options[:s], options: { search: options[:search], tag: tags, tag_bool: options[:bool] })
|
1055
|
+
else
|
1056
|
+
puts wwid.last(times: true, section: options[:s],
|
1057
|
+
options: { search: options[:search], tag: tags, tag_bool: options[:bool] }).strip
|
1058
|
+
end
|
915
1059
|
end
|
916
1060
|
end
|
917
1061
|
|
918
1062
|
desc 'List sections'
|
919
1063
|
command :sections do |c|
|
920
1064
|
c.desc 'List in single column'
|
921
|
-
c.switch [
|
1065
|
+
c.switch %i[c column], default_value: false
|
922
1066
|
|
923
|
-
c.action do |
|
1067
|
+
c.action do |_global_options, options, _args|
|
924
1068
|
joiner = options[:c] ? "\n" : "\t"
|
925
1069
|
print wwid.sections.join(joiner)
|
926
1070
|
end
|
@@ -928,124 +1072,145 @@ end
|
|
928
1072
|
|
929
1073
|
desc 'Select a section to display from a menu'
|
930
1074
|
command :choose do |c|
|
931
|
-
c.action do |
|
1075
|
+
c.action do |_global_options, _options, _args|
|
932
1076
|
section = wwid.choose_section
|
933
|
-
puts wwid.list_section({:
|
1077
|
+
puts wwid.list_section({ section: section.cap_first, count: 0 })
|
934
1078
|
end
|
935
1079
|
end
|
936
1080
|
|
937
1081
|
desc 'Add a new section to the "doing" file'
|
938
|
-
arg_name '
|
1082
|
+
arg_name 'SECTION_NAME'
|
939
1083
|
command :add_section do |c|
|
940
|
-
c.action do |
|
941
|
-
|
942
|
-
|
943
|
-
|
944
|
-
|
945
|
-
raise "Section #{args[0]} already exists"
|
946
|
-
end
|
1084
|
+
c.action do |_global_options, _options, args|
|
1085
|
+
raise "Section #{args[0]} already exists" if wwid.sections.include?(args[0])
|
1086
|
+
|
1087
|
+
wwid.add_section(args[0].cap_first)
|
1088
|
+
wwid.write(wwid.doing_file)
|
947
1089
|
end
|
948
1090
|
end
|
949
1091
|
|
950
1092
|
desc 'List available color variables for configuration templates and views'
|
951
1093
|
command :colors do |c|
|
952
|
-
c.action do |
|
1094
|
+
c.action do |_global_options, _options, _args|
|
953
1095
|
clrs = wwid.colors
|
954
1096
|
bgs = []
|
955
1097
|
fgs = []
|
956
|
-
clrs.each
|
1098
|
+
clrs.each do |k, v|
|
957
1099
|
if k =~ /bg/
|
958
1100
|
bgs.push("#{v} #{clrs['default']} <-- #{k}")
|
959
1101
|
else
|
960
1102
|
fgs.push("#{v}XXXX#{clrs['default']} <-- #{k}")
|
961
1103
|
end
|
962
|
-
|
1104
|
+
end
|
963
1105
|
puts fgs.join("\n")
|
964
1106
|
puts bgs.join("\n")
|
965
1107
|
end
|
966
1108
|
end
|
967
1109
|
|
968
1110
|
desc 'Display a user-created view'
|
969
|
-
arg_name '
|
1111
|
+
arg_name 'VIEW_NAME'
|
970
1112
|
command :view do |c|
|
971
1113
|
c.desc 'Section (override view settings)'
|
972
|
-
c.
|
1114
|
+
c.arg_name 'NAME'
|
1115
|
+
c.flag %i[s section]
|
973
1116
|
|
974
1117
|
c.desc 'Count to display (override view settings)'
|
975
|
-
c.
|
1118
|
+
c.arg_name 'COUNT'
|
1119
|
+
c.flag %i[c count], must_match: /^\d+$/, type: Integer
|
976
1120
|
|
977
|
-
c.desc 'Output to export format (csv|html|json)'
|
978
|
-
c.
|
1121
|
+
c.desc 'Output to export format (csv|html|json|template|timeline)'
|
1122
|
+
c.arg_name 'FORMAT'
|
1123
|
+
c.flag %i[o output], must_match: /^(template|html|csv|json|timeline)$/i
|
979
1124
|
|
980
1125
|
c.desc 'Show time intervals on @done tasks'
|
981
|
-
c.switch [
|
1126
|
+
c.switch %i[t times], default_value: true
|
982
1127
|
|
983
1128
|
c.desc 'Show intervals with totals at the end of output'
|
984
|
-
c.switch [:totals], :
|
1129
|
+
c.switch [:totals], default_value: false, negatable: true
|
1130
|
+
|
1131
|
+
c.desc 'Include colors in output'
|
1132
|
+
c.switch [:color], default_value: true, negatable: true
|
985
1133
|
|
986
1134
|
c.desc 'Sort tags by (name|time)'
|
987
1135
|
default = 'time'
|
988
|
-
if wwid.config.
|
989
|
-
|
990
|
-
|
991
|
-
c.flag [:tag_sort], :default_value => default
|
1136
|
+
default = wwid.config['tag_sort'] if wwid.config.key?('tag_sort')
|
1137
|
+
c.arg_name 'KEY'
|
1138
|
+
c.flag [:tag_sort], must_match: /^(name|time)$/i, default_value: default
|
992
1139
|
|
993
1140
|
c.desc 'Only show items with recorded time intervals'
|
994
|
-
c.switch [:only_timed], :
|
1141
|
+
c.switch [:only_timed], default_value: false, negatable: true
|
995
1142
|
|
996
|
-
c.action do |
|
997
|
-
if args.empty?
|
998
|
-
|
999
|
-
|
1000
|
-
|
1001
|
-
|
1143
|
+
c.action do |_global_options, options, args|
|
1144
|
+
title = if args.empty?
|
1145
|
+
wwid.choose_view
|
1146
|
+
else
|
1147
|
+
wwid.guess_view(args[0])
|
1148
|
+
end
|
1002
1149
|
|
1003
|
-
if options[:s]
|
1004
|
-
section = wwid.guess_section(options[:s]) || options[:s].cap_first
|
1005
|
-
end
|
1150
|
+
section = wwid.guess_section(options[:s]) || options[:s].cap_first if options[:s]
|
1006
1151
|
|
1007
1152
|
view = wwid.get_view(title)
|
1008
1153
|
if view
|
1009
|
-
if (view.
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
|
1015
|
-
template = view.
|
1016
|
-
format = view.
|
1017
|
-
tags_color = view.
|
1154
|
+
only_timed = if (view.key?('only_timed') && view['only_timed']) || options[:only_timed]
|
1155
|
+
true
|
1156
|
+
else
|
1157
|
+
false
|
1158
|
+
end
|
1159
|
+
|
1160
|
+
template = view.key?('template') ? view['template'] : nil
|
1161
|
+
format = view.key?('date_format') ? view['date_format'] : nil
|
1162
|
+
tags_color = view.key?('tags_color') ? view['tags_color'] : nil
|
1018
1163
|
tag_filter = false
|
1019
|
-
if view.
|
1020
|
-
|
1021
|
-
|
1022
|
-
|
1023
|
-
|
1024
|
-
|
1025
|
-
|
1026
|
-
|
1027
|
-
tag_filter['bool'] = view.has_key?('tags_bool') && !view['tags_bool'].nil? ? view['tags_bool'].upcase : "OR"
|
1028
|
-
end
|
1164
|
+
if view.key?('tags') && !(view['tags'].nil? || view['tags'].empty?)
|
1165
|
+
tag_filter = { 'tags' => [], 'bool' => 'OR' }
|
1166
|
+
tag_filter['tags'] = if view['tags'].instance_of?(Array)
|
1167
|
+
view['tags'].map(&:strip)
|
1168
|
+
else
|
1169
|
+
view['tags'].gsub(/[, ]+/, ' ').split(' ').map(&:strip)
|
1170
|
+
end
|
1171
|
+
tag_filter['bool'] = view.key?('tags_bool') && !view['tags_bool'].nil? ? view['tags_bool'].upcase : 'OR'
|
1029
1172
|
end
|
1030
1173
|
|
1031
1174
|
# If the -o/--output flag was specified, override any default in the view template
|
1032
|
-
options[:o] ||= view.
|
1033
|
-
|
1034
|
-
count =
|
1035
|
-
|
1036
|
-
|
1175
|
+
options[:o] ||= view.key?('output_format') ? view['output_format'] : 'template'
|
1176
|
+
|
1177
|
+
count = if options[:c]
|
1178
|
+
options[:c]
|
1179
|
+
else
|
1180
|
+
view.key?('count') ? view['count'] : 10
|
1181
|
+
end
|
1182
|
+
section = if options[:s]
|
1183
|
+
section
|
1184
|
+
else
|
1185
|
+
view.key?('section') ? view['section'] : wwid.current_section
|
1186
|
+
end
|
1187
|
+
order = view.key?('order') ? view['order'] : 'asc'
|
1037
1188
|
|
1038
1189
|
options[:t] = true if options[:totals]
|
1039
|
-
options[:output]
|
1190
|
+
options[:output]&.downcase!
|
1040
1191
|
options[:sort_tags] = options[:tag_sort] =~ /^n/i
|
1041
1192
|
|
1042
|
-
|
1193
|
+
opts = {
|
1194
|
+
count: count,
|
1195
|
+
format: format,
|
1196
|
+
highlight: options[:color],
|
1197
|
+
only_timed: only_timed,
|
1198
|
+
order: order,
|
1199
|
+
output: options[:o],
|
1200
|
+
section: section,
|
1201
|
+
sort_tags: options[:sort_tags],
|
1202
|
+
tag_filter: tag_filter,
|
1203
|
+
tags_color: tags_color,
|
1204
|
+
template: template,
|
1205
|
+
times: options[:t],
|
1206
|
+
totals: options[:totals]
|
1207
|
+
}
|
1208
|
+
|
1209
|
+
puts wwid.list_section(opts)
|
1210
|
+
elsif title.instance_of?(FalseClass)
|
1211
|
+
exit_now! 'Cancelled'
|
1043
1212
|
else
|
1044
|
-
|
1045
|
-
exit_now! "Cancelled"
|
1046
|
-
else
|
1047
|
-
raise "View #{title} not found in config"
|
1048
|
-
end
|
1213
|
+
raise "View #{title} not found in config"
|
1049
1214
|
end
|
1050
1215
|
end
|
1051
1216
|
end
|
@@ -1053,124 +1218,135 @@ end
|
|
1053
1218
|
desc 'List available custom views'
|
1054
1219
|
command :views do |c|
|
1055
1220
|
c.desc 'List in single column'
|
1056
|
-
c.switch [
|
1221
|
+
c.switch %i[c column], default_value: false
|
1057
1222
|
|
1058
|
-
c.action do |
|
1223
|
+
c.action do |_global_options, options, _args|
|
1059
1224
|
joiner = options[:c] ? "\n" : "\t"
|
1060
1225
|
print wwid.views.join(joiner)
|
1061
1226
|
end
|
1062
1227
|
end
|
1063
1228
|
|
1064
|
-
desc 'Move entries
|
1065
|
-
arg_name '
|
1229
|
+
desc 'Move entries between sections'
|
1230
|
+
arg_name 'SECTION_NAME'
|
1066
1231
|
default_value wwid.current_section
|
1067
1232
|
command :archive do |c|
|
1068
1233
|
c.desc 'Count to keep (ignored if archiving by tag)'
|
1069
|
-
c.
|
1234
|
+
c.arg_name 'COUNT'
|
1235
|
+
c.flag %i[k keep], default_value: 5, must_match: /^\d+$/, type: Integer
|
1070
1236
|
|
1071
1237
|
c.desc 'Move entries to'
|
1072
|
-
c.
|
1238
|
+
c.arg_name 'SECTION_NAME'
|
1239
|
+
c.flag %i[t to], default_value: 'Archive'
|
1073
1240
|
|
1074
|
-
c.desc 'Tag
|
1075
|
-
c.
|
1241
|
+
c.desc 'Tag filter, combine multiple tags with a comma. Added for compatibility with other commands.'
|
1242
|
+
c.arg_name 'TAG'
|
1243
|
+
c.flag [:tag]
|
1076
1244
|
|
1077
|
-
c.
|
1078
|
-
|
1079
|
-
|
1080
|
-
|
1081
|
-
|
1082
|
-
|
1083
|
-
section = args[0].cap_first
|
1084
|
-
tags = args.length > 1 ? args[1..-1].map {|t| t.sub(/^@/,'').strip } : nil
|
1085
|
-
end
|
1086
|
-
else
|
1245
|
+
c.desc 'Tag boolean (AND|OR|NOT)'
|
1246
|
+
c.arg_name 'BOOLEAN'
|
1247
|
+
c.flag [:bool], must_match: /(and|or|not)/i, default_value: 'AND'
|
1248
|
+
|
1249
|
+
c.action do |_global_options, options, args|
|
1250
|
+
if args.empty?
|
1087
1251
|
section = wwid.current_section
|
1088
|
-
tags =
|
1252
|
+
tags = []
|
1253
|
+
elsif args[0] =~ /^@\S+/
|
1254
|
+
section = 'all'
|
1255
|
+
tags = args.map { |t| t.sub(/^@/, '').strip }
|
1256
|
+
else
|
1257
|
+
section = args[0].cap_first
|
1258
|
+
tags = args.length > 1 ? args[1..].map { |t| t.sub(/^@/, '').strip } : nil
|
1089
1259
|
end
|
1090
|
-
|
1260
|
+
|
1261
|
+
tags.concat(options[:tag].split(/ *, */).map { |t| t.sub(/^@/, '').strip }) if options[:tag]
|
1262
|
+
|
1263
|
+
wwid.archive(section, options[:keep], options[:to], tags, options[:bool])
|
1091
1264
|
end
|
1092
1265
|
end
|
1093
1266
|
|
1094
1267
|
desc 'Open the "doing" file in an editor'
|
1268
|
+
long_desc "`doing open` defaults to using the editor_app setting in #{wwid.config_file} (#{wwid.config.key?('editor_app') ? wwid.config['editor_app'] : 'not set'})"
|
1095
1269
|
command :open do |c|
|
1096
|
-
if `uname` =~ /Darwin/
|
1097
|
-
|
1098
|
-
|
1099
|
-
|
1100
|
-
|
1101
|
-
|
1102
|
-
|
1103
|
-
|
1104
|
-
end
|
1105
|
-
c.desc
|
1106
|
-
c.switch [
|
1270
|
+
if `uname` =~ /Darwin/
|
1271
|
+
c.desc 'Open with app name'
|
1272
|
+
c.arg_name 'APP_NAME'
|
1273
|
+
c.flag [:a]
|
1274
|
+
|
1275
|
+
c.desc 'Open with app bundle id'
|
1276
|
+
c.arg_name 'BUNDLE_ID'
|
1277
|
+
c.flag [:b]
|
1278
|
+
end
|
1279
|
+
c.desc "Open with $EDITOR (#{ENV['EDITOR']})"
|
1280
|
+
c.switch %i[e editor], negatable: false, default_value: false
|
1107
1281
|
|
1108
|
-
c.action do |
|
1282
|
+
c.action do |_global_options, options, _args|
|
1109
1283
|
params = options.dup
|
1110
|
-
params.delete_if
|
1111
|
-
k.
|
1112
|
-
|
1284
|
+
params.delete_if do |k, v|
|
1285
|
+
k.instance_of?(String) || v.nil? || v == false
|
1286
|
+
end
|
1113
1287
|
if `uname` =~ /Darwin/
|
1114
|
-
if params.length < 2
|
1115
1288
|
if options[:a]
|
1116
|
-
system %
|
1289
|
+
system %(open -a "#{options[:a]}" "#{File.expand_path(wwid.doing_file)}")
|
1117
1290
|
elsif options[:b]
|
1118
|
-
system %
|
1291
|
+
system %(open -b "#{options[:b]}" "#{File.expand_path(wwid.doing_file)}")
|
1119
1292
|
elsif options[:e]
|
1120
|
-
raise
|
1121
|
-
|
1293
|
+
raise 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
1294
|
+
|
1295
|
+
system %($EDITOR "#{File.expand_path(wwid.doing_file)}")
|
1296
|
+
elsif wwid.config.key?('editor_app') && !wwid.config['editor_app'].nil?
|
1297
|
+
system %(open -a "#{wwid.config['editor_app']}" "#{File.expand_path(wwid.doing_file)}")
|
1122
1298
|
else
|
1123
|
-
|
1124
|
-
system %Q{open -a "#{wwid.config['editor_app']}" "#{File.expand_path(wwid.doing_file)}"}
|
1125
|
-
else
|
1126
|
-
system %Q{open "#{File.expand_path(wwid.doing_file)}"}
|
1127
|
-
end
|
1299
|
+
system %(open "#{File.expand_path(wwid.doing_file)}")
|
1128
1300
|
end
|
1301
|
+
|
1129
1302
|
else
|
1130
|
-
raise
|
1131
|
-
|
1132
|
-
|
1133
|
-
raise "No EDITOR variable defined in environment" if ENV['EDITOR'].nil?
|
1134
|
-
system %Q{$EDITOR "#{File.expand_path(wwid.doing_file)}"}
|
1303
|
+
raise 'No EDITOR variable defined in environment' if ENV['EDITOR'].nil?
|
1304
|
+
|
1305
|
+
system %($EDITOR "#{File.expand_path(wwid.doing_file)}")
|
1135
1306
|
end
|
1136
1307
|
end
|
1137
1308
|
end
|
1138
1309
|
|
1139
|
-
|
1140
1310
|
desc 'Edit the configuration file'
|
1141
1311
|
command :config do |c|
|
1142
1312
|
c.desc 'Editor to use'
|
1143
|
-
c.
|
1313
|
+
c.arg_name 'EDITOR'
|
1314
|
+
c.flag %i[e editor], default_value: nil
|
1144
1315
|
|
1145
|
-
if `uname` =~ /
|
1316
|
+
if `uname` =~ /Darwin/
|
1146
1317
|
c.desc 'Application to use'
|
1318
|
+
c.arg_name 'APP_NAME'
|
1147
1319
|
c.flag [:a]
|
1148
1320
|
|
1149
|
-
c.desc "Use the editor_app defined in ~/.doingrc (#{wwid.config['editor_app']})"
|
1150
|
-
c.switch [:x]
|
1151
|
-
|
1152
1321
|
c.desc 'Application bundle id to use'
|
1322
|
+
c.arg_name 'BUNDLE_ID'
|
1153
1323
|
c.flag [:b]
|
1324
|
+
|
1325
|
+
c.desc "Use the config_editor_app defined in ~/.doingrc (#{wwid.config.key?('config_editor_app') ? wwid.config['config_editor_app'] : 'config_editor_app not set'})"
|
1326
|
+
c.switch [:x]
|
1154
1327
|
end
|
1155
|
-
|
1156
|
-
|
1328
|
+
|
1329
|
+
c.action do |_global_options, options, _args|
|
1330
|
+
if `uname` =~ /Darwin/
|
1157
1331
|
if options[:x]
|
1158
|
-
|
1332
|
+
`open -a "#{wwid.config['config_editor_app']}" "#{wwid.config_file}"`
|
1159
1333
|
elsif options[:a] || options[:b]
|
1160
1334
|
if options[:a]
|
1161
|
-
|
1335
|
+
`open -a "#{options[:a]}" "#{wwid.config_file}"`
|
1162
1336
|
elsif options[:b]
|
1163
|
-
|
1337
|
+
`open -b #{options[:b]} "#{wwid.config_file}"`
|
1164
1338
|
end
|
1165
1339
|
else
|
1166
|
-
raise
|
1340
|
+
raise 'No EDITOR variable defined in environment' if options[:e].nil? && ENV['EDITOR'].nil?
|
1341
|
+
|
1167
1342
|
editor = options[:e].nil? ? ENV['EDITOR'] : options[:e]
|
1168
|
-
system %
|
1343
|
+
system %(#{editor} "#{wwid.config_file}")
|
1169
1344
|
end
|
1170
1345
|
else
|
1171
|
-
raise
|
1346
|
+
raise 'No EDITOR variable defined in environment' if options[:e].nil? && ENV['EDITOR'].nil?
|
1347
|
+
|
1172
1348
|
editor = options[:e].nil? ? ENV['EDITOR'] : options[:e]
|
1173
|
-
system %
|
1349
|
+
system %(#{editor} "#{wwid.config_file}")
|
1174
1350
|
end
|
1175
1351
|
end
|
1176
1352
|
end
|
@@ -1178,19 +1354,19 @@ end
|
|
1178
1354
|
desc 'Undo the last change to the doing_file'
|
1179
1355
|
command :undo do |c|
|
1180
1356
|
c.desc 'Specify alternate doing file'
|
1181
|
-
c.
|
1357
|
+
c.arg_name 'PATH'
|
1358
|
+
c.flag %i[f file], default_value: wwid.doing_file
|
1182
1359
|
|
1183
|
-
c.action do |
|
1360
|
+
c.action do |_global_options, options, _args|
|
1184
1361
|
file = options[:f] || wwid.doing_file
|
1185
1362
|
wwid.restore_backup(file)
|
1186
1363
|
end
|
1187
1364
|
end
|
1188
1365
|
|
1189
|
-
|
1190
|
-
|
1191
|
-
if global[:config_file]
|
1366
|
+
pre do |global, _command, _options, _args|
|
1367
|
+
if global[:config_file] && global[:config_file] != wwid.config_file
|
1192
1368
|
wwid.config_file = global[:config_file]
|
1193
|
-
wwid.configure({:
|
1369
|
+
wwid.configure({ ignore_local: true })
|
1194
1370
|
# wwid.results.push("Override config file #{wwid.config_file}")
|
1195
1371
|
end
|
1196
1372
|
|
@@ -1204,9 +1380,7 @@ pre do |global,command,options,args|
|
|
1204
1380
|
|
1205
1381
|
wwid.config[:include_notes] = false unless global[:notes]
|
1206
1382
|
|
1207
|
-
if global[:version]
|
1208
|
-
$stdout.puts "doing v" + Doing::VERSION
|
1209
|
-
end
|
1383
|
+
$stdout.puts "doing v#{Doing::VERSION}" if global[:version]
|
1210
1384
|
|
1211
1385
|
# Return true to proceed; false to abort and not call the
|
1212
1386
|
# chosen command
|
@@ -1215,7 +1389,7 @@ pre do |global,command,options,args|
|
|
1215
1389
|
true
|
1216
1390
|
end
|
1217
1391
|
|
1218
|
-
post do |global,
|
1392
|
+
post do |global, _command, _options, _args|
|
1219
1393
|
# Use skips_post before a command to skip this
|
1220
1394
|
# block on that command only
|
1221
1395
|
if global[:stdout]
|
@@ -1225,7 +1399,7 @@ post do |global,command,options,args|
|
|
1225
1399
|
end
|
1226
1400
|
end
|
1227
1401
|
|
1228
|
-
on_error do |
|
1402
|
+
on_error do |_exception|
|
1229
1403
|
# puts exception.message
|
1230
1404
|
true
|
1231
1405
|
end
|