doing 2.1.25 → 2.1.26
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardoc/checksums +4 -4
- data/.yardoc/object_types +0 -0
- data/.yardoc/objects/root.dat +0 -0
- data/CHANGELOG.md +283 -108
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/bin/commands/add_section.rb +13 -0
- data/bin/commands/again.rb +99 -0
- data/bin/commands/archive.rb +96 -0
- data/bin/commands/cancel.rb +102 -0
- data/bin/commands/changes.rb +42 -0
- data/bin/commands/choose.rb +9 -0
- data/bin/commands/colors.rb +19 -0
- data/bin/commands/commands.rb +87 -0
- data/bin/commands/commands_accepting.rb +25 -0
- data/bin/commands/completion.rb +24 -0
- data/bin/commands/config.rb +245 -0
- data/bin/commands/done.rb +249 -0
- data/bin/commands/finish.rb +149 -0
- data/bin/commands/flag.rb +126 -0
- data/bin/commands/grep.rb +124 -0
- data/bin/commands/import.rb +101 -0
- data/bin/commands/install_fzf.rb +17 -0
- data/bin/commands/last.rb +114 -0
- data/bin/commands/meanwhile.rb +86 -0
- data/bin/commands/note.rb +130 -0
- data/bin/commands/now.rb +151 -0
- data/bin/commands/on.rb +66 -0
- data/bin/commands/open.rb +53 -0
- data/bin/commands/plugins.rb +23 -0
- data/bin/commands/recent.rb +78 -0
- data/bin/commands/redo.rb +22 -0
- data/bin/commands/reset.rb +106 -0
- data/bin/commands/rotate.rb +73 -0
- data/bin/commands/sections.rb +11 -0
- data/bin/commands/select.rb +123 -0
- data/bin/commands/show.rb +231 -0
- data/bin/commands/since.rb +64 -0
- data/bin/commands/tag.rb +179 -0
- data/bin/commands/tag_dir.rb +29 -0
- data/bin/commands/tags.rb +93 -0
- data/bin/commands/template.rb +61 -0
- data/bin/commands/today.rb +65 -0
- data/bin/commands/undo.rb +49 -0
- data/bin/commands/view.rb +238 -0
- data/bin/commands/views.rb +11 -0
- data/bin/commands/yesterday.rb +73 -0
- data/bin/doing +39 -3641
- data/docs/doc/Array.html +1 -1
- 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 +1 -1
- data/docs/doc/Doing/Completion.html +1 -1
- data/docs/doc/Doing/Configuration.html +2 -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 +1 -1
- data/docs/doc/Doing/LogAdapter.html +1 -1
- data/docs/doc/Doing/Note.html +1 -1
- 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 +1 -1
- data/docs/doc/Doing/Types.html +1 -1
- data/docs/doc/Doing/Util/Backup.html +1 -1
- data/docs/doc/Doing/Util.html +1 -1
- data/docs/doc/Doing/WWID.html +1 -1
- data/docs/doc/Doing.html +2 -2
- data/docs/doc/FalseClass.html +201 -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 +1 -1
- data/docs/doc/Symbol.html +1 -1
- data/docs/doc/Time.html +1 -1
- data/docs/doc/TrueClass.html +201 -0
- data/docs/doc/_index.html +1 -1
- data/docs/doc/file.README.html +2 -2
- data/docs/doc/index.html +2 -2
- data/docs/doc/method_list.html +374 -366
- data/docs/doc/top-level-namespace.html +1 -1
- data/doing.rdoc +15 -5
- data/lib/completion/_doing.zsh +3 -3
- data/lib/completion/doing.fish +1 -1
- data/lib/doing/changelog/changes.rb +1 -1
- data/lib/doing/configuration.rb +1 -0
- data/lib/doing/pager.rb +1 -0
- data/lib/doing/prompt.rb +8 -0
- data/lib/doing/version.rb +1 -1
- data/lib/examples/commands/wiki.rb +6 -7
- data/lib/helpers/threaded_tests.rb +25 -19
- metadata +45 -1
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -8,7 +8,7 @@ _If you're one of the rare people like me who find this useful, feel free to
|
|
8
8
|
|
9
9
|
<!--README-->
|
10
10
|
|
11
|
-
The current version of `doing` is <!--VER-->2.1.
|
11
|
+
The current version of `doing` is <!--VER-->2.1.25<!--END VER-->.
|
12
12
|
|
13
13
|
Find all of the documentation in the [doing wiki][wiki].
|
14
14
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# @@add_section
|
2
|
+
desc 'Add a new section to the "doing" file'
|
3
|
+
arg_name 'SECTION_NAME'
|
4
|
+
command :add_section do |c|
|
5
|
+
c.example 'doing add_section Ideas', desc: 'Add a section called Ideas to the doing file'
|
6
|
+
|
7
|
+
c.action do |_global_options, _options, args|
|
8
|
+
raise InvalidArgument, "Section #{args[0]} already exists" if @wwid.sections.include?(args[0])
|
9
|
+
|
10
|
+
@wwid.content.add_section(args.join(' ').cap_first, log: true)
|
11
|
+
@wwid.write(@wwid.doing_file)
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# @@again @@resume
|
2
|
+
desc 'Repeat last entry as new entry'
|
3
|
+
long_desc 'This command is designed to allow multiple time intervals to be created for an entry by duplicating it with a new start (and end, eventually) time'
|
4
|
+
command %i[again resume] do |c|
|
5
|
+
c.example 'doing resume', desc: 'Duplicate the most recent entry with a new start time, removing any @done tag'
|
6
|
+
c.example 'doing again', desc: 'again is an alias for resume'
|
7
|
+
c.example 'doing resume --editor', desc: 'Repeat the last entry, opening the new entry in the default editor'
|
8
|
+
c.example 'doing resume --tag project1 --in Projects', desc: 'Repeat the last entry tagged @project1, creating the new entry in the Projects section'
|
9
|
+
c.example 'doing resume --interactive', desc: 'Select the entry to repeat from a menu'
|
10
|
+
|
11
|
+
c.desc 'Get last entry from a specific section'
|
12
|
+
c.arg_name 'NAME'
|
13
|
+
c.flag %i[s section], default_value: 'All'
|
14
|
+
|
15
|
+
c.desc 'Add new entry to section (default: same section as repeated entry)'
|
16
|
+
c.arg_name 'SECTION_NAME'
|
17
|
+
c.flag [:in]
|
18
|
+
|
19
|
+
c.desc 'Backdate start date by interval or set to time [4pm|20m|2h|"yesterday noon"]'
|
20
|
+
c.arg_name 'DATE_STRING'
|
21
|
+
c.flag %i[b back started], type: DateBeginString
|
22
|
+
|
23
|
+
c.desc 'Repeat last entry matching tags. Combine multiple tags with a comma. Wildcards allowed (*, ?)'
|
24
|
+
c.arg_name 'TAG'
|
25
|
+
c.flag [:tag], type: TagArray
|
26
|
+
|
27
|
+
c.desc 'Repeat last entry matching search. Surround with
|
28
|
+
slashes for regex (e.g. "/query/"), start with a single quote for exact match ("\'query").'
|
29
|
+
c.arg_name 'QUERY'
|
30
|
+
c.flag [:search]
|
31
|
+
|
32
|
+
c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
|
33
|
+
c.arg_name 'QUERY'
|
34
|
+
c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
|
35
|
+
|
36
|
+
# c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
|
37
|
+
# c.switch [:fuzzy], default_value: false, negatable: false
|
38
|
+
|
39
|
+
c.desc 'Force exact search string matching (case sensitive)'
|
40
|
+
c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
|
41
|
+
|
42
|
+
c.desc 'Resume items that *don\'t* match search/tag filters'
|
43
|
+
c.switch [:not], default_value: false, negatable: false
|
44
|
+
|
45
|
+
c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
|
46
|
+
c.arg_name 'TYPE'
|
47
|
+
c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
|
48
|
+
|
49
|
+
c.desc 'Boolean used to combine multiple tags. Use PATTERN to parse + and - as booleans'
|
50
|
+
c.arg_name 'BOOLEAN'
|
51
|
+
c.flag [:bool], must_match: REGEX_BOOL, default_value: 'PATTERN'
|
52
|
+
|
53
|
+
c.desc "Edit duplicated entry with #{Doing::Util.default_editor} before adding"
|
54
|
+
c.switch %i[e editor], negatable: false, default_value: false
|
55
|
+
|
56
|
+
c.desc 'Add a note'
|
57
|
+
c.arg_name 'TEXT'
|
58
|
+
c.flag %i[n note]
|
59
|
+
|
60
|
+
c.desc 'Prompt for note via multi-line input'
|
61
|
+
c.switch %i[ask], negatable: false, default_value: false
|
62
|
+
|
63
|
+
c.desc 'Select item to resume from a menu of matching entries'
|
64
|
+
c.switch %i[i interactive], negatable: false, default_value: false
|
65
|
+
|
66
|
+
c.action do |_global_options, options, _args|
|
67
|
+
options[:fuzzy] = false
|
68
|
+
tags = options[:tag].nil? ? [] : options[:tag]
|
69
|
+
|
70
|
+
options[:case] = options[:case].normalize_case
|
71
|
+
|
72
|
+
if options[:search]
|
73
|
+
search = options[:search]
|
74
|
+
search.sub!(/^'?/, "'") if options[:exact]
|
75
|
+
options[:search] = search
|
76
|
+
end
|
77
|
+
|
78
|
+
if options[:back]
|
79
|
+
date = options[:back]
|
80
|
+
raise InvalidTimeExpression, 'Unable to parse date string for --back' if date.nil?
|
81
|
+
else
|
82
|
+
date = Time.now
|
83
|
+
end
|
84
|
+
|
85
|
+
note = Doing::Note.new(options[:note])
|
86
|
+
note.add(Doing::Prompt.read_lines(prompt: 'Add a note')) if options[:ask]
|
87
|
+
|
88
|
+
options[:note] = note
|
89
|
+
|
90
|
+
opts = options.clone
|
91
|
+
|
92
|
+
opts[:tag] = tags
|
93
|
+
opts[:tag_bool] = options[:bool].normalize_bool
|
94
|
+
opts[:interactive] = options[:interactive]
|
95
|
+
opts[:date] = date
|
96
|
+
|
97
|
+
@wwid.repeat_last(opts)
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# @@archive @@move
|
2
|
+
desc 'Move entries between sections'
|
3
|
+
long_desc %(Argument can be a section name to move all entries from a section,
|
4
|
+
or start with an "@" to move entries matching a tag.
|
5
|
+
|
6
|
+
Default with no argument moves items from the "#{@settings['current_section']}" section to Archive.)
|
7
|
+
arg_name 'SECTION_OR_TAG'
|
8
|
+
default_value @settings['current_section']
|
9
|
+
command %i[archive move] do |c|
|
10
|
+
c.example 'doing archive Currently', desc: 'Move all entries in the Currently section to Archive section'
|
11
|
+
c.example 'doing archive @done', desc: 'Move all entries tagged @done to Archive'
|
12
|
+
c.example 'doing archive --to Later @project1', desc: 'Move all entries tagged @project1 to Later section'
|
13
|
+
c.example 'doing move Later --tag project1 --to Currently', desc: 'Move entries in Later tagged @project1 to Currently (move is an alias for archive)'
|
14
|
+
|
15
|
+
c.desc 'How many items to keep (ignored if archiving by tag or search)'
|
16
|
+
c.arg_name 'X'
|
17
|
+
c.flag %i[k keep], must_match: /^\d+$/, type: Integer
|
18
|
+
|
19
|
+
c.desc 'Move entries to'
|
20
|
+
c.arg_name 'SECTION_NAME'
|
21
|
+
c.flag %i[t to], default_value: 'Archive'
|
22
|
+
|
23
|
+
c.desc 'Label moved items with @from(SECTION_NAME)'
|
24
|
+
c.switch [:label], default_value: true, negatable: true
|
25
|
+
|
26
|
+
c.desc 'Tag filter, combine multiple tags with a comma. Wildcards allowed (*, ?). Added for compatibility with other commands'
|
27
|
+
c.arg_name 'TAG'
|
28
|
+
c.flag [:tag], type: TagArray
|
29
|
+
|
30
|
+
c.desc 'Tag boolean (AND|OR|NOT). Use PATTERN to parse + and - as booleans'
|
31
|
+
c.arg_name 'BOOLEAN'
|
32
|
+
c.flag [:bool], must_match: REGEX_BOOL, default_value: 'PATTERN'
|
33
|
+
|
34
|
+
c.desc 'Search filter'
|
35
|
+
c.arg_name 'QUERY'
|
36
|
+
c.flag [:search]
|
37
|
+
|
38
|
+
c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
|
39
|
+
c.arg_name 'QUERY'
|
40
|
+
c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
|
41
|
+
|
42
|
+
# c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
|
43
|
+
# c.switch [:fuzzy], default_value: false, negatable: false
|
44
|
+
|
45
|
+
c.desc 'Force exact search string matching (case sensitive)'
|
46
|
+
c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
|
47
|
+
|
48
|
+
c.desc 'Show items that *don\'t* match search string'
|
49
|
+
c.switch [:not], default_value: false, negatable: false
|
50
|
+
|
51
|
+
c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
|
52
|
+
c.arg_name 'TYPE'
|
53
|
+
c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
|
54
|
+
|
55
|
+
c.desc 'Archive entries older than date
|
56
|
+
(Flexible date format, e.g. 1/27/2021, 2020-07-19, or Monday 3pm)'
|
57
|
+
c.arg_name 'DATE_STRING'
|
58
|
+
c.flag [:before], type: DateEndString
|
59
|
+
|
60
|
+
c.action do |_global_options, options, args|
|
61
|
+
options[:fuzzy] = false
|
62
|
+
if args.empty?
|
63
|
+
section = @settings['current_section']
|
64
|
+
tags = []
|
65
|
+
elsif args[0] =~ /^all/i
|
66
|
+
section = 'all'
|
67
|
+
elsif args[0] =~ /^@\S+/
|
68
|
+
section = 'all'
|
69
|
+
tags = args.map { |t| t.sub(/^@/, '').strip }
|
70
|
+
else
|
71
|
+
section = args[0].cap_first
|
72
|
+
tags = args.length > 1 ? args[1..].map { |t| t.sub(/^@/, '').strip } : []
|
73
|
+
end
|
74
|
+
|
75
|
+
raise InvalidArgument, '--keep and --count can not be used together' if options[:keep] && options[:count]
|
76
|
+
|
77
|
+
tags.concat(options[:tag]) if options[:tag]
|
78
|
+
|
79
|
+
search = nil
|
80
|
+
|
81
|
+
options[:case] = options[:case].normalize_case
|
82
|
+
|
83
|
+
if options[:search]
|
84
|
+
search = options[:search]
|
85
|
+
search.sub!(/^'?/, "'") if options[:exact]
|
86
|
+
end
|
87
|
+
|
88
|
+
opts = options.clone
|
89
|
+
opts[:search] = search
|
90
|
+
opts[:bool] = options[:bool].normalize_bool
|
91
|
+
opts[:destination] = options[:to]
|
92
|
+
opts[:tags] = tags
|
93
|
+
|
94
|
+
@wwid.archive(section, opts)
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# @@cancel
|
2
|
+
desc 'End last X entries with no time tracked'
|
3
|
+
long_desc 'Adds @done tag without datestamp so no elapsed time is recorded. Alias for `doing finish --no-date`'
|
4
|
+
arg_name 'COUNT'
|
5
|
+
command :cancel do |c|
|
6
|
+
c.example 'doing cancel', desc: 'Cancel the last entry'
|
7
|
+
c.example 'doing cancel --tag project1 -u 5', desc: 'Cancel the last 5 unfinished entries containing @project1'
|
8
|
+
|
9
|
+
c.desc 'Archive entries'
|
10
|
+
c.switch %i[a archive], negatable: false, default_value: false
|
11
|
+
|
12
|
+
c.desc 'Section'
|
13
|
+
c.arg_name 'NAME'
|
14
|
+
c.flag %i[s section]
|
15
|
+
|
16
|
+
c.desc 'Cancel the last X entries containing TAG. Separate multiple tags with comma (--tag=tag1,tag2). Wildcards allowed (*, ?)'
|
17
|
+
c.arg_name 'TAG'
|
18
|
+
c.flag [:tag], type: TagArray
|
19
|
+
|
20
|
+
c.desc 'Boolean (AND|OR|NOT) with which to combine multiple tag filters. Use PATTERN to parse + and - as booleans'
|
21
|
+
c.arg_name 'BOOLEAN'
|
22
|
+
c.flag [:bool], must_match: REGEX_BOOL, default_value: 'PATTERN'
|
23
|
+
|
24
|
+
c.desc 'Cancel the last X entries matching search filter, surround with slashes for regex (e.g. "/query.*/"), start with single quote for exact match ("\'query")'
|
25
|
+
c.arg_name 'QUERY'
|
26
|
+
c.flag [:search]
|
27
|
+
|
28
|
+
c.desc 'Perform a tag value query ("@done > two hours ago" or "@progress < 50"). May be used multiple times, combined with --bool'
|
29
|
+
c.arg_name 'QUERY'
|
30
|
+
c.flag [:val], multiple: true, must_match: REGEX_VALUE_QUERY
|
31
|
+
|
32
|
+
# c.desc '[DEPRECATED] Use alternative fuzzy matching for search string'
|
33
|
+
# c.switch [:fuzzy], default_value: false, negatable: false
|
34
|
+
|
35
|
+
c.desc 'Force exact search string matching (case sensitive)'
|
36
|
+
c.switch %i[x exact], default_value: @config.exact_match?, negatable: @config.exact_match?
|
37
|
+
|
38
|
+
c.desc 'Finish items that *don\'t* match search/tag filters'
|
39
|
+
c.switch [:not], default_value: false, negatable: false
|
40
|
+
|
41
|
+
c.desc 'Case sensitivity for search string matching [(c)ase-sensitive, (i)gnore, (s)mart]'
|
42
|
+
c.arg_name 'TYPE'
|
43
|
+
c.flag [:case], must_match: /^[csi]/, default_value: @settings.dig('search', 'case')
|
44
|
+
|
45
|
+
c.desc 'Cancel last entry (or entries) not already marked @done'
|
46
|
+
c.switch %i[u unfinished], negatable: false, default_value: false
|
47
|
+
|
48
|
+
c.desc 'Select item(s) to cancel from a menu of matching entries'
|
49
|
+
c.switch %i[i interactive], negatable: false, default_value: false
|
50
|
+
|
51
|
+
c.action do |_global_options, options, args|
|
52
|
+
options[:fuzzy] = false
|
53
|
+
if options[:section]
|
54
|
+
section = @wwid.guess_section(options[:section]) || options[:section].cap_first
|
55
|
+
else
|
56
|
+
section = @settings['current_section']
|
57
|
+
end
|
58
|
+
|
59
|
+
if options[:tag].nil?
|
60
|
+
tags = []
|
61
|
+
else
|
62
|
+
tags = options[:tag]
|
63
|
+
end
|
64
|
+
|
65
|
+
raise InvalidArgument, 'Only one argument allowed' if args.length > 1
|
66
|
+
|
67
|
+
raise InvalidArgument, 'Invalid argument (specify number of recent items to mark @done)' unless args.empty? || args[0] =~ /\d+/
|
68
|
+
|
69
|
+
if options[:interactive]
|
70
|
+
count = 0
|
71
|
+
else
|
72
|
+
count = args[0] ? args[0].to_i : 1
|
73
|
+
end
|
74
|
+
|
75
|
+
search = nil
|
76
|
+
|
77
|
+
if options[:search]
|
78
|
+
search = options[:search]
|
79
|
+
search.sub!(/^'?/, "'") if options[:exact]
|
80
|
+
end
|
81
|
+
|
82
|
+
opts = {
|
83
|
+
archive: options[:archive],
|
84
|
+
case: options[:case].normalize_case,
|
85
|
+
count: count,
|
86
|
+
date: false,
|
87
|
+
fuzzy: options[:fuzzy],
|
88
|
+
interactive: options[:interactive],
|
89
|
+
not: options[:not],
|
90
|
+
search: search,
|
91
|
+
section: section,
|
92
|
+
sequential: false,
|
93
|
+
tag: tags,
|
94
|
+
tag_bool: options[:bool].normalize_bool,
|
95
|
+
tags: ['done'],
|
96
|
+
unfinished: options[:unfinished],
|
97
|
+
val: options[:val]
|
98
|
+
}
|
99
|
+
|
100
|
+
@wwid.tag_last(opts)
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# @@changelog @@changes
|
2
|
+
desc 'List recent changes in Doing'
|
3
|
+
long_desc %(Display a formatted list of changes in recent versions.
|
4
|
+
|
5
|
+
Without flags, displays only the most recent version.
|
6
|
+
Use --lookup or --all for history.)
|
7
|
+
command %i[changes changelog] do |c|
|
8
|
+
c.desc 'Display all versions'
|
9
|
+
c.switch %i[a all], default_value: false, negatable: false
|
10
|
+
|
11
|
+
c.desc %(Look up a specific version. Specify versions as "MAJ.MIN.PATCH", MIN
|
12
|
+
and PATCH are optional. Use > or < to see all changes since or prior
|
13
|
+
to a version.)
|
14
|
+
c.arg_name 'VERSION'
|
15
|
+
c.flag %i[l lookup], must_match: /^(?:(?:(?:[<>=]|p(?:rior)|b(?:efore)|o(?:lder)|s(?:ince)|a(?:fter)|n(?:ewer))? *[\d.*?]+ *)+|(?:[\d.]+ *-+ *[\d.]+))$/
|
16
|
+
|
17
|
+
c.desc %(Show changelogs matching search terms (uses pattern-based searching).
|
18
|
+
Add slashes to search with regular expressions, e.g. `--search "/output.*flag/"`)
|
19
|
+
c.flag %i[s search]
|
20
|
+
|
21
|
+
c.example 'doing changes', desc: 'View changes in the current version'
|
22
|
+
c.example 'doing changes --all', desc: 'See the entire changelog'
|
23
|
+
c.example 'doing changes --lookup 2.0.21', desc: 'See changes from version 2.0.21'
|
24
|
+
c.example 'doing changes --lookup "> 2.1"', desc: 'See all changes since 2.1.0'
|
25
|
+
c.example 'doing changes --search "tags +bool"', desc: 'See all changes containing "tags" and "bool"'
|
26
|
+
c.example 'doing changes -l "> 2.1" -s "pattern"', desc: 'Lookup and search can be combined'
|
27
|
+
|
28
|
+
|
29
|
+
c.action do |_global_options, options, args|
|
30
|
+
cl = Doing::Changes.new(lookup: options[:lookup], search: options[:search])
|
31
|
+
|
32
|
+
content = if options[:all] || options[:search] || options[:lookup]
|
33
|
+
cl.to_s
|
34
|
+
else
|
35
|
+
cl.latest
|
36
|
+
end
|
37
|
+
|
38
|
+
parsed = TTY::Markdown.parse(content, width: 80, symbols: {override: {bullet: "•"}})
|
39
|
+
Doing::Pager.paginate = true
|
40
|
+
Doing::Pager.page parsed
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# @@choose
|
2
|
+
desc 'Select a section to display from a menu'
|
3
|
+
command :choose do |c|
|
4
|
+
c.action do |_global_options, _options, _args|
|
5
|
+
section = @wwid.choose_section
|
6
|
+
|
7
|
+
Doing::Pager.page @wwid.list_section({ section: section.cap_first, count: 0 }) if section
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# @@colors
|
2
|
+
desc 'List available color variables for configuration templates and views'
|
3
|
+
command :colors do |c|
|
4
|
+
c.action do |_global_options, _options, _args|
|
5
|
+
bgs = []
|
6
|
+
fgs = []
|
7
|
+
@colors::attributes.each do |color|
|
8
|
+
if color.to_s =~ /bg/
|
9
|
+
bgs.push("#{@colors.send(color, " ")}#{@colors.default} <-- #{color.to_s}")
|
10
|
+
else
|
11
|
+
fgs.push("#{@colors.send(color, "XXXX")}#{@colors.default} <-- #{color.to_s}")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
out = []
|
15
|
+
out << fgs.join("\n")
|
16
|
+
out << bgs.join("\n")
|
17
|
+
Doing::Pager.page out.join("\n")
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @@commands
|
4
|
+
desc 'Enable and disable Doing commands'
|
5
|
+
command :commands do |c|
|
6
|
+
c.example 'doing commands add', desc: 'Get a menu of available commands'
|
7
|
+
c.example 'doing commands add COMMAND', desc: 'Specify a command to enable'
|
8
|
+
c.example 'doing commands remove COMMAND', desc: 'Specify a command to disable'
|
9
|
+
|
10
|
+
c.default_command :add
|
11
|
+
|
12
|
+
# @@commands.enable
|
13
|
+
c.desc 'Enable Doing commands'
|
14
|
+
c.long_desc 'Run without arguments to select commands from a list.'
|
15
|
+
c.arg_name 'COMMAND [COMMAND...]'
|
16
|
+
c.command %i[add enable] do |add|
|
17
|
+
add.action do |_global, _options, args|
|
18
|
+
cfg = @settings
|
19
|
+
custom_dir = @settings.dig('plugins', 'command_path')
|
20
|
+
|
21
|
+
available = cfg['disabled_commands']
|
22
|
+
raise UserCancelled, 'No commands available to enable' unless args.good? || available.good?
|
23
|
+
|
24
|
+
to_enable = if args.good?
|
25
|
+
args
|
26
|
+
else
|
27
|
+
Doing::Prompt.choose_from(available,
|
28
|
+
prompt: 'Select commands to enable',
|
29
|
+
multiple: true,
|
30
|
+
sorted: true).strip.split("\n")
|
31
|
+
end
|
32
|
+
to_enable.each do |cmd|
|
33
|
+
default_command = File.join(File.dirname(__FILE__), "#{cmd}.rb")
|
34
|
+
custom_command = File.join(File.expand_path(custom_dir), "#{cmd}.rb")
|
35
|
+
unless File.exist?(default_command) || File.exist?(custom_command)
|
36
|
+
raise InvalidArgument, "Command #{cmd} not found"
|
37
|
+
end
|
38
|
+
|
39
|
+
raise InvalidArgument, "Command #{cmd} is not disabled" unless available.include?(cmd)
|
40
|
+
|
41
|
+
available.delete(cmd)
|
42
|
+
end
|
43
|
+
|
44
|
+
cfg.deep_set(['disabled_commands'], available)
|
45
|
+
|
46
|
+
Doing::Util.write_to_file(@config.config_file, YAML.dump(cfg), backup: true)
|
47
|
+
Doing.logger.warn('Config:', "#{@config.config_file} updated")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# @@commands.disable
|
52
|
+
c.desc 'Disable Doing commands'
|
53
|
+
c.command %i[remove disable] do |remove|
|
54
|
+
remove.action do |_global, _options, args|
|
55
|
+
available = Dir.glob(File.join(File.dirname(__FILE__), '*.rb')).map { |cmd| File.basename(cmd, '.rb') }
|
56
|
+
cfg = @settings
|
57
|
+
custom_dir = @settings.dig('plugins', 'command_path')
|
58
|
+
custom_commands = Dir.glob(File.join(File.expand_path(custom_dir), '*.rb'))
|
59
|
+
available.concat(custom_commands.map { |cmd| File.basename(cmd, '.rb') })
|
60
|
+
disabled = cfg['disabled_commands']
|
61
|
+
disabled.each { |cmd| available.delete(cmd) }
|
62
|
+
to_disable = if args.good?
|
63
|
+
args
|
64
|
+
else
|
65
|
+
Doing::Prompt.choose_from(available,
|
66
|
+
prompt: 'Select commands to enable',
|
67
|
+
multiple: true,
|
68
|
+
sorted: true).strip.split("\n")
|
69
|
+
end
|
70
|
+
to_disable.each do |cmd|
|
71
|
+
default_command = File.join(File.dirname(__FILE__), "#{cmd}.rb")
|
72
|
+
custom_command = File.join(File.expand_path(custom_dir), "#{cmd}.rb")
|
73
|
+
unless File.exist?(default_command) || File.exist?(custom_command)
|
74
|
+
raise InvalidArgument, "Command #{cmd} not found"
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
raise InvalidArgument, "Command #{cmd} is not enabled" unless available.include?(cmd)
|
79
|
+
end
|
80
|
+
|
81
|
+
cfg.deep_set(['disabled_commands'], disabled.concat(to_disable))
|
82
|
+
|
83
|
+
Doing::Util.write_to_file(@config.config_file, YAML.dump(cfg), backup: true)
|
84
|
+
Doing.logger.warn('Config:', "#{@config.config_file} updated")
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# @@commands_accepting
|
2
|
+
arg_name 'OPTION'
|
3
|
+
command :commands_accepting do |c|
|
4
|
+
c.desc 'Output in single column for completion'
|
5
|
+
c.switch %i[c column]
|
6
|
+
|
7
|
+
c.action do |g, o, a|
|
8
|
+
a.each do |option|
|
9
|
+
cmds = []
|
10
|
+
commands.each do |cmd, v|
|
11
|
+
v.flags.merge(v.switches).each do |n, flag|
|
12
|
+
if flag.name == option.to_sym || flag.aliases&.include?(option.to_sym)
|
13
|
+
cmds.push(cmd)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
if o[:column]
|
19
|
+
puts cmds.sort
|
20
|
+
else
|
21
|
+
puts "Commands accepting --#{option}: #{cmds.sort.join(', ')}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# @@completion
|
2
|
+
desc 'Generate shell completion scripts'
|
3
|
+
long_desc 'Generates the necessary scripts to add command line completion to various shells, so typing \'doing\' and hitting
|
4
|
+
tab will offer completions of subcommands and their options.'
|
5
|
+
command :completion do |c|
|
6
|
+
c.example 'doing completion', desc: 'Output zsh (default) to STDOUT'
|
7
|
+
c.example 'doing completion --type zsh --file ~/.zsh-completions/_doing.zsh', desc: 'Output zsh completions to file'
|
8
|
+
c.example 'doing completion --type fish --file ~/.config/fish/completions/doing.fish', desc: 'Output fish completions to file'
|
9
|
+
c.example 'doing completion --type bash --file ~/.bash_it/completion/enabled/doing.bash', desc: 'Output bash completions to file'
|
10
|
+
|
11
|
+
c.desc 'Shell to generate for (bash, zsh, fish)'
|
12
|
+
c.arg_name 'SHELL'
|
13
|
+
c.flag %i[t type], must_match: /^(?:[bzf](?:[ai]?sh)?|all)$/i, default_value: 'zsh'
|
14
|
+
|
15
|
+
c.desc 'File to write output to'
|
16
|
+
c.arg_name 'PATH'
|
17
|
+
c.flag %i[f file], default_value: 'STDOUT'
|
18
|
+
|
19
|
+
c.action do |_global_options, options, _args|
|
20
|
+
script_dir = File.join(File.dirname(__FILE__), '..', 'scripts')
|
21
|
+
|
22
|
+
Doing::Completion.generate_completion(type: options[:type], file: options[:file])
|
23
|
+
end
|
24
|
+
end
|