na 1.1.9 → 1.1.11

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: e1c1a61c33eaccbf930903d5adbc75fd4ef717b31bdb21539240535d03b71922
4
- data.tar.gz: 1c1067564d9e0e8452512cf146ff9443ecc8a61456ed1d1d46dc5a76c5cf2280
3
+ metadata.gz: b309ba145cf0859f1a9011cf62268261766338d79cb956810fab9c64863c37f2
4
+ data.tar.gz: 6fc7e065d4d85bf70b9122634414274e6a44d640ffffeeee7409e9102115f11d
5
5
  SHA512:
6
- metadata.gz: a39268399afd4fdb653b5578f729967fca4c4313b1c0698e42b714d775257cbdae511380c895066ef7a399dc0637f806cc6890d6664919244cbacbca23a2182b
7
- data.tar.gz: 7cc6f8938f297e23352d9b5d5bdfa6d3727d316cd6508dc29cf829e8a68939c30964e40036e04624921e157ccbcc45df2e71fd426a06d1e80d59cdcd8b80d98a
6
+ metadata.gz: 63ee0b1ac9370533379956e8cd5f6571c8d39731c12c02404b2912d5cb97f49d32c988e4d23c12c50d1b2080412296bc58351680122abd26281580c49d93f722
7
+ data.tar.gz: b7500123c7ea092acc42ccba0873942fe871bd201405e3ef2faa66f3d155c3d0ac60b530078d962114a4d0d66bdb8ea427b1452873c14e2b06419cb2675e822e
data/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ ### 1.1.11
2
+
3
+ 2022-10-05 08:56
4
+
5
+ #### IMPROVED
6
+
7
+ - Respect na_tag setting when creating new todo file
8
+ - Code cleanup
9
+
10
+ ### 1.1.10
11
+
12
+ 2022-10-05 08:19
13
+
14
+ #### FIXED
15
+
16
+ - When adding a project, don't use Ruby #capitalize, which downcases the rest of the project name
17
+
1
18
  ### 1.1.9
2
19
 
3
20
  2022-10-03 12:08
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- na (1.1.9)
4
+ na (1.1.11)
5
5
  gli (~> 2.21.0)
6
6
  tty-reader (~> 0.9, >= 0.9.0)
7
7
  tty-screen (~> 0.8, >= 0.8.1)
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.1.9
12
+ The current version of `na` is 1.1.11
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.
@@ -37,7 +37,7 @@ You can list next actions in files in the current directory by typing `na`. By d
37
37
 
38
38
  #### Easy matching
39
39
 
40
- `na` features intelligent project matching. Every time it locates a todo file, it adds the project to the database. Once a project is recorded, you can list its actions by using any portion of the parent directories or file names. If your project is in `~/Sites/dev/markedapp`, you could quickly list its next actions by typing `na dev mark`. It will always look for the shortest match.
40
+ `na` features intelligent project matching. Every time it locates a todo file, it adds the project to the database. Once a project is recorded, you can list its actions by using any portion of the parent directories or file names. If your project is in `~/Sites/dev/markedapp`, you could quickly list its next actions by typing `na next dev mark`. It will always look for the shortest match.
41
41
 
42
42
  #### Recursion
43
43
 
data/bin/na CHANGED
@@ -1,8 +1,11 @@
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 'na'
5
7
 
8
+ # Main application
6
9
  class App
7
10
  extend GLI::App
8
11
 
@@ -123,7 +126,7 @@ class App
123
126
 
124
127
  c.desc 'Add action to specific project'
125
128
  c.default_value 'Inbox'
126
- c.flag %[to]
129
+ c.flag %i[to]
127
130
 
128
131
  c.desc 'Use a tag other than the default next action tag'
129
132
  c.arg_name 'TAG'
@@ -136,7 +139,7 @@ class App
136
139
  c.arg_name 'PATH'
137
140
  c.flag %i[f file]
138
141
 
139
- c.action do |global_options, options, args|
142
+ c.action do |_global_options, options, args|
140
143
  reader = TTY::Reader.new
141
144
  action = if args.count.positive?
142
145
  args.join(' ').strip
@@ -168,7 +171,10 @@ class App
168
171
 
169
172
  note = if options[:note]
170
173
  if TTY::Which.exist?('gum')
171
- `gum write --placeholder "Enter a note, CTRL-d to save" --width $(tput cols) --char-limit 0`.strip.split("\n")
174
+ args = ['--placeholder "Enter a note, CTRL-d to save"']
175
+ args << '--char-limit 0'
176
+ args << '--width $(tput cols)'
177
+ `gum write #{args.join(' ')}`.strip.split("\n")
172
178
  else
173
179
  puts NA::Color.template('{bm}Enter a note, {bw}CTRL-d{bm} to end editing{bw}')
174
180
  reader.read_multiline
@@ -190,7 +196,7 @@ class App
190
196
  end
191
197
  else
192
198
  files = NA.find_files(depth: 1)
193
- if files.count == 0
199
+ if files.count.zero?
194
200
  print NA::Color.template('{by}No todo file found, create one? {w}(y/{g}N{w}){x} ')
195
201
  res = reader.read_char
196
202
  if res =~ /y/i
@@ -256,10 +262,10 @@ class App
256
262
  end
257
263
  end
258
264
 
259
-
260
265
  desc 'Find actions matching a tag'
261
266
  long_desc 'Finds actions with tags matching the arguments. An action is shown if it
262
- contains any of the tags listed. Add a + before a tag to make it required. You can specify values using TAG=VALUE pairs.'
267
+ contains any of the tags listed. Add a + before a tag to make it required.
268
+ You can specify values using TAG=VALUE pairs.'
263
269
  arg_name 'TAG [VALUE]'
264
270
  command %i[tagged] do |c|
265
271
  c.example 'na tagged +maybe', desc: 'Show all actions tagged @maybe'
@@ -304,7 +310,7 @@ class App
304
310
  c.example 'na init', desc: 'Generate a new todo file, prompting for project name'
305
311
  c.example 'na init warpspeed', desc: 'Generate a new todo for a project called warpspeed'
306
312
 
307
- c.action do |global_options, _, args|
313
+ c.action do |_global_options, _options, args|
308
314
  reader = TTY::Reader.new
309
315
  if args.count.positive?
310
316
  project = args.join(' ')
@@ -346,7 +352,7 @@ class App
346
352
  c.arg_name 'EDITOR'
347
353
  c.flag %i[a app]
348
354
 
349
- c.action do |global_options, options, args|
355
+ c.action do |global_options, options, _args|
350
356
  depth = if global_options[:recurse] && options[:depth].nil? && global_options[:depth] == 1
351
357
  3
352
358
  else
@@ -371,15 +377,16 @@ class App
371
377
  long_desc 'Installing the prompt hook allows you to automatically
372
378
  list next actions when you cd into a directory'
373
379
  command %i[prompt] do |c|
374
- c.desc 'Output the prompt hook for the current shell to STDOUT. Pass an argument to specify a shell (zsh, bash, fish)'
380
+ c.desc 'Output the prompt hook for the current shell to STDOUT. Pass an argument to
381
+ specify a shell (zsh, bash, fish)'
375
382
  c.arg_name '[SHELL]'
376
383
  c.command %i[show] do |s|
377
- s.action do |global_options, options, args|
378
- if args.count.positive?
379
- shell = args[0]
380
- else
381
- shell = File.basename(ENV['SHELL'])
382
- end
384
+ s.action do |_global_options, _options, args|
385
+ shell = if args.count.positive?
386
+ args[0]
387
+ else
388
+ File.basename(ENV['SHELL'])
389
+ end
383
390
 
384
391
  case shell
385
392
  when /^f/i
@@ -395,12 +402,12 @@ class App
395
402
  c.desc 'Install the hook for the current shell to the appropriate startup file.'
396
403
  c.arg_name '[SHELL]'
397
404
  c.command %i[install] do |s|
398
- s.action do |global_options, options, args|
399
- if args.count.positive?
400
- shell = args[0]
401
- else
402
- shell = File.basename(ENV['SHELL'])
403
- end
405
+ s.action do |_global_options, _options, args|
406
+ shell = if args.count.positive?
407
+ args[0]
408
+ else
409
+ File.basename(ENV['SHELL'])
410
+ end
404
411
 
405
412
  case shell
406
413
  when /^f/i
@@ -414,7 +421,7 @@ class App
414
421
  end
415
422
  end
416
423
 
417
- pre do |global, command, options, args|
424
+ pre do |global, _command, _options, _args|
418
425
  NA.verbose = global[:debug]
419
426
  NA.extension = global[:ext]
420
427
  NA.na_tag = global[:na_tag]
@@ -432,9 +439,7 @@ class App
432
439
  case exception
433
440
  when GLI::UnknownCommand
434
441
  cmd = ['add']
435
- if ARGV.count.positive?
436
- cmd.concat(ARGV.unshift($first_arg))
437
- end
442
+ cmd.concat(ARGV.unshift($first_arg)) if ARGV.count.positive?
438
443
 
439
444
  exit run(cmd)
440
445
  when SystemExit
@@ -5,10 +5,17 @@ module NA
5
5
  class << self
6
6
  attr_accessor :verbose, :extension, :na_tag
7
7
 
8
+ def notify(msg, exit_code: false)
9
+ $stderr.puts NA::Color.template("{x}#{msg}{x}")
10
+ if exit_code && exit_code.is_a?(Number)
11
+ Process.exit exit_code
12
+ end
13
+ end
14
+
8
15
  def create_todo(target, basename)
9
16
  File.open(target, 'w') do |f|
10
17
  content = <<~ENDCONTENT
11
- Inbox: @inbox
18
+ Inbox:
12
19
  #{basename}:
13
20
  \tFeature Requests:
14
21
  \tIdeas:
@@ -18,11 +25,11 @@ module NA
18
25
  \tTop Priority @search(@priority = 5 and not @done)
19
26
  \tHigh Priority @search(@priority > 3 and not @done)
20
27
  \tMaybe @search(@maybe)
21
- \tNext @search(@na and not @done and not project = \"Archive\")
28
+ \tNext @search(@#{NA.na_tag} and not @done and not project = \"Archive\")
22
29
  ENDCONTENT
23
30
  f.puts(content)
24
31
  end
25
- $stderr.puts NA::Color.template("{y}Created {bw}#{target}{x}")
32
+ notify("{y}Created {bw}#{target}")
26
33
  end
27
34
 
28
35
  def find_files(depth: 1)
@@ -41,8 +48,7 @@ module NA
41
48
  elsif TTY::Which.exist?('fzf')
42
49
  res = choose_from(files, prompt: 'Use which file?')
43
50
  unless res
44
- $stderr.puts 'No file selected, cancelled'
45
- Process.exit 1
51
+ notify('{r}No file selected, cancelled', exit_code: 1)
46
52
  end
47
53
 
48
54
  res.strip
@@ -60,18 +66,18 @@ module NA
60
66
  def add_action(file, project, action, note = nil)
61
67
  content = IO.read(file)
62
68
  unless content =~ /^[ \t]*#{project}:/i
63
- content = "#{project.capitalize}:\n#{content}"
69
+ content = "#{project.cap_first}:\n#{content}"
64
70
  end
65
71
 
66
72
  content.sub!(/^([ \t]*)#{project}:(.*?)$/i) do
67
73
  m = Regexp.last_match
68
74
  note = note.nil? ? '' : "\n#{m[1]}\t\t#{note.join('').strip}"
69
- "#{m[1]}#{project.capitalize}:#{m[2]}\n#{m[1]}\t- #{action}#{note}"
75
+ "#{m[1]}#{project.cap_first}:#{m[2]}\n#{m[1]}\t- #{action}#{note}"
70
76
  end
71
77
 
72
78
  File.open(file, 'w') { |f| f.puts content }
73
79
 
74
- $stderr.puts NA::Color.template("{by}Task added to {bw}#{file}{x}")
80
+ notify("{by}Task added to {bw}#{file}")
75
81
  end
76
82
 
77
83
  def output_actions(actions, depth, files: nil)
@@ -91,7 +97,7 @@ module NA
91
97
  '%parent%action'
92
98
  end
93
99
  if files && @verbose
94
- $stderr.puts files.map { |f| NA::Color.template("{dw}#{f}{x}") }
100
+ files.map { |f| notify("{dw}#{f}") }
95
101
  end
96
102
 
97
103
  puts actions.map { |action| action.pretty(template: { output: template }) }
@@ -188,11 +194,7 @@ module NA
188
194
  def choose_from(options, prompt: 'Make a selection: ', multiple: false, sorted: true, fzf_args: [])
189
195
  return nil unless $stdout.isatty
190
196
 
191
- # fzf_args << '-1' # User is expecting a menu, and even if only one it seves as confirmation
192
- default_args = []
193
- default_args << %(--prompt="#{prompt}")
194
- default_args << "--height=#{options.count + 2}"
195
- default_args << '--info=inline'
197
+ default_args = [%(--prompt="#{prompt}"), "--height=#{options.count + 2}", '--info=inline']
196
198
  default_args << '--multi' if multiple
197
199
  header = "esc: cancel,#{multiple ? ' tab: multi-select, ctrl-a: select all,' : ''} return: confirm"
198
200
  default_args << %(--header="#{header}")
@@ -205,33 +207,50 @@ module NA
205
207
  res
206
208
  end
207
209
 
210
+ ##
211
+ ## Get path to database of known todo files
212
+ ##
213
+ ## @return [String] File path
214
+ ##
208
215
  def database_path
209
216
  db_dir = File.expand_path('~/.local/share/na')
217
+ # Create directory if needed
210
218
  FileUtils.mkdir_p(db_dir) unless File.directory?(db_dir)
211
219
  db_file = 'tdlist.txt'
212
220
  File.join(db_dir, db_file)
213
221
  end
214
222
 
215
- def match_working_dir(search)
223
+ ##
224
+ ## Find a matching path using semi-fuzzy matching.
225
+ ## Search tokens can include ! and + to negate or make
226
+ ## required.
227
+ ##
228
+ ## @param search [Array] search tokens to match
229
+ ## @param distance [Integer] allowed distance
230
+ ## between characters
231
+ ##
232
+ def match_working_dir(search, distance: 1)
216
233
  optional = []
217
234
  required = []
218
235
 
219
236
  search&.each do |t|
220
- new_rx = t[:token].to_s.split('').join('.{0,1}')
237
+ # Make "search" into "s.{0,1}e.{0,1}a.{0,1}r.{0,1}c.{0,1}h"
238
+ new_rx = t[:token].to_s.split('').join(".{0,#{distance}}")
221
239
 
222
240
  optional.push(new_rx)
223
241
  required.push(new_rx) if t[:required]
224
242
  end
225
243
 
244
+ match_dir(optional, required)
245
+ end
246
+
247
+ def match_dir(optional, required)
226
248
  file = database_path
227
- if File.exist?(file)
228
- dirs = IO.read(file).split("\n")
229
- dirs.delete_if { |d| !d.matches(any: optional, all: required) }
230
- dirs.sort.uniq
231
- else
232
- $stderr.puts NA::Color.template('{r}No na database found{x}')
233
- Process.exit 1
234
- end
249
+ notify('{r}No na database found', exit_code: 1) unless File.exist?(file)
250
+
251
+ dirs = IO.read(file).split("\n")
252
+ dirs.delete_if { |d| !d.matches(any: optional, all: required) }
253
+ dirs.sort.uniq
235
254
  end
236
255
 
237
256
  def save_working_dir(todo_file)
@@ -258,6 +277,26 @@ module NA
258
277
  os_open(file, app: app) if file && File.exist?(file)
259
278
  end
260
279
 
280
+ def darwin_open(file, app: nil)
281
+ if app
282
+ `open -a "#{app}" #{Shellwords.escape(file)}`
283
+ else
284
+ `open #{Shellwords.escape(file)}`
285
+ end
286
+ end
287
+
288
+ def win_open(file)
289
+ `start #{Shellwords.escape(file)}`
290
+ end
291
+
292
+ def linux_open(file)
293
+ if TTY::Which.exist?('xdg-open')
294
+ `xdg-open #{Shellwords.escape(file)}`
295
+ else
296
+ notify('{r}Unable to determine executable for `xdg-open`.')
297
+ end
298
+ end
299
+
261
300
  ##
262
301
  ## Platform-agnostic open command
263
302
  ##
@@ -267,19 +306,11 @@ module NA
267
306
  os = RbConfig::CONFIG['target_os']
268
307
  case os
269
308
  when /darwin.*/i
270
- if app
271
- `open -a "#{app}" #{Shellwords.escape(file)}`
272
- else
273
- `open #{Shellwords.escape(file)}`
274
- end
309
+ darwin_open(file, app: app)
275
310
  when /mingw|mswin/i
276
- `start #{Shellwords.escape(file)}`
311
+ win_open(file)
277
312
  else
278
- if 'xdg-open'.available?
279
- `xdg-open #{Shellwords.escape(file)}`
280
- else
281
- $stderr.puts NA::Color.template('{r}Unable to determine executable for `open`.{x}')
282
- end
313
+ linux_open(file)
283
314
  end
284
315
  end
285
316
  end
data/lib/na/string.rb CHANGED
@@ -48,4 +48,15 @@ class ::String
48
48
  end
49
49
  true
50
50
  end
51
+
52
+ def cap_first!
53
+ replace cap_first
54
+ end
55
+
56
+ def cap_first
57
+ sub(/^([a-z])(.*)$/) do
58
+ m = Regexp.last_match
59
+ m[1].upcase << m[2]
60
+ end
61
+ end
51
62
  end
data/lib/na/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Na
2
- VERSION = '1.1.9'
2
+ VERSION = '1.1.11'
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.1.8<!--END VER-->.
12
+ The current version of `na` is <!--VER-->1.1.10<!--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
 
@@ -36,7 +36,7 @@ You can list next actions in files in the current directory by typing `na`. By d
36
36
 
37
37
  #### Easy matching
38
38
 
39
- `na` features intelligent project matching. Every time it locates a todo file, it adds the project to the database. Once a project is recorded, you can list its actions by using any portion of the parent directories or file names. If your project is in `~/Sites/dev/markedapp`, you could quickly list its next actions by typing `na dev mark`. It will always look for the shortest match.
39
+ `na` features intelligent project matching. Every time it locates a todo file, it adds the project to the database. Once a project is recorded, you can list its actions by using any portion of the parent directories or file names. If your project is in `~/Sites/dev/markedapp`, you could quickly list its next actions by typing `na next dev mark`. It will always look for the shortest match.
40
40
 
41
41
  #### Recursion
42
42
 
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.1.9
4
+ version: 1.1.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-10-03 00:00:00.000000000 Z
11
+ date: 2022-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake