na 1.2.40 → 1.2.41

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2039d6fb16db44b000517f1f19d0928c5b82124abfe1c5416f98b892f21d88da
4
- data.tar.gz: a21cd96adf698afdbf0ffd55eb43ae9c408c46cbcbec691aeb9d1052f1a55862
3
+ metadata.gz: 817b128da7a400740e7e7ee5f8fb8d50920745290475c83ae9c70f23b2d2638e
4
+ data.tar.gz: ba7c83c4710bd2794ecf0c9d570cbbadf3188dc622c46b7e82afeeff7d8c0f75
5
5
  SHA512:
6
- metadata.gz: a1a9e7ac7341409792174d9ecce9b00379e9fa0a53c75d01d7abf8318d7336b417aa41d1a23d5a00628a8aef9fd1f3c53070b795220b7c94b0c5d8fa12781b63
7
- data.tar.gz: 4033e29443e01ff8ee866f98255f9f2b008619d1a1f1b2e3420201a7efb99837eae612d3383234afb67c6c30fea16a1cad2199a273fd66c7d6ef21275d2aa0a5
6
+ metadata.gz: 6df78d477a696e991210196962c28238dc397de20747fba3e24a54d2e341d06c8194957b0b04a612a7c6f504f77912f541a813b9717fc0886b7cc8d9049b531e
7
+ data.tar.gz: 43d3affb7e8c8e720309b65430d314ae4ab484ad0136c9a2689dacebad28d5703568d0ad01f219ddfd0c85f998b57fd91a5f5bdc6784d95e1f88a04167473191
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ### 1.2.41
2
+
3
+ 2023-09-06 08:13
4
+
5
+ #### NEW
6
+
7
+ - Tag command
8
+
9
+ #### FIXED
10
+
11
+ - Nil error in action.pretty
12
+
1
13
  ### 1.2.40
2
14
 
3
15
  2023-09-06 08:11
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- na (1.2.40)
4
+ na (1.2.41)
5
5
  chronic (~> 0.10, >= 0.10.2)
6
6
  gli (~> 2.21.0)
7
7
  mdless (~> 1.0, >= 1.0.32)
data/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  _If you're one of the rare people like me who find this useful, feel free to
10
10
  [buy me some coffee][donate]._
11
11
 
12
- The current version of `na` is 1.2.40
12
+ The current version of `na` is 1.2.41
13
13
  .
14
14
 
15
15
  `na` ("next action") is a command line tool designed to make it easy to see what your next actions are for any project, right from the command line. It works with TaskPaper-formatted files (but any plain text format will do), looking for `@na` tags (or whatever you specify) in todo files in your current folder.
@@ -77,7 +77,7 @@ SYNOPSIS
77
77
  na [global options] command [command options] [arguments...]
78
78
 
79
79
  VERSION
80
- 1.2.40
80
+ 1.2.41
81
81
 
82
82
  GLOBAL OPTIONS
83
83
  -a, --add - Add a next action (deprecated, for backwards compatibility)
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ class App
4
+ extend GLI::App
5
+ desc 'Add tags to matching action(s)'
6
+ long_desc 'Provides an easy way to tag existing actions.
7
+
8
+ Use !tag to remove a tag, use ~tag(new value) to change a tag or add a value.
9
+
10
+ If multiple todo files are found in the current directory, a menu will
11
+ allow you to pick which file to act on, or use --all to apply to all matches.'
12
+ arg_name 'TAG', mutliple: true
13
+ command %i[tag] do |c|
14
+ c.example 'na tag "project(warpspeed)" --search "An existing task"',
15
+ desc: 'Find "An existing task" action and add @project(warpspeed) to it'
16
+ c.example 'na tag "!project1" --tagged project2 --all',
17
+ desc: 'Find all actions tagged @project2 and remove @project1 from them'
18
+ c.example 'na tag "!project2" --all',
19
+ desc: 'Remove @project2 from all actions'
20
+ c.example 'na tag "~project(dirt nap)" --search "An existing task"',
21
+ desc: 'Find "An existing task" and change (or add) its @project tag value to "dirt nap"'
22
+
23
+ c.desc 'Use a known todo file, partial matches allowed'
24
+ c.arg_name 'TODO_FILE'
25
+ c.flag %i[in todo]
26
+
27
+ c.desc 'Include @done actions'
28
+ c.switch %i[done]
29
+
30
+ c.desc 'Specify the file to search for the task'
31
+ c.arg_name 'PATH'
32
+ c.flag %i[file]
33
+
34
+ c.desc 'Search for files X directories deep'
35
+ c.arg_name 'DEPTH'
36
+ c.flag %i[d depth], must_match: /^[1-9]$/, type: :integer, default_value: 1
37
+
38
+ c.desc 'Match actions containing tag. Allows value comparisons'
39
+ c.arg_name 'TAG'
40
+ c.flag %i[tagged], multiple: true
41
+
42
+ c.desc 'Act on all matches immediately (no menu)'
43
+ c.switch %i[all], negatable: false
44
+
45
+ c.desc 'Filter results using search terms'
46
+ c.arg_name 'QUERY'
47
+ c.flag %i[search find grep], multiple: true
48
+
49
+ c.desc 'Interpret search pattern as regular expression'
50
+ c.switch %i[e regex], negatable: false
51
+
52
+ c.desc 'Match pattern exactly'
53
+ c.switch %i[x exact], negatable: false
54
+
55
+ c.action do |global_options, options, args|
56
+ tags = args.join(',').split(/ *, */)
57
+ options[:remove] = []
58
+ options[:tag] = []
59
+ tags.each do |tag|
60
+ if tag =~ /^[!-]/
61
+ options[:remove] << tag.sub(/^[!-]/, '').sub(/^@/, '')
62
+ elsif tag =~ /^~/
63
+ options[:remove] << tag.sub(/^~/, '').sub(/\(.*?\)$/, '').sub(/^@/, '')
64
+ options[:tag] << tag.sub(/^~/, '').sub(/^@/, '')
65
+ else
66
+ options[:tag] << tag.sub(/^@/, '')
67
+ end
68
+ end
69
+
70
+ if options[:search]
71
+ tokens = nil
72
+ if options[:exact]
73
+ tokens = options[:search]
74
+ elsif options[:regex]
75
+ tokens = Regexp.new(options[:search], Regexp::IGNORECASE)
76
+ else
77
+ action = options[:search].join(' ')
78
+ tokens = []
79
+ all_req = action !~ /[+!-]/ && !options[:or]
80
+
81
+ action.split(/ /).each do |arg|
82
+ m = arg.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
83
+ tokens.push({
84
+ token: m['tok'],
85
+ required: all_req || (!m['req'].nil? && m['req'] == '+'),
86
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/ ? true : false
87
+ })
88
+ end
89
+ end
90
+ end
91
+
92
+ if (tokens.nil? || tokens.empty?) && options[:tagged].empty?
93
+ NA.notify("#{NA.theme[:error]}Empty input, cancelled", exit_code: 1)
94
+ end
95
+
96
+ all_req = options[:tagged].join(' ') !~ /[+!-]/ && !options[:or]
97
+ tags = []
98
+ options[:tagged].join(',').split(/ *, */).each do |arg|
99
+ m = arg.match(/^(?<req>[+!-])?(?<tag>[^ =<>$~\^]+?) *(?:(?<op>[=<>~]{1,2}|[*$\^]=) *(?<val>.*?))?$/)
100
+
101
+ tags.push({
102
+ tag: m['tag'].wildcard_to_rx,
103
+ comp: m['op'],
104
+ value: m['val'],
105
+ required: all_req || (!m['req'].nil? && m['req'] == '+'),
106
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/ ? true : false
107
+ })
108
+ end
109
+
110
+ add_tags = options[:tag] ? options[:tag].join(',').split(/ *, */).map { |t| t.sub(/^@/, '').wildcard_to_rx } : []
111
+ remove_tags = options[:remove] ? options[:remove].join(',').split(/ *, */).map { |t| t.sub(/^@/, '').wildcard_to_rx } : []
112
+
113
+ if options[:file]
114
+ file = File.expand_path(options[:file])
115
+ NA.notify("#{NA.theme[:error]}File not found", exit_code: 1) unless File.exist?(file)
116
+
117
+ targets = [file]
118
+ elsif options[:todo]
119
+ todo = []
120
+ options[:todo].split(/ *, */).each do |a|
121
+ m = a.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
122
+ todo.push({
123
+ token: m['tok'],
124
+ required: all_req || (!m['req'].nil? && m['req'] == '+'),
125
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/ ? true : false
126
+ })
127
+ end
128
+ dirs = NA.match_working_dir(todo)
129
+
130
+ if dirs.count == 1
131
+ targets = [dirs[0]]
132
+ elsif dirs.count.positive?
133
+ targets = NA.select_file(dirs, multiple: true)
134
+ NA.notify("#{NA.theme[:error]}Cancelled", exit_code: 1) unless targets && targets.count.positive?
135
+ else
136
+ NA.notify("#{NA.theme[:error]}Todo not found", exit_code: 1) unless targets && targets.count.positive?
137
+
138
+ end
139
+ else
140
+ files = NA.find_files_matching({
141
+ depth: options[:depth],
142
+ done: options[:done],
143
+ regex: options[:regex],
144
+ require_na: false,
145
+ search: tokens,
146
+ tag: tags
147
+ })
148
+ NA.notify("#{NA.theme[:error]}No todo file found", exit_code: 1) if files.count.zero?
149
+
150
+ targets = files.count > 1 ? NA.select_file(files, multiple: true) : [files[0]]
151
+ NA.notify("#{NA.theme[:error]}Cancelled", exit_code: 1) unless files.count.positive?
152
+
153
+ end
154
+
155
+ NA.notify("#{NA.theme[:error]}No search terms provided", exit_code: 1) if tokens.nil? && options[:tagged].empty?
156
+
157
+ targets.each do |target|
158
+ NA.update_action(target, tokens,
159
+ add_tag: add_tags,
160
+ all: options[:all],
161
+ done: options[:done],
162
+ remove_tag: remove_tags,
163
+ tagged: tags)
164
+ end
165
+ end
166
+ end
167
+ end
data/lib/na/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Na
2
- VERSION = '1.2.40'
2
+ VERSION = '1.2.41'
3
3
  end
data/src/_README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  _If you're one of the rare people like me who find this useful, feel free to
10
10
  [buy me some coffee][donate]._
11
11
 
12
- The current version of `na` is <!--VER-->1.2.39<!--END VER-->.
12
+ The current version of `na` is <!--VER-->1.2.40<!--END VER-->.
13
13
 
14
14
  `na` ("next action") is a command line tool designed to make it easy to see what your next actions are for any project, right from the command line. It works with TaskPaper-formatted files (but any plain text format will do), looking for `@na` tags (or whatever you specify) in todo files in your current folder.
15
15
 
data/test2.txt ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ comment: 2023
3
+ keywords:
4
+ ---
5
+
6
+ Inbox:
7
+ - Test @na
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: na
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.40
4
+ version: 1.2.41
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
@@ -220,6 +220,7 @@ files:
220
220
  - bin/commands/prompt.rb
221
221
  - bin/commands/restore.rb
222
222
  - bin/commands/saved.rb
223
+ - bin/commands/tag.rb
223
224
  - bin/commands/tagged.rb
224
225
  - bin/commands/todos.rb
225
226
  - bin/commands/undo.rb
@@ -245,6 +246,7 @@ files:
245
246
  - na.rdoc
246
247
  - scripts/fixreadme.rb
247
248
  - src/_README.md
249
+ - test2.txt
248
250
  homepage: https://brettterpstra.com/projects/na/
249
251
  licenses:
250
252
  - MIT