na 1.2.63 → 1.2.65

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8913d21ba15ed1f470aa073fbdd6a9d3cb081e1d7d99f09bbb11a56b4bac7167
4
- data.tar.gz: ed3bff3d476c30cb44d2c1d5a0ddb553278f496952a2459585c15ed1f402af30
3
+ metadata.gz: 34efff84dbba9e86c4cfbd4fc0c36f0c153cb024e42a887a93012f41b18774ce
4
+ data.tar.gz: 4db0cd0d3067c119ccdf34ae8481bc08c75acc3dd79dc30e11661b15530a0864
5
5
  SHA512:
6
- metadata.gz: 68f9d3099ff4e2903e612252f17f56fe7c936b1eb2f854ce87f6866464b1a206eec80518ef673dc4f91e30c070a84b11165cd5f442c65f5c4c94593416cf2748
7
- data.tar.gz: 0340b85bdac9837612ad361cd9da0fe96af557697f8e38ac77097a6dd5da0e22031e098c67348de035a95982c7f27445df5fa8bbab0087e6030ab87d6c22a014
6
+ metadata.gz: 78c9b862f5962fe23fb0a7452044d26f6eac92208634857b21881e1c74a7277ea086fa7d0e663b61b0883adc3f3f731cb12e4af92860aa4534f72373e0c2205c
7
+ data.tar.gz: e8819f6ec109c3903c5c51b53e2009deb86a385f5465816825fad39c5794285a48cd693cb3876f2b43e7d39c6cb6006299896c76b6103884aed206127b422f95
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ### 1.2.64
2
+
3
+ 2024-06-22 12:06
4
+
5
+ #### FIXED
6
+
7
+ - Handle file searches where the filename matches the dirname and previously returned a "FILE is a directory" error by testing with an extension or a DIRNAME/BASENAME/BASENAME.EXT
8
+
9
+ #### NEW
10
+
11
+ - `na move ACTION --to PROJECT` command to allow quickly moving actions around
12
+
1
13
  ### 1.2.63
2
14
 
3
15
  2023-12-14 13:56
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- na (1.2.63)
4
+ na (1.2.65)
5
5
  chronic (~> 0.10, >= 0.10.2)
6
6
  gli (~> 2.21.0)
7
7
  mdless (~> 1.0, >= 1.0.32)
@@ -23,7 +23,7 @@ GEM
23
23
  tty-cursor (~> 0.7)
24
24
  tty-screen (~> 0.8)
25
25
  wisper (~> 2.0)
26
- tty-screen (0.8.1)
26
+ tty-screen (0.8.2)
27
27
  tty-which (0.5.0)
28
28
  wisper (2.0.1)
29
29
  yard (0.9.34)
data/README.md CHANGED
@@ -9,8 +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.63
13
- .
12
+ The current version of `na` is 1.2.64.
14
13
 
15
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.
16
15
 
@@ -77,7 +76,7 @@ SYNOPSIS
77
76
  na [global options] command [command options] [arguments...]
78
77
 
79
78
  VERSION
80
- 1.2.63
79
+ 1.2.65
81
80
 
82
81
  GLOBAL OPTIONS
83
82
  -a, --add - Add a next action (deprecated, for backwards compatibility)
@@ -109,6 +108,7 @@ COMMANDS
109
108
  help - Shows a list of commands or help for one command
110
109
  init, create - Create a new todo file in the current directory
111
110
  initconfig - Initialize the config file using current global options
111
+ move - Move an existing action to a different section
112
112
  next, show - Show next actions
113
113
  open - Open a todo file in the default editor
114
114
  projects - Show list of projects for a file
@@ -264,6 +264,48 @@ EXAMPLES
264
264
  na init warpspeed
265
265
  ```
266
266
 
267
+ ##### move
268
+
269
+ Move an action between projects. Argument is a search term, if left blank a prompt will allow you to enter terms. If no `--to` project is specified, a menu will be shown of projects in the target file.
270
+
271
+ Examples:
272
+
273
+ - `na move` (enter a search term, select a file/destination)
274
+ - `na move "Bug description"` (find matching action and show a menu of project destinations)
275
+ - `na move "Bug description" --to Bugs (move matching action to Bugs project)
276
+
277
+ ```
278
+ NAME
279
+ move - Move an existing action to a different section
280
+
281
+ SYNOPSIS
282
+
283
+ na [global options] move [command options] ACTION
284
+
285
+ DESCRIPTION
286
+ Provides an easy way to move an action. If multiple todo files are found in the current directory, a menu will allow you to pick which file to act on.
287
+
288
+ COMMAND OPTIONS
289
+ --all - Act on all matches immediately (no menu)
290
+ --at=POSITION - When moving task, add at [s]tart or [e]nd of target project (default: none)
291
+ -d, --depth=DEPTH - Search for files X directories deep (default: 1)
292
+ -e, --regex - Interpret search pattern as regular expression
293
+ --file=PATH - Specify the file to search for the task (default: none)
294
+ --from=PROJECT[/SUBPROJECT] - Search for actions in a specific project (default: none)
295
+ --in, --todo=TODO_FILE - Use a known todo file, partial matches allowed (default: none)
296
+ -n, --note - Prompt for additional notes. Input will be appended to any existing note. If STDIN input (piped) is detected, it will be used as a note.
297
+ -o, --overwrite - Overwrite note instead of appending
298
+ --[no-]search_notes - Include notes in search (default: enabled)
299
+ --tagged=TAG - Match actions containing tag. Allows value comparisons (may be used more than once, default: none)
300
+ --to=PROJECT - Move action to specific project. If not provided, a menu will be shown (default: none)
301
+ -x, --exact - Match pattern exactly
302
+
303
+ EXAMPLE
304
+
305
+ # Find "A bug in inbox" action and move it to section Bugs
306
+ na move "A bug in inbox" --to Bugs
307
+ ```
308
+
267
309
  ##### next, show
268
310
 
269
311
  Examples:
data/Rakefile CHANGED
@@ -28,6 +28,23 @@ Rake::TestTask.new do |t|
28
28
  t.test_files = FileList['test/*_test.rb']
29
29
  end
30
30
 
31
+ desc 'Install current gem in all versions of asdf-controlled ruby'
32
+ task :install do
33
+ Rake::Task['clobber'].invoke
34
+ Rake::Task['package'].invoke
35
+ Dir.chdir 'pkg'
36
+ file = Dir.glob('*.gem').last
37
+
38
+ current_ruby = `asdf current ruby`.match(/(\d.\d+.\d+)/)[1]
39
+
40
+ `asdf list ruby`.split.map { |ruby| ruby.strip.sub(/^*/, '') }.each do |ruby|
41
+ `asdf shell ruby #{ruby}`
42
+ puts `gem install #{file}`
43
+ end
44
+
45
+ `asdf shell ruby #{current_ruby}`
46
+ end
47
+
31
48
  desc 'Development version check'
32
49
  task :ver do
33
50
  gver = `git ver`
@@ -0,0 +1,203 @@
1
+ # frozen_string_literal: true
2
+
3
+ class App
4
+ extend GLI::App
5
+ desc 'Move an existing action to a different section'
6
+ long_desc 'Provides an easy way to move an action.
7
+
8
+ If multiple todo files are found in the current directory, a menu will
9
+ allow you to pick which file to act on.'
10
+ arg_name 'ACTION'
11
+ command %i[move] do |c|
12
+ c.example 'na move "A bug in inbox" --to Bugs',
13
+ desc: 'Find "A bug in inbox" action and move it to section Bugs'
14
+
15
+ c.desc 'Prompt for additional notes. Input will be appended to any existing note.
16
+ If STDIN input (piped) is detected, it will be used as a note.'
17
+ c.switch %i[n note], negatable: false
18
+
19
+ c.desc 'Overwrite note instead of appending'
20
+ c.switch %i[o overwrite], negatable: false
21
+
22
+ c.desc 'Move action to specific project. If not provided, a menu will be shown'
23
+ c.arg_name 'PROJECT'
24
+ c.flag %i[to]
25
+
26
+ c.desc 'When moving task, add at [s]tart or [e]nd of target project'
27
+ c.arg_name 'POSITION'
28
+ c.flag %i[at], must_match: /^[sbea].*?$/i
29
+
30
+ c.desc 'Search for actions in a specific project'
31
+ c.arg_name 'PROJECT[/SUBPROJECT]'
32
+ c.flag %i[from]
33
+
34
+ c.desc 'Use a known todo file, partial matches allowed'
35
+ c.arg_name 'TODO_FILE'
36
+ c.flag %i[in todo]
37
+
38
+ c.desc 'Specify the file to search for the task'
39
+ c.arg_name 'PATH'
40
+ c.flag %i[file]
41
+
42
+ c.desc 'Search for files X directories deep'
43
+ c.arg_name 'DEPTH'
44
+ c.flag %i[d depth], must_match: /^[1-9]$/, type: :integer, default_value: 1
45
+
46
+ c.desc 'Include notes in search'
47
+ c.switch %i[search_notes], negatable: true, default_value: true
48
+
49
+ c.desc 'Match actions containing tag. Allows value comparisons'
50
+ c.arg_name 'TAG'
51
+ c.flag %i[tagged], multiple: true
52
+
53
+ c.desc 'Act on all matches immediately (no menu)'
54
+ c.switch %i[all], negatable: false
55
+
56
+ c.desc 'Interpret search pattern as regular expression'
57
+ c.switch %i[e regex], negatable: false
58
+
59
+ c.desc 'Match pattern exactly'
60
+ c.switch %i[x exact], negatable: false
61
+
62
+ c.action do |global_options, options, args|
63
+ reader = TTY::Reader.new
64
+
65
+ args.concat(options[:search]) unless options[:search].nil?
66
+
67
+ append = options[:at] ? options[:at] =~ /^[ae]/i : global_options[:add_at] =~ /^[ae]/i
68
+
69
+ options[:done] = true
70
+
71
+ action = if args.count.positive?
72
+ args.join(' ').strip
73
+ else
74
+ NA.request_input(options, prompt: 'Enter a task to search for')
75
+ end
76
+ if action
77
+ tokens = nil
78
+ if options[:exact]
79
+ tokens = action
80
+ elsif options[:regex]
81
+ tokens = Regexp.new(action, Regexp::IGNORECASE)
82
+ else
83
+ tokens = []
84
+ all_req = action !~ /[+!-]/ && !options[:or]
85
+
86
+ action.split(/ /).each do |arg|
87
+ m = arg.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
88
+ tokens.push({
89
+ token: m['tok'],
90
+ required: all_req || (!m['req'].nil? && m['req'] == '+'),
91
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/ ? true : false
92
+ })
93
+ end
94
+ end
95
+ end
96
+
97
+ if (action.nil? || action.empty?) && options[:tagged].empty?
98
+ NA.notify("#{NA.theme[:error]}Empty input, cancelled", exit_code: 1)
99
+ end
100
+
101
+ all_req = options[:tagged].join(' ') !~ /[+!-]/ && !options[:or]
102
+ tags = []
103
+ options[:tagged].join(',').split(/ *, */).each do |arg|
104
+ m = arg.match(/^(?<req>[+!-])?(?<tag>[^ =<>$~\^]+?) *(?:(?<op>[=<>~]{1,2}|[*$\^]=) *(?<val>.*?))?$/)
105
+
106
+ tags.push({
107
+ tag: m['tag'].wildcard_to_rx,
108
+ comp: m['op'],
109
+ value: m['val'],
110
+ required: all_req || (!m['req'].nil? && m['req'] == '+'),
111
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/ ? true : false
112
+ })
113
+ end
114
+
115
+ stdin_note = NA.stdin ? NA.stdin.split("\n") : []
116
+
117
+ line_note = if options[:note] && $stdin.isatty
118
+ puts stdin_note unless stdin_note.nil?
119
+ if TTY::Which.exist?('gum')
120
+ args = ['--placeholder "Enter a note, CTRL-d to save"']
121
+ args << '--char-limit 0'
122
+ args << '--width $(tput cols)'
123
+ gum = TTY::Which.which('gum')
124
+ `#{gum} write #{args.join(' ')}`.strip.split("\n")
125
+ else
126
+ NA.notify("#{NA.theme[:prompt]}Enter a note, {bw}CTRL-d#{NA.theme[:prompt]} to end editing:#{NA.theme[:action]}")
127
+ reader.read_multiline
128
+ end
129
+ end
130
+
131
+ note = stdin_note.empty? ? [] : stdin_note
132
+ note.concat(line_note) unless line_note.nil? || line_note.empty?
133
+
134
+ if options[:file]
135
+ file = File.expand_path(options[:file])
136
+ NA.notify("#{NA.theme[:error]}File not found", exit_code: 1) unless File.exist?(file)
137
+
138
+ targets = [file]
139
+ elsif options[:todo]
140
+ todo = []
141
+ options[:todo].split(/ *, */).each do |a|
142
+ m = a.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
143
+ todo.push({
144
+ token: m['tok'],
145
+ required: all_req || (!m['req'].nil? && m['req'] == '+'),
146
+ negate: !m['req'].nil? && m['req'] =~ /[!-]/ ? true : false
147
+ })
148
+ end
149
+ dirs = NA.match_working_dir(todo)
150
+
151
+ if dirs.count == 1
152
+ targets = [dirs[0]]
153
+ elsif dirs.count.positive?
154
+ targets = NA.select_file(dirs, multiple: true)
155
+ NA.notify("#{NA.theme[:error]}Cancelled", exit_code: 1) unless targets && targets.count.positive?
156
+ else
157
+ NA.notify("#{NA.theme[:error]}Todo not found", exit_code: 1) unless targets && targets.count.positive?
158
+
159
+ end
160
+ else
161
+ files = NA.find_files_matching({
162
+ depth: options[:depth],
163
+ done: options[:done],
164
+ project: options[:from],
165
+ regex: options[:regex],
166
+ require_na: false,
167
+ search: tokens,
168
+ tag: tags
169
+ })
170
+ NA.notify("#{NA.theme[:error]}No todo file found", exit_code: 1) if files.count.zero?
171
+
172
+ targets = files.count > 1 ? NA.select_file(files, multiple: true) : [files[0]]
173
+ NA.notify("#{NA.theme[:error]}Cancelled", exit_code: 1) unless files.count.positive?
174
+ end
175
+
176
+ target_proj = if options[:to]
177
+ options[:to]
178
+ else
179
+ todo = NA::Todo.new(require_na: false, file_path: targets[0])
180
+ projects = todo.projects
181
+ menu = projects.each_with_object([]) { |proj, arr| arr << proj.project }
182
+
183
+ NA.choose_from(menu, prompt: 'Move to: ', multiple: false, sorted: false)
184
+ end
185
+
186
+ NA.notify("#{NA.theme[:error]}No target selected", exit_code: 1) unless target_proj
187
+
188
+ NA.notify("#{NA.theme[:error]}No search terms provided", exit_code: 1) if tokens.nil? && options[:tagged].empty?
189
+
190
+ targets.each do |target|
191
+ NA.update_action(target, tokens,
192
+ all: options[:all],
193
+ append: append,
194
+ move: target_proj,
195
+ note: note,
196
+ overwrite: options[:overwrite],
197
+ project: options[:from],
198
+ search_note: options[:search_notes],
199
+ tagged: tags)
200
+ end
201
+ end
202
+ end
203
+ end
@@ -425,7 +425,7 @@ module NA
425
425
  end
426
426
 
427
427
  def project_hierarchy(actions)
428
- parents = { actions: []}
428
+ parents = { actions: [] }
429
429
  actions.each do |a|
430
430
  parent = a.parent
431
431
  current_parent = parents
@@ -513,6 +513,7 @@ module NA
513
513
  }
514
514
  opts = defaults.merge(options)
515
515
  files = find_files(depth: options[:depth])
516
+
516
517
  files.delete_if do |file|
517
518
  cmd_options = {
518
519
  depth: options[:depth],
@@ -958,7 +959,7 @@ module NA
958
959
  end
959
960
 
960
961
  return false if res&.strip&.size&.zero?
961
- pp NA::Color.uncolor(NA::Color.template(res))
962
+ # pp NA::Color.uncolor(NA::Color.template(res))
962
963
  multiple ? NA::Color.uncolor(NA::Color.template(res)).split(/\n/) : NA::Color.uncolor(NA::Color.template(res))
963
964
  end
964
965
 
data/lib/na/string.rb CHANGED
@@ -39,7 +39,15 @@ class ::String
39
39
  file = File.expand_path(self)
40
40
  raise "Missing file #{file}" unless File.exist?(file)
41
41
 
42
- NA.notify("#{NA.theme[:error]}#{file} is a directory", exit_code: 2) if File.directory?(file)
42
+ if File.directory?(file)
43
+ if File.exist?("#{file}.#{NA.extension}")
44
+ file = "#{file}.#{NA.extension}"
45
+ elsif File.exist?("#{file}/#{File.basename(file)}.#{NA.extension}")
46
+ file = "#{file}/#{File.basename(file)}.#{NA.extension}"
47
+ else
48
+ NA.notify("#{NA.theme[:error]}#{file} is a directory", exit_code: 2)
49
+ end
50
+ end
43
51
 
44
52
  # IO.read(file).force_encoding('ASCII-8BIT').encode('UTF-8', invalid: :replace, undef: :replace, replace: '?')
45
53
  IO.read(file).force_encoding('utf-8')
data/lib/na/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Na
2
- VERSION = '1.2.63'
2
+ VERSION = '1.2.65'
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.62<!--END VER-->.
12
+ The current version of `na` is <!--VER-->1.2.63<!--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
 
@@ -112,6 +112,20 @@ Unless `--exact` is specified, search is tokenized and combined with AND, so `na
112
112
  @cli(bundle exec bin/na help init)
113
113
  ```
114
114
 
115
+ ##### move
116
+
117
+ Move an action between projects. Argument is a search term, if left blank a prompt will allow you to enter terms. If no `--to` project is specified, a menu will be shown of projects in the target file.
118
+
119
+ Examples:
120
+
121
+ - `na move` (enter a search term, select a file/destination)
122
+ - `na move "Bug description"` (find matching action and show a menu of project destinations)
123
+ - `na move "Bug description" --to Bugs (move matching action to Bugs project)
124
+
125
+ ```
126
+ @cli(bundle exec bin/na help move)
127
+ ```
128
+
115
129
  ##### next, show
116
130
 
117
131
  Examples:
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: na
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.63
4
+ version: 1.2.65
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-14 00:00:00.000000000 Z
11
+ date: 2024-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -199,7 +199,6 @@ extra_rdoc_files:
199
199
  - na.rdoc
200
200
  files:
201
201
  - ".travis.yml"
202
- - '0'
203
202
  - CHANGELOG.md
204
203
  - Gemfile
205
204
  - Gemfile.lock
@@ -216,6 +215,7 @@ files:
216
215
  - bin/commands/edit.rb
217
216
  - bin/commands/find.rb
218
217
  - bin/commands/init.rb
218
+ - bin/commands/move.rb
219
219
  - bin/commands/next.rb
220
220
  - bin/commands/open.rb
221
221
  - bin/commands/projects.rb
@@ -276,7 +276,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
276
276
  - !ruby/object:Gem::Version
277
277
  version: '0'
278
278
  requirements: []
279
- rubygems_version: 3.4.0.dev
279
+ rubygems_version: 3.5.11
280
280
  signing_key:
281
281
  specification_version: 4
282
282
  summary: A command line tool for adding and listing project todos
data/0 DELETED
File without changes