na 1.2.34 → 1.2.35

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: 4b14bfb4de93f7d04ffbb7685426941e279da422d00802c4320586c85f08e14b
4
- data.tar.gz: 7a3e6c43f2118bbb5260e2d43e1fe135bc07db8f44489a47068b7733e8b57ec2
3
+ metadata.gz: 1f8982f5720115576a78ab96a430d1d83f501bb1b477b2cff1a694648a186afc
4
+ data.tar.gz: 86dd520a3265473471b6d09fc05136dd07b53ded24ab2c82936074fa50595b67
5
5
  SHA512:
6
- metadata.gz: b9fbce0504e8a40bf1a9961fcde1c538135ed37d0ced7bc491db3158d8bdd2be01b118f3102a148e0ff9df465e9210fd45fbe82ae14e2400a1b01d0300fbac9d
7
- data.tar.gz: 1715ad409675baca29123792064f4729d2d65123a5b0ac11343f165cb427bdf704f2fc810f41e91bf1847852108d5eda3de89d990bcd43621f22377fef77a896
6
+ metadata.gz: 7ea9bb861c82251c1ec52925d6527f0e4efa1eef0175d268b8a8bb307fafcc407dc888e5e5cd6d588e5e4a500a96ae64765106f23d9c4acda6e0667c71247d62
7
+ data.tar.gz: e5f603160851411f66dd50ae47a65369aaa1e51352cf2eb6313674d95d64db8914d3a870f816dcc4303fbd5b4e675f213020fa6a7fd93805af4692f04eb99aed
data/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ ### 1.2.35
2
+
3
+ 2023-08-30 11:59
4
+
5
+ #### IMPROVED
6
+
7
+ - If a search string contains @tags and --exact or --regex isn't specified, the @tags will be extracted and passed as a --tagged search.
8
+ - Tag handling (including values and comparisons) in tags extracted from search strings
9
+ - `na complete --project PROJ` flag to move to a specific project
10
+ - `na restore --project PROJ` flag to move restored action to
11
+ - Exit gracefully if tagged command is run with invalid
12
+ - Display action affected when using update command
13
+
14
+ #### FIXED
15
+
16
+ - Escape search for tokens to allow parenthesis and other
17
+
1
18
  ### 1.2.34
2
19
 
3
20
  2023-08-30 09:22
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- na (1.2.34)
4
+ na (1.2.35)
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.34
12
+ The current version of `na` is 1.2.35
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.34
80
+ 1.2.35
81
81
 
82
82
  GLOBAL OPTIONS
83
83
  -a, --add - Add a next action (deprecated, for backwards compatibility)
@@ -104,7 +104,7 @@ COMMANDS
104
104
  complete, finish - Find and mark an action as @done
105
105
  completed, finished - Display completed actions
106
106
  edit - Edit an existing action
107
- find, grep - Find actions matching a search pattern
107
+ find, grep, search - Find actions matching a search pattern
108
108
  help - Shows a list of commands or help for one command
109
109
  init, create - Create a new todo file in the current directory
110
110
  initconfig - Initialize the config file using current global options
@@ -20,6 +20,10 @@ class App
20
20
  c.desc 'Add a @done tag to action and move to Archive'
21
21
  c.switch %i[a archive], negatable: false
22
22
 
23
+ c.desc 'Move action to specific project'
24
+ c.arg_name 'PROJECT'
25
+ c.flag %i[to project proj]
26
+
23
27
  c.desc 'Specify the file to search for the task'
24
28
  c.arg_name 'PATH'
25
29
  c.flag %i[file]
@@ -44,7 +48,7 @@ class App
44
48
  c.action do |global, options, args|
45
49
  options[:finish] = true
46
50
  options[:f] = true
47
- options[:project] = 'Archive' if options[:archive]
51
+ options[:project] = 'Archive' if options[:archive] && !options[:project]
48
52
 
49
53
  cmd = commands[:update]
50
54
  action = cmd.send(:get_action, nil)
data/bin/commands/find.rb CHANGED
@@ -7,7 +7,7 @@ class App
7
7
  (partial matches allowed). Add a + before a token to make it required, e.g. `na find +feature +maybe`,
8
8
  add a - or ! to ignore matches containing that token.'
9
9
  arg_name 'PATTERN'
10
- command %i[find grep] do |c|
10
+ command %i[find grep search] do |c|
11
11
  c.example 'na find feature idea swift', desc: 'Find all actions containing feature, idea, and swift'
12
12
  c.example 'na find feature idea -swift', desc: 'Find all actions containing feature and idea but NOT swift'
13
13
  c.example 'na find -x feature idea', desc: 'Find all actions containing the exact text "feature idea"'
@@ -64,17 +64,33 @@ class App
64
64
  NA.save_search(title, "#{NA.command_line.join(' ').sub(/ --save[= ]*\S+/, '').split(' ').map { |t| %("#{t}") }.join(' ')}")
65
65
  end
66
66
 
67
-
68
67
  depth = if global_options[:recurse] && options[:depth].nil? && global_options[:depth] == 1
69
68
  3
70
69
  else
71
70
  options[:depth].nil? ? global_options[:depth].to_i : options[:depth].to_i
72
71
  end
73
72
 
73
+ if options[:exact] || options[:regex]
74
+ search = args.join(' ')
75
+ else
76
+ search = args.join(' ').gsub(/(?<=\A|[ ,])(?<req>[+\-!])?@(?<tag>[^ *=<>$\^,@(]+)(?:\((?<value>.*?)\)| *(?<op>[=<>]{1,2}|[*$\^]=) *(?<val>.*?(?=\Z|[,@])))?/) do |arg|
77
+ m = Regexp.last_match
78
+ string = if m['value']
79
+ "#{m['req']}#{m['tag']}=#{m['value']}"
80
+ else
81
+ m[0]
82
+ end
83
+ options[:tagged] << string.sub(/@/, '')
84
+ ''
85
+ end
86
+ end
87
+
88
+ search = search.gsub(/ +/, ' ').strip
89
+
74
90
  all_req = options[:tagged].join(' ') !~ /[+!\-]/ && !options[:or]
75
91
  tags = []
76
92
  options[:tagged].join(',').split(/ *, */).each do |arg|
77
- m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>$\^]+?)(?:(?<op>[=<>]{1,2}|[*$\^]=)(?<val>.*?))?$/)
93
+ m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>$\^]+?) *(?:(?<op>[=<>]{1,2}|[*$\^]=) *(?<val>.*?))?$/)
78
94
 
79
95
  tags.push({
80
96
  tag: m['tag'].wildcard_to_rx,
@@ -85,19 +101,23 @@ class App
85
101
  })
86
102
  end
87
103
 
104
+ search_for_done = false
105
+ tags.each { |tag| search_for_done = true if tag[:tag] =~ /done/ }
106
+ options[:done] = true if search_for_done
107
+
88
108
  tokens = nil
89
109
  if options[:exact]
90
- tokens = args.join(' ')
110
+ tokens = search
91
111
  elsif options[:regex]
92
- tokens = Regexp.new(args.join(' '), Regexp::IGNORECASE)
112
+ tokens = Regexp.new(search, Regexp::IGNORECASE)
93
113
  else
94
114
  tokens = []
95
- all_req = args.join(' ') !~ /[+!\-]/ && !options[:or]
115
+ all_req = search !~ /[+!\-]/ && !options[:or]
96
116
 
97
- args.join(' ').split(/ /).each do |arg|
117
+ search.split(/ /).each do |arg|
98
118
  m = arg.match(/^(?<req>[+\-!])?(?<tok>.*?)$/)
99
119
  tokens.push({
100
- token: m['tok'],
120
+ token: Regexp.escape(m['tok']),
101
121
  required: all_req || (!m['req'].nil? && m['req'] == '+'),
102
122
  negate: !m['req'].nil? && m['req'] =~ /[!\-]/
103
123
  })
@@ -17,6 +17,10 @@ class App
17
17
  c.desc 'Overwrite note instead of appending'
18
18
  c.switch %i[o overwrite], negatable: false
19
19
 
20
+ c.desc 'Move action to specific project'
21
+ c.arg_name 'PROJECT'
22
+ c.flag %i[to project proj]
23
+
20
24
  c.desc 'Specify the file to search for the task'
21
25
  c.arg_name 'PATH'
22
26
  c.flag %i[file]
@@ -43,7 +47,7 @@ class App
43
47
  options[:done] = true
44
48
  options[:finish] = false
45
49
  options[:f] = false
46
-
50
+
47
51
  cmd = commands[:update]
48
52
  action = cmd.send(:get_action, nil)
49
53
  action.call(global, options, args)
@@ -80,10 +80,11 @@ class App
80
80
 
81
81
  all_req = args.join(' ') !~ /[+!\-]/ && !options[:or]
82
82
  args.join(',').split(/ *, */).each do |arg|
83
- m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>$\^]+?)(?:(?<op>[=<>]{1,2}|[*$\^]=)(?<val>.*?))?$/)
83
+ m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>$\^]+?) *(?:(?<op>[=<>]{1,2}|[*$\^]=) *(?<val>.*?))?$/)
84
+ next if m.nil?
84
85
 
85
86
  tags.push({
86
- tag: m['tag'].wildcard_to_rx,
87
+ tag: m['tag'].sub(/^@/, '').wildcard_to_rx,
87
88
  comp: m['op'],
88
89
  value: m['val'],
89
90
  required: all_req || (!m['req'].nil? && m['req'] == '+'),
@@ -93,7 +94,8 @@ class App
93
94
 
94
95
  search_for_done = false
95
96
  tags.each { |tag| search_for_done = true if tag[:tag] =~ /done/ }
96
- tags.push({ tag: 'done', value: nil, negate: true}) unless search_for_done
97
+ tags.push({ tag: 'done', value: nil, negate: true}) unless search_for_done || options[:done]
98
+ options[:done] = true if search_for_done
97
99
 
98
100
  tokens = nil
99
101
  if options[:search]
@@ -129,6 +131,8 @@ class App
129
131
  end
130
132
  end
131
133
 
134
+ NA.notify('{br}No actions matched search', exit_code: 1) if tags.empty? && tokens.empty?
135
+
132
136
  files, actions, = NA.parse_actions(depth: depth,
133
137
  done: options[:done],
134
138
  query: todo,
data/lib/na/action.rb CHANGED
@@ -37,7 +37,18 @@ module NA
37
37
  EOINSPECT
38
38
  end
39
39
 
40
+ ##
41
+ ## Pretty print an action
42
+ ##
43
+ ## @param extension [String] The file extension
44
+ ## @param template [Hash] The template to use for
45
+ ## colorization
46
+ ## @param regexes [Array] The regexes to
47
+ ## highlight (searches)
48
+ ## @param notes [Boolean] Include notes
49
+ ##
40
50
  def pretty(extension: 'taskpaper', template: {}, regexes: [], notes: false)
51
+ # Default colorization, can be overridden with full or partial template variable
41
52
  default_template = {
42
53
  file: '{xbk}',
43
54
  parent: '{c}',
@@ -51,32 +62,38 @@ module NA
51
62
  note: '{dw}'
52
63
  }
53
64
  template = default_template.merge(template)
54
-
65
+ # Create the hierarchical parent string
55
66
  parents = @parent.map do |par|
56
67
  NA::Color.template("#{template[:parent]}#{par}")
57
68
  end.join(NA::Color.template(template[:parent_divider]))
58
69
  parents = "{dc}[{x}#{parents}{dc}]{x} "
59
70
 
71
+ # Create the project string
60
72
  project = NA::Color.template("#{template[:project]}#{@project}{x} ")
61
73
 
74
+ # Create the source filename string, substituting ~ for HOME and removing extension
62
75
  file = @file.sub(%r{^\./}, '').sub(/#{ENV['HOME']}/, '~')
63
76
  file = file.sub(/\.#{extension}$/, '')
77
+ # colorize the basename
64
78
  file = file.sub(/#{File.basename(@file, ".#{extension}")}$/, "{dw}#{File.basename(@file, ".#{extension}")}{x}")
65
79
  file_tpl = "#{template[:file]}#{file} {x}"
66
80
  filename = NA::Color.template(file_tpl)
67
81
 
82
+ # Add notes if needed
68
83
  note = if notes && @note.count.positive?
69
84
  NA::Color.template("\n#{@note.map { |l| " #{template[:note]}• #{l}{x}" }.join("\n")}")
70
85
  else
71
86
  ''
72
87
  end
73
88
 
89
+ # colorize the action and highlight tags
74
90
  action = NA::Color.template("#{template[:action]}#{@action.sub(/ @#{NA.na_tag}\b/, '')}{x}")
75
91
  action = action.highlight_tags(color: template[:tags],
76
92
  parens: template[:value_parens],
77
93
  value: template[:values],
78
94
  last_color: template[:action])
79
95
 
96
+ # Replace variables in template string and output colorized
80
97
  NA::Color.template(template[:output].gsub(/%filename/, filename)
81
98
  .gsub(/%project/, project)
82
99
  .gsub(/%parents?/, parents)
@@ -420,7 +420,7 @@ module NA
420
420
  projects.select { |proj| proj.project =~ /^#{action.parent.join(':')}$/ }.first
421
421
  end
422
422
 
423
- NA.notify("{r}Error parsing project #{target_proj}", exit_code: 1) if target_proj.nil?
423
+ NA.notify("{r}Error parsing project #{target}", exit_code: 1) if target_proj.nil?
424
424
 
425
425
  indent = "\t" * target_proj.indent
426
426
  note = note.split("\n") unless note.is_a?(Array)
@@ -450,6 +450,8 @@ module NA
450
450
  end
451
451
 
452
452
  contents.insert(target_line, "#{indent}\t- #{action.action}#{note}")
453
+
454
+ notify(action.pretty)
453
455
  else
454
456
  _, actions = find_actions(target, search, tagged, done: done, all: all)
455
457
 
@@ -502,7 +504,10 @@ module NA
502
504
  target_line = target_proj.line + 1
503
505
  end
504
506
 
507
+
505
508
  contents.insert(target_line, "#{indent}\t- #{action.action}#{note}")
509
+
510
+ notify(action.pretty)
506
511
  end
507
512
  end
508
513
 
@@ -692,6 +697,9 @@ module NA
692
697
  negated_tag = []
693
698
  projects = []
694
699
 
700
+ notify("{dw}Tags: #{tag}", debug:true)
701
+ notify("{dw}Search: #{search}", debug:true)
702
+
695
703
  tag&.each do |t|
696
704
  unless t[:tag].nil?
697
705
  if negate
@@ -706,7 +714,7 @@ module NA
706
714
  end
707
715
  end
708
716
 
709
- unless search.nil?
717
+ unless search.nil? || search.empty?
710
718
  if regex || search.is_a?(String)
711
719
  if negate
712
720
  negated.push(search)
data/lib/na/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Na
2
- VERSION = '1.2.34'
2
+ VERSION = '1.2.35'
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.33<!--END VER-->.
12
+ The current version of `na` is <!--VER-->1.2.34<!--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
 
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.34
4
+ version: 1.2.35
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra