doing 2.1.25 → 2.1.29
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/.yardoc/checksums +15 -20
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/CHANGELOG.md +322 -108
- data/Dockerfile +5 -5
- data/Dockerfile-2.6 +5 -5
- data/Dockerfile-2.7 +5 -4
- data/Dockerfile-3.0 +5 -4
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/Rakefile +2 -3
- data/bin/commands/add_section.rb +15 -0
- data/bin/commands/again.rb +57 -0
- data/bin/commands/archive.rb +55 -0
- data/bin/commands/cancel.rb +60 -0
- data/bin/commands/changes.rb +83 -0
- data/bin/commands/choose.rb +9 -0
- data/bin/commands/colors.rb +21 -0
- data/bin/commands/commands.rb +89 -0
- data/bin/commands/commands_accepting.rb +76 -0
- data/bin/commands/completion.rb +27 -0
- data/bin/commands/config.rb +245 -0
- data/bin/commands/done.rb +235 -0
- data/bin/commands/finish.rb +126 -0
- data/bin/commands/flag.rb +90 -0
- data/bin/commands/grep.rb +108 -0
- data/bin/commands/import.rb +71 -0
- data/bin/commands/install_fzf.rb +17 -0
- data/bin/commands/last.rb +81 -0
- data/bin/commands/meanwhile.rb +76 -0
- data/bin/commands/note.rb +91 -0
- data/bin/commands/now.rb +145 -0
- data/bin/commands/on.rb +65 -0
- data/bin/commands/open.rb +53 -0
- data/bin/commands/plugins.rb +23 -0
- data/bin/commands/recent.rb +77 -0
- data/bin/commands/redo.rb +26 -0
- data/bin/commands/reset.rb +73 -0
- data/bin/commands/rotate.rb +42 -0
- data/bin/commands/sections.rb +11 -0
- data/bin/commands/select.rb +105 -0
- data/bin/commands/show.rb +185 -0
- data/bin/commands/since.rb +63 -0
- data/bin/commands/tag.rb +149 -0
- data/bin/commands/tag_dir.rb +29 -0
- data/bin/commands/tags.rb +66 -0
- data/bin/commands/template.rb +61 -0
- data/bin/commands/today.rb +64 -0
- data/bin/commands/undo.rb +49 -0
- data/bin/commands/view.rb +201 -0
- data/bin/commands/views.rb +11 -0
- data/bin/commands/yesterday.rb +72 -0
- data/bin/doing +241 -3706
- data/docs/doc/Array.html +3 -502
- data/docs/doc/BooleanTermParser/Clause.html +1 -1
- data/docs/doc/BooleanTermParser/Operator.html +1 -1
- data/docs/doc/BooleanTermParser/Query.html +1 -1
- data/docs/doc/BooleanTermParser/QueryParser.html +1 -1
- data/docs/doc/BooleanTermParser/QueryTransformer.html +1 -1
- data/docs/doc/BooleanTermParser.html +1 -1
- data/docs/doc/Doing/Color.html +62 -56
- data/docs/doc/Doing/Completion.html +1 -1
- data/docs/doc/Doing/Configuration.html +36 -1
- data/docs/doc/Doing/Errors/DoingNoTraceError.html +1 -1
- data/docs/doc/Doing/Errors/DoingRuntimeError.html +1 -1
- data/docs/doc/Doing/Errors/DoingStandardError.html +1 -1
- data/docs/doc/Doing/Errors/EmptyInput.html +1 -1
- data/docs/doc/Doing/Errors/NoResults.html +1 -1
- data/docs/doc/Doing/Errors/PluginException.html +1 -1
- data/docs/doc/Doing/Errors/UserCancelled.html +1 -1
- data/docs/doc/Doing/Errors/WrongCommand.html +1 -1
- data/docs/doc/Doing/Errors.html +1 -1
- data/docs/doc/Doing/Hooks.html +1 -1
- data/docs/doc/Doing/Item.html +1 -1
- data/docs/doc/Doing/Items.html +2 -2
- data/docs/doc/Doing/LogAdapter.html +1 -1
- data/docs/doc/Doing/Note.html +2 -2
- data/docs/doc/Doing/Pager.html +1 -1
- data/docs/doc/Doing/Plugins.html +1 -1
- data/docs/doc/Doing/Prompt.html +46 -1
- data/docs/doc/Doing/Section.html +1 -1
- data/docs/doc/Doing/TemplateString.html +2 -2
- data/docs/doc/Doing/Types.html +41 -1
- data/docs/doc/Doing/Util/Backup.html +1 -1
- data/docs/doc/Doing/Util.html +1 -1
- data/docs/doc/Doing/WWID.html +10 -10
- data/docs/doc/Doing.html +3 -3
- data/docs/doc/FalseClass.html +235 -0
- data/docs/doc/GLI/Commands/Help.html +1 -1
- data/docs/doc/GLI/Commands/MarkdownDocumentListener.html +1 -1
- data/docs/doc/GLI/Commands.html +1 -1
- data/docs/doc/GLI.html +1 -1
- data/docs/doc/Hash.html +1 -1
- data/docs/doc/Numeric.html +1 -1
- data/docs/doc/Object.html +203 -0
- data/docs/doc/PhraseParser/Operator.html +1 -1
- data/docs/doc/PhraseParser/PhraseClause.html +1 -1
- data/docs/doc/PhraseParser/Query.html +1 -1
- data/docs/doc/PhraseParser/QueryParser.html +1 -1
- data/docs/doc/PhraseParser/QueryTransformer.html +1 -1
- data/docs/doc/PhraseParser/TermClause.html +1 -1
- data/docs/doc/PhraseParser.html +1 -1
- data/docs/doc/Status.html +1 -1
- data/docs/doc/String.html +287 -3155
- data/docs/doc/Symbol.html +40 -6
- data/docs/doc/Time.html +1 -1
- data/docs/doc/TrueClass.html +235 -0
- data/docs/doc/_index.html +5 -10
- data/docs/doc/class_list.html +1 -1
- data/docs/doc/file.README.html +2 -2
- data/docs/doc/index.html +2 -2
- data/docs/doc/method_list.html +289 -681
- data/docs/doc/top-level-namespace.html +2 -2
- data/doing.rdoc +306 -205
- data/lib/completion/_doing.zsh +35 -35
- data/lib/completion/doing.bash +30 -30
- data/lib/completion/doing.fish +88 -78
- data/lib/doing/array/array.rb +4 -0
- data/lib/doing/array/nested_hash.rb +17 -0
- data/lib/doing/{array.rb → array/tags.rb} +7 -25
- data/lib/doing/changelog/change.rb +26 -11
- data/lib/doing/changelog/changes.rb +31 -4
- data/lib/doing/{array_chronify.rb → chronify/array.rb} +0 -0
- data/lib/doing/chronify/chronify.rb +5 -0
- data/lib/doing/{numeric_chronify.rb → chronify/numeric.rb} +0 -0
- data/lib/doing/{string_chronify.rb → chronify/string.rb} +0 -0
- data/lib/doing/colors.rb +115 -54
- data/lib/doing/configuration.rb +5 -0
- data/lib/doing/good.rb +8 -0
- data/lib/doing/help_monkey_patch.rb +6 -5
- data/lib/doing/item.rb +5 -5
- data/lib/doing/items.rb +2 -2
- data/lib/doing/log_adapter.rb +35 -2
- data/lib/doing/normalize.rb +188 -0
- data/lib/doing/pager.rb +1 -0
- data/lib/doing/plugins/export/dayone_export.rb +1 -1
- data/lib/doing/plugins/export/html_export.rb +1 -1
- data/lib/doing/plugins/export/json_export.rb +1 -1
- data/lib/doing/plugins/export/markdown_export.rb +1 -1
- data/lib/doing/plugins/export/template_export.rb +3 -1
- data/lib/doing/prompt.rb +9 -3
- data/lib/doing/string/highlight.rb +95 -0
- data/lib/doing/string/query.rb +129 -0
- data/lib/doing/string/string.rb +12 -0
- data/lib/doing/string/tags.rb +164 -0
- data/lib/doing/string/transform.rb +168 -0
- data/lib/doing/string/truncate.rb +75 -0
- data/lib/doing/string/url.rb +82 -0
- data/lib/doing/template_string.rb +0 -22
- data/lib/doing/types.rb +8 -0
- data/lib/doing/util.rb +13 -9
- data/lib/doing/version.rb +1 -1
- data/lib/doing/wwid.rb +53 -35
- data/lib/doing.rb +4 -6
- data/lib/examples/commands/wiki.rb +6 -7
- data/lib/examples/plugins/wiki_export/wiki_export.rb +1 -1
- data/lib/helpers/threaded_tests.rb +39 -20
- data/scripts/deploy.rb +107 -0
- data/scripts/runtests.sh +4 -0
- metadata +63 -8
- data/lib/doing/string.rb +0 -765
- data/lib/doing/symbol.rb +0 -28
data/bin/commands/tag.rb
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# @@tag
|
|
2
|
+
desc 'Add tag(s) to last entry'
|
|
3
|
+
long_desc 'Add (or remove) tags from the last entry, or from multiple entries
|
|
4
|
+
(with `--count`), entries matching a search (with `--search`), or entries
|
|
5
|
+
containing another tag (with `--tag`).
|
|
6
|
+
|
|
7
|
+
When removing tags with `-r`, wildcards are allowed (`*` to match
|
|
8
|
+
multiple characters, `?` to match a single character). With `--regex`,
|
|
9
|
+
regular expressions will be interpreted instead of wildcards.
|
|
10
|
+
|
|
11
|
+
For all tag removals the match is case insensitive by default, but if
|
|
12
|
+
the tag search string contains any uppercase letters, the match will
|
|
13
|
+
become case sensitive automatically.
|
|
14
|
+
|
|
15
|
+
Tag name arguments do not need to be prefixed with @.'
|
|
16
|
+
arg_name 'TAG', :multiple
|
|
17
|
+
command :tag do |c|
|
|
18
|
+
c.example 'doing tag mytag', desc: 'Add @mytag to the last entry created'
|
|
19
|
+
c.example 'doing tag --remove mytag', desc: 'Remove @mytag from the last entry created'
|
|
20
|
+
c.example 'doing tag --rename "other*" --count 10 newtag', desc: 'Rename tags beginning with "other" (wildcard) to @newtag on the last 10 entries'
|
|
21
|
+
c.example 'doing tag --search "developing" coding', desc: 'Add @coding to the last entry containing string "developing" (fuzzy matching)'
|
|
22
|
+
c.example 'doing tag --interactive --tag project1 coding', desc: 'Create an interactive menu from entries tagged @project1, selection(s) will be tagged with @coding'
|
|
23
|
+
|
|
24
|
+
c.desc 'Section'
|
|
25
|
+
c.arg_name 'SECTION_NAME'
|
|
26
|
+
c.flag %i[s section], default_value: 'All'
|
|
27
|
+
|
|
28
|
+
c.desc 'How many recent entries to tag (0 for all)'
|
|
29
|
+
c.arg_name 'COUNT'
|
|
30
|
+
c.flag %i[c count], default_value: 1, must_match: /^\d+$/, type: Integer
|
|
31
|
+
|
|
32
|
+
c.desc 'Replace existing tag with tag argument, wildcards (*,?) allowed, or use with --regex'
|
|
33
|
+
c.arg_name 'ORIG_TAG'
|
|
34
|
+
c.flag %i[rename]
|
|
35
|
+
|
|
36
|
+
c.desc 'Include a value, e.g. @tag(value)'
|
|
37
|
+
c.arg_name 'VALUE'
|
|
38
|
+
c.flag %i[v value]
|
|
39
|
+
|
|
40
|
+
c.desc 'Don\'t ask permission to tag all entries when count is 0'
|
|
41
|
+
c.switch %i[force], negatable: false, default_value: false
|
|
42
|
+
|
|
43
|
+
c.desc 'Include current date/time with tag'
|
|
44
|
+
c.switch %i[d date], negatable: false, default_value: false
|
|
45
|
+
|
|
46
|
+
c.desc 'Remove given tag(s)'
|
|
47
|
+
c.switch %i[r remove], negatable: false, default_value: false
|
|
48
|
+
|
|
49
|
+
c.desc 'Interpret tag string as regular expression (with --remove)'
|
|
50
|
+
c.switch %i[regex], negatable: false, default_value: false
|
|
51
|
+
|
|
52
|
+
c.desc 'Tag last entry (or entries) not marked @done'
|
|
53
|
+
c.switch %i[u unfinished], negatable: false, default_value: false
|
|
54
|
+
|
|
55
|
+
c.desc 'Autotag entries based on autotag configuration in ~/.config/doing/config.yml'
|
|
56
|
+
c.switch %i[a autotag], negatable: false, default_value: false
|
|
57
|
+
|
|
58
|
+
c.desc 'Select item(s) to tag from a menu of matching entries'
|
|
59
|
+
c.switch %i[i interactive], negatable: false, default_value: false
|
|
60
|
+
|
|
61
|
+
add_options(:search, c)
|
|
62
|
+
add_options(:tag_filter, c)
|
|
63
|
+
|
|
64
|
+
c.action do |_global_options, options, args|
|
|
65
|
+
options[:fuzzy] = false
|
|
66
|
+
# raise MissingArgument, 'You must specify at least one tag' if args.empty? && !options[:autotag]
|
|
67
|
+
|
|
68
|
+
raise InvalidArgument, '--search and --tag can not be used together' if options[:search] && options[:tag]
|
|
69
|
+
|
|
70
|
+
section = 'All'
|
|
71
|
+
|
|
72
|
+
if options[:section]
|
|
73
|
+
section = @wwid.guess_section(options[:section]) || options[:section].cap_first
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
if options[:tag].nil?
|
|
78
|
+
search_tags = []
|
|
79
|
+
else
|
|
80
|
+
search_tags = options[:tag]
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
if options[:autotag]
|
|
84
|
+
tags = []
|
|
85
|
+
else
|
|
86
|
+
if args.empty?
|
|
87
|
+
tags = []
|
|
88
|
+
else
|
|
89
|
+
tags = if args.join('') =~ /,/
|
|
90
|
+
args.join('').split(/ *, */)
|
|
91
|
+
else
|
|
92
|
+
args.join(' ').split(' ') # in case tags are quoted as one arg
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
tags.map! { |tag| tag.sub(/^@/, '').strip }
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
if options[:interactive]
|
|
100
|
+
count = 0
|
|
101
|
+
options[:force] = true
|
|
102
|
+
else
|
|
103
|
+
count = options[:count].to_i
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
if options[:search]
|
|
107
|
+
search = options[:search]
|
|
108
|
+
search.sub!(/^'?/, "'") if options[:exact]
|
|
109
|
+
options[:search] = search
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
options[:count] = count
|
|
113
|
+
options[:section] = section
|
|
114
|
+
options[:tag] = search_tags
|
|
115
|
+
options[:tags] = tags
|
|
116
|
+
options[:tag_bool] = options[:bool]
|
|
117
|
+
|
|
118
|
+
if count.zero? && !options[:force]
|
|
119
|
+
matches = @wwid.filter_items([], opt: options).count
|
|
120
|
+
|
|
121
|
+
if matches > 5
|
|
122
|
+
if options[:search]
|
|
123
|
+
section_q = ' matching your search terms'
|
|
124
|
+
elsif options[:tag]
|
|
125
|
+
section_q = ' matching your tag search'
|
|
126
|
+
elsif section == 'All'
|
|
127
|
+
section_q = ''
|
|
128
|
+
else
|
|
129
|
+
section_q = " in section #{section}"
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
question = if options[:autotag]
|
|
134
|
+
"Are you sure you want to autotag #{matches} records#{section_q}"
|
|
135
|
+
elsif options[:remove]
|
|
136
|
+
"Are you sure you want to remove #{tags.join(' and ')} from #{matches} records#{section_q}"
|
|
137
|
+
else
|
|
138
|
+
"Are you sure you want to add #{tags.join(' and ')} to #{matches} records#{section_q}"
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
res = Doing::Prompt.yn(question, default_response: false)
|
|
142
|
+
|
|
143
|
+
raise UserCancelled unless res
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
@wwid.tag_last(options)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# @@tag_dir
|
|
2
|
+
desc 'Set the default tags for the current directory'
|
|
3
|
+
long_desc 'Adds default_tags to a .doingrc file in the current directory. Any entry created in this directory or its
|
|
4
|
+
subdirectories will be tagged with the default tags. You can modify these any time using the `config set` commnand or
|
|
5
|
+
manually editing the .doingrc file.'
|
|
6
|
+
arg_name 'TAG [TAG..]'
|
|
7
|
+
command :tag_dir do |c|
|
|
8
|
+
c.example 'doing tag_dir project1 project2', desc: 'Add @project1 and @project to to any entries in the current directory'
|
|
9
|
+
c.example 'doing tag_dir --remove', desc: 'Clear the default tags for the directory'
|
|
10
|
+
|
|
11
|
+
c.desc 'Remove all default_tags from the local .doingrc'
|
|
12
|
+
c.switch %i[r remove], negatable: false
|
|
13
|
+
|
|
14
|
+
c.action do |global, options, args|
|
|
15
|
+
tags = args.join(' ').gsub(/ *, */, ' ').split(' ')
|
|
16
|
+
|
|
17
|
+
cfg_cmd = commands[:config]
|
|
18
|
+
set_cmd = cfg_cmd.commands[:set]
|
|
19
|
+
set_options = {}
|
|
20
|
+
if options[:remove]
|
|
21
|
+
set_args = ['default_tags']
|
|
22
|
+
set_options[:remove] = true
|
|
23
|
+
else
|
|
24
|
+
set_args = ['default_tags', tags.join(',')]
|
|
25
|
+
end
|
|
26
|
+
action = set_cmd.send(:get_action, nil)
|
|
27
|
+
return action.call(global, set_options, set_args)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# @@tags
|
|
2
|
+
desc 'List all tags in the current Doing file'
|
|
3
|
+
arg_name 'MAX_COUNT', optional: true, type: Integer
|
|
4
|
+
command :tags do |c|
|
|
5
|
+
c.desc 'Section'
|
|
6
|
+
c.arg_name 'SECTION_NAME'
|
|
7
|
+
c.flag %i[s section], default_value: 'All'
|
|
8
|
+
|
|
9
|
+
c.desc 'Show count of occurrences'
|
|
10
|
+
c.switch %i[c counts]
|
|
11
|
+
|
|
12
|
+
c.desc 'Output in a single line with @ symbols. Ignored if --counts is specified.'
|
|
13
|
+
c.switch %i[l line]
|
|
14
|
+
|
|
15
|
+
c.desc 'Sort by name or count'
|
|
16
|
+
c.arg_name 'SORT_ORDER'
|
|
17
|
+
c.flag %i[sort], default_value: 'name', must_match: /^(?:n(?:ame)?|c(?:ount)?)$/
|
|
18
|
+
|
|
19
|
+
c.desc 'Sort order (asc/desc)'
|
|
20
|
+
c.arg_name 'ORDER'
|
|
21
|
+
c.flag %i[o order], must_match: REGEX_SORT_ORDER, default_value: :asc, type: OrderSymbol
|
|
22
|
+
|
|
23
|
+
c.desc 'Select items to scan from a menu of matching entries'
|
|
24
|
+
c.switch %i[i interactive], negatable: false, default_value: false
|
|
25
|
+
|
|
26
|
+
add_options(:search, c)
|
|
27
|
+
add_options(:tag_filter, c)
|
|
28
|
+
|
|
29
|
+
c.action do |_global, options, args|
|
|
30
|
+
section = @wwid.guess_section(options[:section]) || options[:section].cap_first
|
|
31
|
+
options[:count] = args.count.positive? ? args[0].to_i : 0
|
|
32
|
+
|
|
33
|
+
items = @wwid.filter_items([], opt: options)
|
|
34
|
+
|
|
35
|
+
if options[:interactive]
|
|
36
|
+
items = Doing::Prompt.choose_from_items(items, include_section: options[:section].nil?,
|
|
37
|
+
menu: true,
|
|
38
|
+
header: '',
|
|
39
|
+
prompt: 'Select entries to scan > ',
|
|
40
|
+
multiple: true,
|
|
41
|
+
sort: true,
|
|
42
|
+
show_if_single: true)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# items = @wwid.content.in_section(section)
|
|
46
|
+
tags = @wwid.all_tags(items, counts: true)
|
|
47
|
+
|
|
48
|
+
if options[:sort] =~ /^n/i
|
|
49
|
+
tags = tags.sort_by { |tag, count| tag }
|
|
50
|
+
else
|
|
51
|
+
tags = tags.sort_by { |tag, count| count }
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
tags.reverse! if options[:order] == :desc
|
|
55
|
+
|
|
56
|
+
if options[:counts]
|
|
57
|
+
tags.each { |t, c| puts "#{t} (#{c})" }
|
|
58
|
+
else
|
|
59
|
+
if options[:line]
|
|
60
|
+
puts tags.map { |t, c| t }.to_tags.join(' ')
|
|
61
|
+
else
|
|
62
|
+
tags.each { |t, c| puts "#{t}" }
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# @@template
|
|
2
|
+
desc 'Output HTML, CSS, and Markdown (ERB) templates for customization'
|
|
3
|
+
long_desc %(
|
|
4
|
+
Templates are printed to STDOUT for piping to a file.
|
|
5
|
+
Save them and use them in the configuration file under export_templates.
|
|
6
|
+
)
|
|
7
|
+
arg_name 'TYPE', must_match: Doing::Plugins.template_regex
|
|
8
|
+
command :template do |c|
|
|
9
|
+
c.example 'doing template haml > ~/styles/my_doing.haml', desc: 'Output the haml template and save it to a file'
|
|
10
|
+
|
|
11
|
+
c.desc 'List all available templates'
|
|
12
|
+
c.switch %i[l list], negatable: false
|
|
13
|
+
|
|
14
|
+
c.desc 'List in single column for completion'
|
|
15
|
+
c.switch %i[c column]
|
|
16
|
+
|
|
17
|
+
c.desc 'Save template to file instead of STDOUT'
|
|
18
|
+
c.switch %i[s save], default_value: false, negatable: false
|
|
19
|
+
|
|
20
|
+
c.desc 'Save template to alternate location'
|
|
21
|
+
c.arg_name 'DIRECTORY'
|
|
22
|
+
c.flag %i[p path], default_value: File.join(Doing::Util.user_home, '.config', 'doing', 'templates')
|
|
23
|
+
|
|
24
|
+
c.action do |_global_options, options, args|
|
|
25
|
+
if options[:list] || options[:column]
|
|
26
|
+
if options[:column]
|
|
27
|
+
$stdout.print Doing::Plugins.plugin_templates.join("\n")
|
|
28
|
+
else
|
|
29
|
+
$stdout.puts "Available templates: #{Doing::Plugins.plugin_templates.join(', ')}"
|
|
30
|
+
end
|
|
31
|
+
return
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
if args.empty?
|
|
35
|
+
type = Doing::Prompt.choose_from(Doing::Plugins.plugin_templates, sorted: false, prompt: 'Select template type > ')
|
|
36
|
+
type.sub!(/ \(.*?\)$/, '').strip!
|
|
37
|
+
options[:save] = Doing::Prompt.yn("Save to #{options[:path]}? (No outputs to STDOUT)", default_response: false)
|
|
38
|
+
else
|
|
39
|
+
type = args[0]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
raise InvalidPluginType, "No type specified, use `doing template [#{Doing::Plugins.plugin_templates.join('|')}]`" unless type
|
|
43
|
+
|
|
44
|
+
if options[:save]
|
|
45
|
+
Doing::Plugins.template_for_trigger(type, save_to: options[:path])
|
|
46
|
+
else
|
|
47
|
+
$stdout.puts Doing::Plugins.template_for_trigger(type, save_to: nil)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# case args[0]
|
|
51
|
+
# when /html|haml/i
|
|
52
|
+
# $stdout.puts @wwid.haml_template
|
|
53
|
+
# when /css/i
|
|
54
|
+
# $stdout.puts @wwid.css_template
|
|
55
|
+
# when /markdown|md|erb/i
|
|
56
|
+
# $stdout.puts @wwid.markdown_template
|
|
57
|
+
# else
|
|
58
|
+
# exit_now! 'Invalid type specified, must be HAML or CSS'
|
|
59
|
+
# end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# @@today
|
|
2
|
+
desc 'List entries from today'
|
|
3
|
+
long_desc 'List entries from the current day. Use --before, --after, and
|
|
4
|
+
--from to specify time ranges.'
|
|
5
|
+
command :today do |c|
|
|
6
|
+
c.example 'doing today', desc: 'List all entries with start dates between 12am and 11:59PM for the current day'
|
|
7
|
+
c.example 'doing today --section Later', desc: 'List today\'s entries in the Later section'
|
|
8
|
+
c.example 'doing today --before 3pm --after 12pm', desc: 'List entries with start dates between 12pm and 3pm today'
|
|
9
|
+
c.example 'doing today --output json', desc: 'Output entries from today in JSON format'
|
|
10
|
+
|
|
11
|
+
c.desc 'Specify a section'
|
|
12
|
+
c.arg_name 'NAME'
|
|
13
|
+
c.flag %i[s section], default_value: 'All'
|
|
14
|
+
|
|
15
|
+
c.desc 'Show time intervals on @done tasks'
|
|
16
|
+
c.switch %i[t times], default_value: true, negatable: true
|
|
17
|
+
|
|
18
|
+
c.desc 'Show elapsed time on entries without @done tag'
|
|
19
|
+
c.switch [:duration]
|
|
20
|
+
|
|
21
|
+
c.desc 'Show time totals at the end of output'
|
|
22
|
+
c.switch [:totals], default_value: false, negatable: false
|
|
23
|
+
|
|
24
|
+
c.desc 'Sort tags by (name|time)'
|
|
25
|
+
default = @settings['tag_sort'].normalize_tag_sort || :name
|
|
26
|
+
c.arg_name 'KEY'
|
|
27
|
+
c.flag [:tag_sort], must_match: REGEX_TAG_SORT, default_value: default, type: TagSortSymbol
|
|
28
|
+
|
|
29
|
+
c.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})"
|
|
30
|
+
c.arg_name 'FORMAT'
|
|
31
|
+
c.flag %i[o output]
|
|
32
|
+
|
|
33
|
+
c.desc "Output using a template from configuration"
|
|
34
|
+
c.arg_name 'TEMPLATE_KEY'
|
|
35
|
+
c.flag [:config_template], type: TemplateName, default_value: 'today'
|
|
36
|
+
|
|
37
|
+
c.desc 'Override output format with a template string containing %placeholders'
|
|
38
|
+
c.arg_name 'TEMPLATE_STRING'
|
|
39
|
+
c.flag [:template]
|
|
40
|
+
|
|
41
|
+
c.desc 'View entries before specified time (e.g. 8am, 12:30pm, 15:00)'
|
|
42
|
+
c.arg_name 'TIME_STRING'
|
|
43
|
+
c.flag [:before]
|
|
44
|
+
|
|
45
|
+
c.desc 'View entries after specified time (e.g. 8am, 12:30pm, 15:00)'
|
|
46
|
+
c.arg_name 'TIME_STRING'
|
|
47
|
+
c.flag [:after]
|
|
48
|
+
|
|
49
|
+
c.desc %(
|
|
50
|
+
Time range to show `doing today --from "12pm to 4pm"`
|
|
51
|
+
)
|
|
52
|
+
c.arg_name 'TIME_RANGE'
|
|
53
|
+
c.flag [:from], type: DateRangeString
|
|
54
|
+
|
|
55
|
+
c.action do |_global_options, options, _args|
|
|
56
|
+
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}") if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
|
57
|
+
|
|
58
|
+
options[:times] = true if options[:totals]
|
|
59
|
+
options[:sort_tags] = options[:tag_sort]
|
|
60
|
+
filter_options = %i[after before duration from section sort_tags totals template config_template].each_with_object({}) { |k, hsh| hsh[k] = options[k] }
|
|
61
|
+
|
|
62
|
+
Doing::Pager.page @wwid.today(options[:times], options[:output], filter_options).chomp
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# @@undo
|
|
2
|
+
desc 'Undo the last X changes to the Doing file'
|
|
3
|
+
long_desc 'Reverts the last X commands that altered the doing file.
|
|
4
|
+
All changes performed by a single command are undone at once.
|
|
5
|
+
|
|
6
|
+
Specify a number to jump back multiple revisions, or use --select for an interactive menu.'
|
|
7
|
+
arg_name 'COUNT'
|
|
8
|
+
command :undo do |c|
|
|
9
|
+
c.example 'doing undo', desc: 'Undo the most recent change to the doing file'
|
|
10
|
+
c.example 'doing undo 5', desc: 'Undo the last 5 changes to the doing file'
|
|
11
|
+
c.example 'doing undo --interactive', desc: 'Select from a menu of available revisions'
|
|
12
|
+
c.example 'doing undo --redo', desc: 'Undo the last undo command'
|
|
13
|
+
|
|
14
|
+
c.desc 'Specify alternate doing file'
|
|
15
|
+
c.arg_name 'PATH'
|
|
16
|
+
c.flag %i[f file], default_value: @wwid.doing_file
|
|
17
|
+
|
|
18
|
+
c.desc 'Select from recent backups'
|
|
19
|
+
c.switch %i[i interactive], negatable: false
|
|
20
|
+
|
|
21
|
+
c.desc 'Remove old backups, retaining X files'
|
|
22
|
+
c.arg_name 'COUNT'
|
|
23
|
+
c.flag %i[p prune], type: Integer
|
|
24
|
+
|
|
25
|
+
c.desc 'Redo last undo. Note: you cannot undo a redo'
|
|
26
|
+
c.switch %i[r redo]
|
|
27
|
+
|
|
28
|
+
c.action do |_global_options, options, args|
|
|
29
|
+
file = options[:file] || @wwid.doing_file
|
|
30
|
+
count = args.empty? ? 1 : args[0].to_i
|
|
31
|
+
raise InvalidArgument, "Invalid count specified for undo" unless count&.positive?
|
|
32
|
+
|
|
33
|
+
if options[:prune]
|
|
34
|
+
Doing::Util::Backup.prune_backups(file, options[:prune])
|
|
35
|
+
elsif options[:redo]
|
|
36
|
+
if options[:interactive]
|
|
37
|
+
Doing::Util::Backup.select_redo(file)
|
|
38
|
+
else
|
|
39
|
+
Doing::Util::Backup.redo_backup(file, count: count)
|
|
40
|
+
end
|
|
41
|
+
else
|
|
42
|
+
if options[:interactive]
|
|
43
|
+
Doing::Util::Backup.select_backup(file)
|
|
44
|
+
else
|
|
45
|
+
Doing::Util::Backup.restore_last_backup(file, count: count)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# @@view
|
|
2
|
+
desc 'Display a user-created view'
|
|
3
|
+
long_desc 'Views are defined in your configuration (use `doing config` to edit).
|
|
4
|
+
Command line options override view configuration.'
|
|
5
|
+
arg_name 'VIEW_NAME'
|
|
6
|
+
command :view do |c|
|
|
7
|
+
c.example 'doing view color', desc: 'Display entries according to config for view "color"'
|
|
8
|
+
c.example 'doing view color --section Archive --count 10', desc: 'Display view "color", overriding some configured settings'
|
|
9
|
+
|
|
10
|
+
c.desc 'Section'
|
|
11
|
+
c.arg_name 'NAME'
|
|
12
|
+
c.flag %i[s section]
|
|
13
|
+
|
|
14
|
+
c.desc 'Count to display'
|
|
15
|
+
c.arg_name 'COUNT'
|
|
16
|
+
c.flag %i[c count], must_match: /^\d+$/, type: Integer
|
|
17
|
+
|
|
18
|
+
c.desc "Output to export format (#{Doing::Plugins.plugin_names(type: :export)})"
|
|
19
|
+
c.arg_name 'FORMAT'
|
|
20
|
+
c.flag %i[o output]
|
|
21
|
+
|
|
22
|
+
c.desc 'Age (oldest|newest)'
|
|
23
|
+
c.arg_name 'AGE'
|
|
24
|
+
c.flag %i[age], default_value: :newest, type: AgeSymbol
|
|
25
|
+
|
|
26
|
+
c.desc 'Show time intervals on @done tasks'
|
|
27
|
+
c.switch %i[t times], default_value: true, negatable: true
|
|
28
|
+
|
|
29
|
+
c.desc 'Show elapsed time on entries without @done tag'
|
|
30
|
+
c.switch [:duration]
|
|
31
|
+
|
|
32
|
+
c.desc 'Show intervals with totals at the end of output'
|
|
33
|
+
c.switch [:totals], default_value: false, negatable: false
|
|
34
|
+
|
|
35
|
+
c.desc 'Include colors in output'
|
|
36
|
+
c.switch [:color], default_value: true, negatable: true
|
|
37
|
+
|
|
38
|
+
c.desc "Highlight search matches in output. Only affects command line output"
|
|
39
|
+
c.switch %i[h hilite], default_value: @settings.dig('search', 'highlight')
|
|
40
|
+
|
|
41
|
+
c.desc 'Sort tags by (name|time)'
|
|
42
|
+
c.arg_name 'KEY'
|
|
43
|
+
c.flag [:tag_sort], must_match: REGEX_TAG_SORT, type: TagSortSymbol
|
|
44
|
+
|
|
45
|
+
c.desc 'Tag sort direction (asc|desc)'
|
|
46
|
+
c.arg_name 'DIRECTION'
|
|
47
|
+
c.flag [:tag_order], must_match: REGEX_SORT_ORDER, type: OrderSymbol
|
|
48
|
+
|
|
49
|
+
c.desc 'Only show items with recorded time intervals (override view settings)'
|
|
50
|
+
c.switch [:only_timed], default_value: false, negatable: false
|
|
51
|
+
|
|
52
|
+
c.desc 'Select from a menu of matching entries to perform additional operations'
|
|
53
|
+
c.switch %i[i interactive], negatable: false, default_value: false
|
|
54
|
+
|
|
55
|
+
add_options(:search, c)
|
|
56
|
+
add_options(:tag_filter, c)
|
|
57
|
+
add_options(:date_filter, c)
|
|
58
|
+
|
|
59
|
+
c.action do |global_options, options, args|
|
|
60
|
+
options[:fuzzy] = false
|
|
61
|
+
if options[:output] && options[:output] !~ Doing::Plugins.plugin_regex(type: :export)
|
|
62
|
+
raise DoingRuntimeError, %(Invalid output type "#{options[:output]}")
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
raise InvalidArgument, '--tag and --search can not be used together' if options[:tag] && options[:search]
|
|
67
|
+
|
|
68
|
+
title = if args.empty?
|
|
69
|
+
@wwid.choose_view
|
|
70
|
+
else
|
|
71
|
+
begin
|
|
72
|
+
@wwid.guess_view(args[0])
|
|
73
|
+
rescue WrongCommand
|
|
74
|
+
cmd = commands[:show]
|
|
75
|
+
options[:sort] = :asc
|
|
76
|
+
options[:tag_order] = :asc
|
|
77
|
+
action = cmd.send(:get_action, nil)
|
|
78
|
+
return action.call(global_options, options, args)
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
section = if options[:section]
|
|
83
|
+
@wwid.guess_section(options[:section]) || options[:section].cap_first
|
|
84
|
+
else
|
|
85
|
+
@settings['current_section']
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
view = @wwid.get_view(title)
|
|
89
|
+
|
|
90
|
+
if view
|
|
91
|
+
page_title = view['title'] || title.cap_first
|
|
92
|
+
only_timed = if (view.key?('only_timed') && view['only_timed']) || options[:only_timed]
|
|
93
|
+
true
|
|
94
|
+
else
|
|
95
|
+
false
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
template = view['template'] || nil
|
|
99
|
+
date_format = view['date_format'] || nil
|
|
100
|
+
|
|
101
|
+
tags_color = view['tags_color'] || nil
|
|
102
|
+
tag_filter = false
|
|
103
|
+
if options[:tag]
|
|
104
|
+
tag_filter = { 'tags' => [], 'bool' => 'OR' }
|
|
105
|
+
bool = options[:bool]
|
|
106
|
+
tag_filter['bool'] = bool
|
|
107
|
+
tag_filter['tags'] = if bool == :pattern
|
|
108
|
+
options[:tag]
|
|
109
|
+
else
|
|
110
|
+
options[:tag].gsub(/[, ]+/, ' ').split(' ').map(&:strip)
|
|
111
|
+
end
|
|
112
|
+
elsif view.key?('tags') && view['tags'].good?
|
|
113
|
+
tag_filter = { 'tags' => [], 'bool' => 'OR' }
|
|
114
|
+
bool = view.key?('tags_bool') && !view['tags_bool'].nil? ? view['tags_bool'].normalize_bool : :pattern
|
|
115
|
+
tag_filter['bool'] = bool
|
|
116
|
+
tag_filter['tags'] = if view['tags'].instance_of?(Array)
|
|
117
|
+
bool == :pattern ? view['tags'].join(' ').strip : view['tags'].map(&:strip)
|
|
118
|
+
else
|
|
119
|
+
bool == :pattern ? view['tags'].strip : view['tags'].gsub(/[, ]+/, ' ').split(' ').map(&:strip)
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# If the -o/--output flag was specified, override any default in the view template
|
|
124
|
+
options[:output] ||= view.key?('output_format') ? view['output_format'] : 'template'
|
|
125
|
+
|
|
126
|
+
count = if options[:count]
|
|
127
|
+
options[:count]
|
|
128
|
+
elsif view.key?('count')
|
|
129
|
+
view['count']
|
|
130
|
+
else
|
|
131
|
+
10
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
section = if options[:section]
|
|
135
|
+
section
|
|
136
|
+
else
|
|
137
|
+
view['section'] || @settings['current_section']
|
|
138
|
+
end
|
|
139
|
+
order = if view.key?('order')
|
|
140
|
+
view['order'].normalize_order
|
|
141
|
+
else
|
|
142
|
+
:asc
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
totals = if options[:totals]
|
|
146
|
+
true
|
|
147
|
+
else
|
|
148
|
+
view['totals'] || false
|
|
149
|
+
end
|
|
150
|
+
tag_order = options[:tag_order] || view['tag_order']&.normalize_order || :asc
|
|
151
|
+
|
|
152
|
+
options[:times] = true if totals
|
|
153
|
+
output_format = options[:output]&.downcase || 'template'
|
|
154
|
+
|
|
155
|
+
options[:sort_tags] = if options[:tag_sort]
|
|
156
|
+
options[:tag_sort]
|
|
157
|
+
elsif view.key?('tag_sort')
|
|
158
|
+
view['tag_sort'].normalize_tag_sort
|
|
159
|
+
else
|
|
160
|
+
false
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
%w[before after from duration].each { |k| options[k.to_sym] = view[k] if view.key?(k) && !options[k.to_sym] }
|
|
164
|
+
|
|
165
|
+
search = nil
|
|
166
|
+
|
|
167
|
+
if options[:search]
|
|
168
|
+
search = options[:search]
|
|
169
|
+
search.sub!(/^'?/, "'") if options[:exact]
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
options[:age] ||= :newest
|
|
173
|
+
|
|
174
|
+
opts = options.clone
|
|
175
|
+
opts[:age] = options[:age]
|
|
176
|
+
opts[:count] = count
|
|
177
|
+
opts[:format] = date_format
|
|
178
|
+
opts[:highlight] = options[:color]
|
|
179
|
+
opts[:hilite] = options[:hilite]
|
|
180
|
+
opts[:only_timed] = only_timed
|
|
181
|
+
opts[:order] = order
|
|
182
|
+
opts[:output] = options[:interactive] ? nil : options[:output]
|
|
183
|
+
opts[:output] = output_format
|
|
184
|
+
opts[:page_title] = page_title
|
|
185
|
+
opts[:search] = search
|
|
186
|
+
opts[:section] = section
|
|
187
|
+
opts[:tag_filter] = tag_filter
|
|
188
|
+
opts[:tag_order] = tag_order
|
|
189
|
+
opts[:tags_color] = tags_color
|
|
190
|
+
opts[:template] = template
|
|
191
|
+
opts[:totals] = totals
|
|
192
|
+
opts[:view_template] = title
|
|
193
|
+
|
|
194
|
+
Doing::Pager.page @wwid.list_section(opts)
|
|
195
|
+
elsif title.instance_of?(FalseClass)
|
|
196
|
+
raise UserCancelled, 'Cancelled'
|
|
197
|
+
else
|
|
198
|
+
raise InvalidView, "View #{title} not found in config"
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# @@views
|
|
2
|
+
desc 'List available custom views'
|
|
3
|
+
command :views do |c|
|
|
4
|
+
c.desc 'List in single column'
|
|
5
|
+
c.switch %i[c column], default_value: false
|
|
6
|
+
|
|
7
|
+
c.action do |_global_options, options, _args|
|
|
8
|
+
joiner = options[:column] ? "\n" : "\t"
|
|
9
|
+
print @wwid.views.join(joiner)
|
|
10
|
+
end
|
|
11
|
+
end
|