na 1.1.11 → 1.1.13
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 +4 -4
 - data/CHANGELOG.md +27 -0
 - data/Gemfile.lock +1 -1
 - data/README.md +1 -1
 - data/bin/na +44 -8
 - data/lib/na/action.rb +103 -1
 - data/lib/na/hash.rb +7 -0
 - data/lib/na/next_action.rb +95 -59
 - data/lib/na/string.rb +13 -0
 - data/lib/na/version.rb +1 -1
 - data/lib/na.rb +8 -8
 - data/src/README.md +1 -1
 - metadata +3 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 03c240ef72b49cbb692ba1f55da9a36036d72bd51bb29549ea87262cceedfd51
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: e61a921f6af384ae5615f54116cb4e4e0c9472ab76d4a347fc50ea5633fc80a3
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: e577876361f3022e29d4c56e06d7c507fe05ac81da752debe476b481f92f671ee85ab25cb06e32a0608baf1b926454ea4e68aa6391e32ee7c5e7e02a5751dd11
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 660039f2b13e23365ffa88fa8a59266f50941275830175458f867a26bfcbcd3553c8e5dc6ef70da1d1ab5946fc9d7a3361bdf932f8d63eda23c3c692657a5074
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,3 +1,30 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ### 1.1.13
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            2022-10-06 06:28
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            #### IMPROVED
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            - When specifying arguments to `next`, allow paths separated by / to do more exact matching
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            ### 1.1.12
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            2022-10-06 05:42
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            #### NEW
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            - `na add -d X` to allow adding new actions to todo files in subdirectories
         
     | 
| 
      
 16 
     | 
    
         
            +
            - You can now perform <>= queries on tag values (`na tagged "priority>=3"`)
         
     | 
| 
      
 17 
     | 
    
         
            +
            - You can now perform string matches on tag values (`na tagged "note*=markdown"`)
         
     | 
| 
      
 18 
     | 
    
         
            +
            - You can use `--project X` to display only actions within a specific project. Specify subprojects with a path, e.g. `na/bugs`. Partial matches allowed, works with `next`, `find`, and `tagged`
         
     | 
| 
      
 19 
     | 
    
         
            +
            - Find and tagged recognize * and ? as wildcards
         
     | 
| 
      
 20 
     | 
    
         
            +
            - --regex flag for find command
         
     | 
| 
      
 21 
     | 
    
         
            +
            - --invert command (like grep -v) for find
         
     | 
| 
      
 22 
     | 
    
         
            +
            - -v/--invert for tagged command
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            #### IMPROVED
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            - Require value 1-9 for --depth option
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
       1 
28 
     | 
    
         
             
            ### 1.1.11
         
     | 
| 
       2 
29 
     | 
    
         | 
| 
       3 
30 
     | 
    
         
             
            2022-10-05 08:56
         
     | 
    
        data/Gemfile.lock
    CHANGED
    
    
    
        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. 
     | 
| 
      
 12 
     | 
    
         
            +
            The current version of `na` is 1.1.13
         
     | 
| 
       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. 
         
     | 
    
        data/bin/na
    CHANGED
    
    | 
         @@ -20,7 +20,7 @@ class App 
     | 
|
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
              desc 'File extension to consider a todo file'
         
     | 
| 
       22 
22 
     | 
    
         
             
              default_value 'taskpaper'
         
     | 
| 
       23 
     | 
    
         
            -
              arg_name ' 
     | 
| 
      
 23 
     | 
    
         
            +
              arg_name 'EXT'
         
     | 
| 
       24 
24 
     | 
    
         
             
              flag :ext
         
     | 
| 
       25 
25 
     | 
    
         | 
| 
       26 
26 
     | 
    
         
             
              desc 'Tag to consider a next action'
         
     | 
| 
         @@ -48,7 +48,7 @@ class App 
     | 
|
| 
       48 
48 
     | 
    
         
             
              desc 'Recurse to depth'
         
     | 
| 
       49 
49 
     | 
    
         
             
              arg_name 'DEPTH'
         
     | 
| 
       50 
50 
     | 
    
         
             
              default_value 1
         
     | 
| 
       51 
     | 
    
         
            -
              flag %i[d depth], type: :integer, must_match:  
     | 
| 
      
 51 
     | 
    
         
            +
              flag %i[d depth], type: :integer, must_match: /^[1-9]$/
         
     | 
| 
       52 
52 
     | 
    
         | 
| 
       53 
53 
     | 
    
         
             
              desc 'Display verbose output'
         
     | 
| 
       54 
54 
     | 
    
         
             
              switch %i[debug]
         
     | 
| 
         @@ -59,13 +59,19 @@ class App 
     | 
|
| 
       59 
59 
     | 
    
         
             
                c.example 'na next', desc: 'display the next actions from any todo files in the current directory'
         
     | 
| 
       60 
60 
     | 
    
         
             
                c.example 'na next -d 3', desc: 'display the next actions from the current directory, traversing 3 levels deep'
         
     | 
| 
       61 
61 
     | 
    
         
             
                c.example 'na next marked', desc: 'display next actions for a project you visited in the past'
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
       62 
63 
     | 
    
         
             
                c.desc 'Recurse to depth'
         
     | 
| 
       63 
64 
     | 
    
         
             
                c.arg_name 'DEPTH'
         
     | 
| 
       64 
     | 
    
         
            -
                c.flag %i[d depth], type: :integer, must_match:  
     | 
| 
      
 65 
     | 
    
         
            +
                c.flag %i[d depth], type: :integer, must_match: /^[1-9]$/
         
     | 
| 
       65 
66 
     | 
    
         | 
| 
       66 
67 
     | 
    
         
             
                c.desc 'Alternate tag to search for'
         
     | 
| 
      
 68 
     | 
    
         
            +
                c.arg_name 'TAG'
         
     | 
| 
       67 
69 
     | 
    
         
             
                c.flag %i[t tag]
         
     | 
| 
       68 
70 
     | 
    
         | 
| 
      
 71 
     | 
    
         
            +
                c.desc 'Show actions from a specific project'
         
     | 
| 
      
 72 
     | 
    
         
            +
                c.arg_name 'PROJECT[/SUBPROJECT]'
         
     | 
| 
      
 73 
     | 
    
         
            +
                c.flag %i[proj project]
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
       69 
75 
     | 
    
         
             
                c.action do |global_options, options, args|
         
     | 
| 
       70 
76 
     | 
    
         
             
                  if global_options[:add]
         
     | 
| 
       71 
77 
     | 
    
         
             
                    cmd = ['add']
         
     | 
| 
         @@ -100,6 +106,7 @@ class App 
     | 
|
| 
       100 
106 
     | 
    
         
             
                  files, actions = NA.parse_actions(depth: depth,
         
     | 
| 
       101 
107 
     | 
    
         
             
                                                    query: tokens,
         
     | 
| 
       102 
108 
     | 
    
         
             
                                                    tag: tag,
         
     | 
| 
      
 109 
     | 
    
         
            +
                                                    project: options[:project],
         
     | 
| 
       103 
110 
     | 
    
         
             
                                                    require_na: require_na)
         
     | 
| 
       104 
111 
     | 
    
         | 
| 
       105 
112 
     | 
    
         
             
                  NA.output_actions(actions, depth, files: files)
         
     | 
| 
         @@ -122,9 +129,11 @@ class App 
     | 
|
| 
       122 
129 
     | 
    
         
             
                c.switch %i[n note], negatable: false
         
     | 
| 
       123 
130 
     | 
    
         | 
| 
       124 
131 
     | 
    
         
             
                c.desc 'Add a priority level 1-5'
         
     | 
| 
      
 132 
     | 
    
         
            +
                c.arg_name 'PRIO'
         
     | 
| 
       125 
133 
     | 
    
         
             
                c.flag %i[p priority], must_match: /[1-5]/, type: :integer, default_value: 0
         
     | 
| 
       126 
134 
     | 
    
         | 
| 
       127 
135 
     | 
    
         
             
                c.desc 'Add action to specific project'
         
     | 
| 
      
 136 
     | 
    
         
            +
                c.arg_name 'PROJECT'
         
     | 
| 
       128 
137 
     | 
    
         
             
                c.default_value 'Inbox'
         
     | 
| 
       129 
138 
     | 
    
         
             
                c.flag %i[to]
         
     | 
| 
       130 
139 
     | 
    
         | 
| 
         @@ -139,6 +148,10 @@ class App 
     | 
|
| 
       139 
148 
     | 
    
         
             
                c.arg_name 'PATH'
         
     | 
| 
       140 
149 
     | 
    
         
             
                c.flag %i[f file]
         
     | 
| 
       141 
150 
     | 
    
         | 
| 
      
 151 
     | 
    
         
            +
                c.desc 'Search for files X directories deep'
         
     | 
| 
      
 152 
     | 
    
         
            +
                c.arg_name 'DEPTH'
         
     | 
| 
      
 153 
     | 
    
         
            +
                c.flag %i[d depth], must_match: /^[1-9]$/, type: :integer, default_value: 1
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
       142 
155 
     | 
    
         
             
                c.action do |_global_options, options, args|
         
     | 
| 
       143 
156 
     | 
    
         
             
                  reader = TTY::Reader.new
         
     | 
| 
       144 
157 
     | 
    
         
             
                  action = if args.count.positive?
         
     | 
| 
         @@ -195,7 +208,7 @@ class App 
     | 
|
| 
       195 
208 
     | 
    
         
             
                      end
         
     | 
| 
       196 
209 
     | 
    
         
             
                    end
         
     | 
| 
       197 
210 
     | 
    
         
             
                  else
         
     | 
| 
       198 
     | 
    
         
            -
                    files = NA.find_files(depth:  
     | 
| 
      
 211 
     | 
    
         
            +
                    files = NA.find_files(depth: options[:depth])
         
     | 
| 
       199 
212 
     | 
    
         
             
                    if files.count.zero?
         
     | 
| 
       200 
213 
     | 
    
         
             
                      print NA::Color.template('{by}No todo file found, create one? {w}(y/{g}N{w}){x} ')
         
     | 
| 
       201 
214 
     | 
    
         
             
                      res = reader.read_char
         
     | 
| 
         @@ -221,19 +234,28 @@ class App 
     | 
|
| 
       221 
234 
     | 
    
         
             
              long_desc 'Search tokens are separated by spaces. Actions matching any token in the pattern will be shown
         
     | 
| 
       222 
235 
     | 
    
         
             
              (partial matches allowed). Add a + before a token to make it required, e.g. `na find +feature +maybe`'
         
     | 
| 
       223 
236 
     | 
    
         
             
              arg_name 'PATTERN'
         
     | 
| 
       224 
     | 
    
         
            -
              command %i[find] do |c|
         
     | 
| 
      
 237 
     | 
    
         
            +
              command %i[find grep] do |c|
         
     | 
| 
       225 
238 
     | 
    
         
             
                c.example 'na find feature +idea +swift', desc: 'Find all actions containing feature, idea, and swift'
         
     | 
| 
       226 
239 
     | 
    
         
             
                c.example 'na find -x feature idea', desc: 'Find all actions containing the exact text "feature idea"'
         
     | 
| 
       227 
240 
     | 
    
         
             
                c.example 'na find -d 3 swift obj-c', desc: 'Find all actions 3 directories deep containing either swift or obj-c'
         
     | 
| 
       228 
241 
     | 
    
         | 
| 
      
 242 
     | 
    
         
            +
                c.desc 'Interpret search pattern as regular expression'
         
     | 
| 
      
 243 
     | 
    
         
            +
                c.switch %i[e regex], negatable: false
         
     | 
| 
      
 244 
     | 
    
         
            +
             
     | 
| 
       229 
245 
     | 
    
         
             
                c.desc 'Match pattern exactly'
         
     | 
| 
       230 
246 
     | 
    
         
             
                c.switch %i[x exact], negatable: false
         
     | 
| 
       231 
247 
     | 
    
         | 
| 
       232 
248 
     | 
    
         
             
                c.desc 'Recurse to depth'
         
     | 
| 
       233 
249 
     | 
    
         
             
                c.arg_name 'DEPTH'
         
     | 
| 
       234 
     | 
    
         
            -
                c.default_value 1
         
     | 
| 
       235 
250 
     | 
    
         
             
                c.flag %i[d depth], type: :integer, must_match: /^\d+$/
         
     | 
| 
       236 
251 
     | 
    
         | 
| 
      
 252 
     | 
    
         
            +
                c.desc 'Show actions from a specific project'
         
     | 
| 
      
 253 
     | 
    
         
            +
                c.arg_name 'PROJECT[/SUBPROJECT]'
         
     | 
| 
      
 254 
     | 
    
         
            +
                c.flag %i[proj project]
         
     | 
| 
      
 255 
     | 
    
         
            +
             
     | 
| 
      
 256 
     | 
    
         
            +
                c.desc 'Show actions not matching search pattern'
         
     | 
| 
      
 257 
     | 
    
         
            +
                c.switch %i[v invert], negatable: false
         
     | 
| 
      
 258 
     | 
    
         
            +
             
     | 
| 
       237 
259 
     | 
    
         
             
                c.action do |global_options, options, args|
         
     | 
| 
       238 
260 
     | 
    
         
             
                  depth = if global_options[:recurse] && options[:depth].nil? && global_options[:depth] == 1
         
     | 
| 
       239 
261 
     | 
    
         
             
                            3
         
     | 
| 
         @@ -243,6 +265,8 @@ class App 
     | 
|
| 
       243 
265 
     | 
    
         
             
                  tokens = nil
         
     | 
| 
       244 
266 
     | 
    
         
             
                  if options[:exact]
         
     | 
| 
       245 
267 
     | 
    
         
             
                    tokens = args.join(' ')
         
     | 
| 
      
 268 
     | 
    
         
            +
                  elsif options[:regex]
         
     | 
| 
      
 269 
     | 
    
         
            +
                    tokens = Regexp.new(args.join(' '), Regexp::IGNORECASE)
         
     | 
| 
       246 
270 
     | 
    
         
             
                  else
         
     | 
| 
       247 
271 
     | 
    
         
             
                    tokens = []
         
     | 
| 
       248 
272 
     | 
    
         
             
                    args.each do |arg|
         
     | 
| 
         @@ -257,6 +281,9 @@ class App 
     | 
|
| 
       257 
281 
     | 
    
         | 
| 
       258 
282 
     | 
    
         
             
                  files, actions = NA.parse_actions(depth: depth,
         
     | 
| 
       259 
283 
     | 
    
         
             
                                                    search: tokens,
         
     | 
| 
      
 284 
     | 
    
         
            +
                                                    negate: options[:invert],
         
     | 
| 
      
 285 
     | 
    
         
            +
                                                    regex: options[:regex],
         
     | 
| 
      
 286 
     | 
    
         
            +
                                                    project: options[:project],
         
     | 
| 
       260 
287 
     | 
    
         
             
                                                    require_na: false)
         
     | 
| 
       261 
288 
     | 
    
         
             
                  NA.output_actions(actions, depth, files: files)
         
     | 
| 
       262 
289 
     | 
    
         
             
                end
         
     | 
| 
         @@ -277,6 +304,13 @@ class App 
     | 
|
| 
       277 
304 
     | 
    
         
             
                c.default_value 1
         
     | 
| 
       278 
305 
     | 
    
         
             
                c.flag %i[d depth], type: :integer, must_match: /^\d+$/
         
     | 
| 
       279 
306 
     | 
    
         | 
| 
      
 307 
     | 
    
         
            +
                c.desc 'Show actions from a specific project'
         
     | 
| 
      
 308 
     | 
    
         
            +
                c.arg_name 'PROJECT[/SUBPROJECT]'
         
     | 
| 
      
 309 
     | 
    
         
            +
                c.flag %i[proj project]
         
     | 
| 
      
 310 
     | 
    
         
            +
             
     | 
| 
      
 311 
     | 
    
         
            +
                c.desc 'Show actions not matching tags'
         
     | 
| 
      
 312 
     | 
    
         
            +
                c.switch %i[v invert], negatable: false
         
     | 
| 
      
 313 
     | 
    
         
            +
             
     | 
| 
       280 
314 
     | 
    
         
             
                c.action do |global_options, options, args|
         
     | 
| 
       281 
315 
     | 
    
         
             
                  depth = if global_options[:recurse] && options[:depth].nil? && global_options[:depth] == 1
         
     | 
| 
       282 
316 
     | 
    
         
             
                            3
         
     | 
| 
         @@ -285,9 +319,9 @@ class App 
     | 
|
| 
       285 
319 
     | 
    
         
             
                          end
         
     | 
| 
       286 
320 
     | 
    
         | 
| 
       287 
321 
     | 
    
         
             
                  tags = []
         
     | 
| 
       288 
     | 
    
         
            -
                  args.each do |arg|
         
     | 
| 
      
 322 
     | 
    
         
            +
                  args.join(',').split(/ *, */).each do |arg|
         
     | 
| 
       289 
323 
     | 
    
         
             
                    # TODO: <> comparisons do nothing right now
         
     | 
| 
       290 
     | 
    
         
            -
                    m = arg.match(/^(?<req>[+\-!])?(?<tag>[^  
     | 
| 
      
 324 
     | 
    
         
            +
                    m = arg.match(/^(?<req>[+\-!])?(?<tag>[^ =<>*$\^]+)(?:(?<op>[=<>*$\^]+)(?<val>\S+))?$/)
         
     | 
| 
       291 
325 
     | 
    
         
             
                    tags.push({
         
     | 
| 
       292 
326 
     | 
    
         
             
                                tag: m['tag'],
         
     | 
| 
       293 
327 
     | 
    
         
             
                                comp: m['op'],
         
     | 
| 
         @@ -299,6 +333,8 @@ class App 
     | 
|
| 
       299 
333 
     | 
    
         | 
| 
       300 
334 
     | 
    
         
             
                  files, actions = NA.parse_actions(depth: depth,
         
     | 
| 
       301 
335 
     | 
    
         
             
                                                    tag: tags,
         
     | 
| 
      
 336 
     | 
    
         
            +
                                                    negate: options[:invert],
         
     | 
| 
      
 337 
     | 
    
         
            +
                                                    project: options[:project],
         
     | 
| 
       302 
338 
     | 
    
         
             
                                                    require_na: false)
         
     | 
| 
       303 
339 
     | 
    
         
             
                  NA.output_actions(actions, depth, files: files)
         
     | 
| 
       304 
340 
     | 
    
         
             
                end
         
     | 
    
        data/lib/na/action.rb
    CHANGED
    
    | 
         @@ -2,7 +2,7 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            module NA
         
     | 
| 
       4 
4 
     | 
    
         
             
              class Action < Hash
         
     | 
| 
       5 
     | 
    
         
            -
                attr_reader :file, :project, :parent, :action
         
     | 
| 
      
 5 
     | 
    
         
            +
                attr_reader :file, :project, :parent, :action, :tags
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
                def initialize(file, project, parent, action)
         
     | 
| 
       8 
8 
     | 
    
         
             
                  super()
         
     | 
| 
         @@ -11,6 +11,7 @@ module NA 
     | 
|
| 
       11 
11 
     | 
    
         
             
                  @project = project
         
     | 
| 
       12 
12 
     | 
    
         
             
                  @parent = parent
         
     | 
| 
       13 
13 
     | 
    
         
             
                  @action = action
         
     | 
| 
      
 14 
     | 
    
         
            +
                  @tags = scan_tags
         
     | 
| 
       14 
15 
     | 
    
         
             
                end
         
     | 
| 
       15 
16 
     | 
    
         | 
| 
       16 
17 
     | 
    
         
             
                def to_s
         
     | 
| 
         @@ -68,5 +69,106 @@ module NA 
     | 
|
| 
       68 
69 
     | 
    
         
             
                                      .gsub(/%parents?/, parents)
         
     | 
| 
       69 
70 
     | 
    
         
             
                                      .gsub(/%action/, action))
         
     | 
| 
       70 
71 
     | 
    
         
             
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                def tags_match?(any: [], all: [], none: [])
         
     | 
| 
      
 74 
     | 
    
         
            +
                  tag_matches_any(any) && tag_matches_all(all) && tag_matches_none(none)
         
     | 
| 
      
 75 
     | 
    
         
            +
                end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                def search_match?(any: [], all: [], none: [])
         
     | 
| 
      
 78 
     | 
    
         
            +
                  search_matches_any(any) && search_matches_all(all) && search_matches_none(none)
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                private
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                def search_matches_none(regexes)
         
     | 
| 
      
 84 
     | 
    
         
            +
                  regexes.each do |rx|
         
     | 
| 
      
 85 
     | 
    
         
            +
                    return false if @action.match(Regexp.new(rx, Regexp::IGNORECASE))
         
     | 
| 
      
 86 
     | 
    
         
            +
                  end
         
     | 
| 
      
 87 
     | 
    
         
            +
                  true
         
     | 
| 
      
 88 
     | 
    
         
            +
                end
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
      
 90 
     | 
    
         
            +
                def search_matches_any(regexes)
         
     | 
| 
      
 91 
     | 
    
         
            +
                  return true if regexes.empty?
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
                  regexes.each do |rx|
         
     | 
| 
      
 94 
     | 
    
         
            +
                    return true if @action.match(Regexp.new(rx, Regexp::IGNORECASE))
         
     | 
| 
      
 95 
     | 
    
         
            +
                  end
         
     | 
| 
      
 96 
     | 
    
         
            +
                  false
         
     | 
| 
      
 97 
     | 
    
         
            +
                end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                def search_matches_all(regexes)
         
     | 
| 
      
 100 
     | 
    
         
            +
                  regexes.each do |rx|
         
     | 
| 
      
 101 
     | 
    
         
            +
                    return false unless @action.match(Regexp.new(rx, Regexp::IGNORECASE))
         
     | 
| 
      
 102 
     | 
    
         
            +
                  end
         
     | 
| 
      
 103 
     | 
    
         
            +
                  true
         
     | 
| 
      
 104 
     | 
    
         
            +
                end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
                def tag_matches_none(tags)
         
     | 
| 
      
 107 
     | 
    
         
            +
                  tags.each do |tag|
         
     | 
| 
      
 108 
     | 
    
         
            +
                    return false if compare_tag(tag)
         
     | 
| 
      
 109 
     | 
    
         
            +
                  end
         
     | 
| 
      
 110 
     | 
    
         
            +
                  true
         
     | 
| 
      
 111 
     | 
    
         
            +
                end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                def tag_matches_any(tags)
         
     | 
| 
      
 114 
     | 
    
         
            +
                  return true if tags.empty?
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                  tags.each do |tag|
         
     | 
| 
      
 117 
     | 
    
         
            +
                    return true if compare_tag(tag)
         
     | 
| 
      
 118 
     | 
    
         
            +
                  end
         
     | 
| 
      
 119 
     | 
    
         
            +
                  false
         
     | 
| 
      
 120 
     | 
    
         
            +
                end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
                def tag_matches_all(tags)
         
     | 
| 
      
 123 
     | 
    
         
            +
                  tags.each do |tag|
         
     | 
| 
      
 124 
     | 
    
         
            +
                    return false unless compare_tag(tag)
         
     | 
| 
      
 125 
     | 
    
         
            +
                  end
         
     | 
| 
      
 126 
     | 
    
         
            +
                  true
         
     | 
| 
      
 127 
     | 
    
         
            +
                end
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
                def compare_tag(tag)
         
     | 
| 
      
 130 
     | 
    
         
            +
                  return false unless @tags.key?(tag[:tag])
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
                  return true if tag[:comp].nil?
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                  tag_val = @tags[tag[:tag]]
         
     | 
| 
      
 135 
     | 
    
         
            +
                  val = tag[:value]
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
                  return false if tag_val.nil?
         
     | 
| 
      
 138 
     | 
    
         
            +
             
     | 
| 
      
 139 
     | 
    
         
            +
                  return case tag[:comp]
         
     | 
| 
      
 140 
     | 
    
         
            +
                         when /^>$/
         
     | 
| 
      
 141 
     | 
    
         
            +
                           tag_val.to_f > val.to_f
         
     | 
| 
      
 142 
     | 
    
         
            +
                         when /^<$/
         
     | 
| 
      
 143 
     | 
    
         
            +
                           tag_val.to_f < val.to_f
         
     | 
| 
      
 144 
     | 
    
         
            +
                         when /^<=$/
         
     | 
| 
      
 145 
     | 
    
         
            +
                           tag_val.to_f <= val.to_f
         
     | 
| 
      
 146 
     | 
    
         
            +
                         when /^>=$/
         
     | 
| 
      
 147 
     | 
    
         
            +
                           tag_val.to_f >= val.to_f
         
     | 
| 
      
 148 
     | 
    
         
            +
                         when /^==?$/
         
     | 
| 
      
 149 
     | 
    
         
            +
                           tag_val =~ /^#{val.wildcard_to_rx}$/
         
     | 
| 
      
 150 
     | 
    
         
            +
                         when /^\$=$/
         
     | 
| 
      
 151 
     | 
    
         
            +
                           tag_val =~ /#{val.wildcard_to_rx}$/i
         
     | 
| 
      
 152 
     | 
    
         
            +
                         when /^\*=$/
         
     | 
| 
      
 153 
     | 
    
         
            +
                           tag_val =~ /#{val.wildcard_to_rx}/i
         
     | 
| 
      
 154 
     | 
    
         
            +
                         when /^\^=$/
         
     | 
| 
      
 155 
     | 
    
         
            +
                           tag_val =~ /^#{val.wildcard_to_rx}/
         
     | 
| 
      
 156 
     | 
    
         
            +
                         else
         
     | 
| 
      
 157 
     | 
    
         
            +
                           false
         
     | 
| 
      
 158 
     | 
    
         
            +
                         end
         
     | 
| 
      
 159 
     | 
    
         
            +
                end
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                def scan_tags
         
     | 
| 
      
 162 
     | 
    
         
            +
                  tags = {}
         
     | 
| 
      
 163 
     | 
    
         
            +
                  rx = /(?<= |^)@(?<tag>\S+?)(?:\((?<value>.*?)\))?(?= |$)/
         
     | 
| 
      
 164 
     | 
    
         
            +
                  all_tags = []
         
     | 
| 
      
 165 
     | 
    
         
            +
                  @action.scan(rx) { all_tags << Regexp.last_match }
         
     | 
| 
      
 166 
     | 
    
         
            +
                  all_tags.each do |m|
         
     | 
| 
      
 167 
     | 
    
         
            +
                    tag = m.named_captures.symbolize_keys
         
     | 
| 
      
 168 
     | 
    
         
            +
                    tags[tag[:tag]] = tag[:value]
         
     | 
| 
      
 169 
     | 
    
         
            +
                  end
         
     | 
| 
      
 170 
     | 
    
         
            +
             
     | 
| 
      
 171 
     | 
    
         
            +
                  tags
         
     | 
| 
      
 172 
     | 
    
         
            +
                end
         
     | 
| 
       71 
173 
     | 
    
         
             
              end
         
     | 
| 
       72 
174 
     | 
    
         
             
            end
         
     | 
    
        data/lib/na/hash.rb
    ADDED
    
    
    
        data/lib/na/next_action.rb
    CHANGED
    
    | 
         @@ -103,45 +103,49 @@ module NA 
     | 
|
| 
       103 
103 
     | 
    
         
             
                  puts actions.map { |action| action.pretty(template: { output: template }) }
         
     | 
| 
       104 
104 
     | 
    
         
             
                end
         
     | 
| 
       105 
105 
     | 
    
         | 
| 
       106 
     | 
    
         
            -
                def parse_actions(depth: 1, query: nil, tag: nil, search: nil, require_na: true)
         
     | 
| 
      
 106 
     | 
    
         
            +
                def parse_actions(depth: 1, query: nil, tag: nil, search: nil, negate: false, regex: false, project: nil, require_na: true)
         
     | 
| 
       107 
107 
     | 
    
         
             
                  actions = []
         
     | 
| 
       108 
108 
     | 
    
         
             
                  required = []
         
     | 
| 
       109 
109 
     | 
    
         
             
                  optional = []
         
     | 
| 
       110 
110 
     | 
    
         
             
                  negated = []
         
     | 
| 
      
 111 
     | 
    
         
            +
                  required_tag = []
         
     | 
| 
      
 112 
     | 
    
         
            +
                  optional_tag = []
         
     | 
| 
      
 113 
     | 
    
         
            +
                  negated_tag = []
         
     | 
| 
       111 
114 
     | 
    
         | 
| 
       112 
115 
     | 
    
         
             
                  tag&.each do |t|
         
     | 
| 
       113 
116 
     | 
    
         
             
                    unless t[:tag].nil?
         
     | 
| 
       114 
     | 
    
         
            -
                       
     | 
| 
       115 
     | 
    
         
            -
             
     | 
| 
       116 
     | 
    
         
            -
             
     | 
| 
       117 
     | 
    
         
            -
             
     | 
| 
       118 
     | 
    
         
            -
                       
     | 
| 
       119 
     | 
    
         
            -
             
     | 
| 
      
 117 
     | 
    
         
            +
                      if negate
         
     | 
| 
      
 118 
     | 
    
         
            +
                        optional_tag.push(t) if t[:negate]
         
     | 
| 
      
 119 
     | 
    
         
            +
                        required_tag.push(t) if t[:required] && t[:negate]
         
     | 
| 
      
 120 
     | 
    
         
            +
                        negated_tag.push(t) unless t[:negate]
         
     | 
| 
      
 121 
     | 
    
         
            +
                      else
         
     | 
| 
      
 122 
     | 
    
         
            +
                        optional_tag.push(t) unless t[:negate]
         
     | 
| 
      
 123 
     | 
    
         
            +
                        required_tag.push(t) if t[:required] && !t[:negate]
         
     | 
| 
      
 124 
     | 
    
         
            +
                        negated_tag.push(t) if t[:negate]
         
     | 
| 
      
 125 
     | 
    
         
            +
                      end
         
     | 
| 
       120 
126 
     | 
    
         
             
                    end
         
     | 
| 
       121 
127 
     | 
    
         
             
                  end
         
     | 
| 
       122 
128 
     | 
    
         | 
| 
       123 
129 
     | 
    
         
             
                  unless search.nil?
         
     | 
| 
       124 
     | 
    
         
            -
                    if search.is_a?(String)
         
     | 
| 
       125 
     | 
    
         
            -
                       
     | 
| 
       126 
     | 
    
         
            -
             
     | 
| 
      
 130 
     | 
    
         
            +
                    if regex || search.is_a?(String)
         
     | 
| 
      
 131 
     | 
    
         
            +
                      if negate
         
     | 
| 
      
 132 
     | 
    
         
            +
                        negated.push(search)
         
     | 
| 
      
 133 
     | 
    
         
            +
                      else
         
     | 
| 
      
 134 
     | 
    
         
            +
                        optional.push(search)
         
     | 
| 
      
 135 
     | 
    
         
            +
                        required.push(search)
         
     | 
| 
      
 136 
     | 
    
         
            +
                      end
         
     | 
| 
       127 
137 
     | 
    
         
             
                    else
         
     | 
| 
       128 
138 
     | 
    
         
             
                      search.each do |t|
         
     | 
| 
       129 
     | 
    
         
            -
                         
     | 
| 
       130 
     | 
    
         
            -
             
     | 
| 
       131 
     | 
    
         
            -
                        optional.push(new_rx) unless t[:negate]
         
     | 
| 
       132 
     | 
    
         
            -
                        required.push(new_rx) if t[:required] && !t[:negate]
         
     | 
| 
       133 
     | 
    
         
            -
                        negated.push(new_rx) if t[:negate]
         
     | 
| 
      
 139 
     | 
    
         
            +
                        optional, required, negated = parse_search(t, negate)
         
     | 
| 
       134 
140 
     | 
    
         
             
                      end
         
     | 
| 
       135 
141 
     | 
    
         
             
                    end
         
     | 
| 
       136 
142 
     | 
    
         
             
                  end
         
     | 
| 
       137 
143 
     | 
    
         | 
| 
       138 
     | 
    
         
            -
                   
     | 
| 
       139 
     | 
    
         
            -
             
     | 
| 
       140 
     | 
    
         
            -
             
     | 
| 
       141 
     | 
    
         
            -
             
     | 
| 
       142 
     | 
    
         
            -
             
     | 
| 
       143 
     | 
    
         
            -
                    files = match_working_dir(query)
         
     | 
| 
       144 
     | 
    
         
            -
                  end
         
     | 
| 
      
 144 
     | 
    
         
            +
                  files = if query.nil?
         
     | 
| 
      
 145 
     | 
    
         
            +
                            find_files(depth: depth)
         
     | 
| 
      
 146 
     | 
    
         
            +
                          else
         
     | 
| 
      
 147 
     | 
    
         
            +
                            match_working_dir(query)
         
     | 
| 
      
 148 
     | 
    
         
            +
                          end
         
     | 
| 
       145 
149 
     | 
    
         | 
| 
       146 
150 
     | 
    
         
             
                  files.each do |file|
         
     | 
| 
       147 
151 
     | 
    
         
             
                    save_working_dir(File.expand_path(file))
         
     | 
| 
         @@ -166,13 +170,24 @@ module NA 
     | 
|
| 
       166 
170 
     | 
    
         
             
                      elsif line =~ /^[ \t]*- / && line !~ / @done/
         
     | 
| 
       167 
171 
     | 
    
         
             
                        next if require_na && line !~ /@#{NA.na_tag}\b/
         
     | 
| 
       168 
172 
     | 
    
         | 
| 
       169 
     | 
    
         
            -
                         
     | 
| 
       170 
     | 
    
         
            -
             
     | 
| 
      
 173 
     | 
    
         
            +
                        action = line.sub(/^[ \t]*- /, '').sub(/ @#{NA.na_tag}\b/, '')
         
     | 
| 
      
 174 
     | 
    
         
            +
                        new_action = NA::Action.new(file, File.basename(file, ".#{NA.extension}"), parent.dup, action)
         
     | 
| 
       171 
175 
     | 
    
         | 
| 
      
 176 
     | 
    
         
            +
                        has_search = !optional.empty? || !required.empty? || !negated.empty?
         
     | 
| 
      
 177 
     | 
    
         
            +
                        next if has_search && !new_action.search_match?(any: optional,
         
     | 
| 
      
 178 
     | 
    
         
            +
                                                                        all: required,
         
     | 
| 
      
 179 
     | 
    
         
            +
                                                                        none: negated)
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                        if project
         
     | 
| 
      
 182 
     | 
    
         
            +
                          rx = project.split(%r{[/:]}).join('.*?/.*?')
         
     | 
| 
      
 183 
     | 
    
         
            +
                          next unless parent.join('/') =~ Regexp.new(rx, Regexp::IGNORECASE)
         
     | 
| 
       172 
184 
     | 
    
         
             
                        end
         
     | 
| 
       173 
185 
     | 
    
         | 
| 
       174 
     | 
    
         
            -
                         
     | 
| 
       175 
     | 
    
         
            -
                         
     | 
| 
      
 186 
     | 
    
         
            +
                        has_tag = !optional_tag.empty? || !required_tag.empty? || !negated_tag.empty?
         
     | 
| 
      
 187 
     | 
    
         
            +
                        next if has_tag && !new_action.tags_match?(any: optional_tag,
         
     | 
| 
      
 188 
     | 
    
         
            +
                                                                   all: required_tag,
         
     | 
| 
      
 189 
     | 
    
         
            +
                                                                   none: negated_tag)
         
     | 
| 
      
 190 
     | 
    
         
            +
             
     | 
| 
       176 
191 
     | 
    
         
             
                        actions.push(new_action)
         
     | 
| 
       177 
192 
     | 
    
         
             
                      end
         
     | 
| 
       178 
193 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -180,6 +195,40 @@ module NA 
     | 
|
| 
       180 
195 
     | 
    
         
             
                  [files, actions]
         
     | 
| 
       181 
196 
     | 
    
         
             
                end
         
     | 
| 
       182 
197 
     | 
    
         | 
| 
      
 198 
     | 
    
         
            +
                def edit_file(file: nil, app: nil)
         
     | 
| 
      
 199 
     | 
    
         
            +
                  os_open(file, app: app) if file && File.exist?(file)
         
     | 
| 
      
 200 
     | 
    
         
            +
                end
         
     | 
| 
      
 201 
     | 
    
         
            +
             
     | 
| 
      
 202 
     | 
    
         
            +
                ##
         
     | 
| 
      
 203 
     | 
    
         
            +
                ## Platform-agnostic open command
         
     | 
| 
      
 204 
     | 
    
         
            +
                ##
         
     | 
| 
      
 205 
     | 
    
         
            +
                ## @param      file  [String] The file to open
         
     | 
| 
      
 206 
     | 
    
         
            +
                ##
         
     | 
| 
      
 207 
     | 
    
         
            +
                def os_open(file, app: nil)
         
     | 
| 
      
 208 
     | 
    
         
            +
                  os = RbConfig::CONFIG['target_os']
         
     | 
| 
      
 209 
     | 
    
         
            +
                  case os
         
     | 
| 
      
 210 
     | 
    
         
            +
                  when /darwin.*/i
         
     | 
| 
      
 211 
     | 
    
         
            +
                    darwin_open(file, app: app)
         
     | 
| 
      
 212 
     | 
    
         
            +
                  when /mingw|mswin/i
         
     | 
| 
      
 213 
     | 
    
         
            +
                    win_open(file)
         
     | 
| 
      
 214 
     | 
    
         
            +
                  else
         
     | 
| 
      
 215 
     | 
    
         
            +
                    linux_open(file)
         
     | 
| 
      
 216 
     | 
    
         
            +
                  end
         
     | 
| 
      
 217 
     | 
    
         
            +
                end
         
     | 
| 
      
 218 
     | 
    
         
            +
             
     | 
| 
      
 219 
     | 
    
         
            +
                def weed_cache_file
         
     | 
| 
      
 220 
     | 
    
         
            +
                  db_dir = File.expand_path('~/.local/share/na')
         
     | 
| 
      
 221 
     | 
    
         
            +
                  db_file = 'tdlist.txt'
         
     | 
| 
      
 222 
     | 
    
         
            +
                  file = File.join(db_dir, db_file)
         
     | 
| 
      
 223 
     | 
    
         
            +
                  if File.exist?(file)
         
     | 
| 
      
 224 
     | 
    
         
            +
                    dirs = IO.read(file).split("\n")
         
     | 
| 
      
 225 
     | 
    
         
            +
                    dirs.delete_if { |f| !File.exist?(f) }
         
     | 
| 
      
 226 
     | 
    
         
            +
                    File.open(file, 'w') { |f| f.puts dirs.join("\n") }
         
     | 
| 
      
 227 
     | 
    
         
            +
                  end
         
     | 
| 
      
 228 
     | 
    
         
            +
                end
         
     | 
| 
      
 229 
     | 
    
         
            +
             
     | 
| 
      
 230 
     | 
    
         
            +
                private
         
     | 
| 
      
 231 
     | 
    
         
            +
             
     | 
| 
       183 
232 
     | 
    
         
             
                ##
         
     | 
| 
       184 
233 
     | 
    
         
             
                ## Generate a menu of options and allow user selection
         
     | 
| 
       185 
234 
     | 
    
         
             
                ##
         
     | 
| 
         @@ -207,6 +256,25 @@ module NA 
     | 
|
| 
       207 
256 
     | 
    
         
             
                  res
         
     | 
| 
       208 
257 
     | 
    
         
             
                end
         
     | 
| 
       209 
258 
     | 
    
         | 
| 
      
 259 
     | 
    
         
            +
                def parse_search(tag, negate)
         
     | 
| 
      
 260 
     | 
    
         
            +
                  required = []
         
     | 
| 
      
 261 
     | 
    
         
            +
                  optional = []
         
     | 
| 
      
 262 
     | 
    
         
            +
                  negated = []
         
     | 
| 
      
 263 
     | 
    
         
            +
                  new_rx = tag[:token].to_s.wildcard_to_rx
         
     | 
| 
      
 264 
     | 
    
         
            +
             
     | 
| 
      
 265 
     | 
    
         
            +
                  if negate
         
     | 
| 
      
 266 
     | 
    
         
            +
                    optional.push(new_rx) if tag[:negate]
         
     | 
| 
      
 267 
     | 
    
         
            +
                    required.push(new_rx) if tag[:required] && tag[:negate]
         
     | 
| 
      
 268 
     | 
    
         
            +
                    negated.push(new_rx) unless tag[:negate]
         
     | 
| 
      
 269 
     | 
    
         
            +
                  else
         
     | 
| 
      
 270 
     | 
    
         
            +
                    optional.push(new_rx) unless tag[:negate]
         
     | 
| 
      
 271 
     | 
    
         
            +
                    required.push(new_rx) if tag[:required] && !tag[:negate]
         
     | 
| 
      
 272 
     | 
    
         
            +
                    negated.push(new_rx) if tag[:negate]
         
     | 
| 
      
 273 
     | 
    
         
            +
                  end
         
     | 
| 
      
 274 
     | 
    
         
            +
             
     | 
| 
      
 275 
     | 
    
         
            +
                  [optional, required, negated]
         
     | 
| 
      
 276 
     | 
    
         
            +
                end
         
     | 
| 
      
 277 
     | 
    
         
            +
             
     | 
| 
       210 
278 
     | 
    
         
             
                ##
         
     | 
| 
       211 
279 
     | 
    
         
             
                ## Get path to database of known todo files
         
     | 
| 
       212 
280 
     | 
    
         
             
                ##
         
     | 
| 
         @@ -249,7 +317,7 @@ module NA 
     | 
|
| 
       249 
317 
     | 
    
         
             
                  notify('{r}No na database found', exit_code: 1) unless File.exist?(file)
         
     | 
| 
       250 
318 
     | 
    
         | 
| 
       251 
319 
     | 
    
         
             
                  dirs = IO.read(file).split("\n")
         
     | 
| 
       252 
     | 
    
         
            -
                  dirs.delete_if { |d| !d. 
     | 
| 
      
 320 
     | 
    
         
            +
                  dirs.delete_if { |d| !d.dir_matches(any: optional, all: required) }
         
     | 
| 
       253 
321 
     | 
    
         
             
                  dirs.sort.uniq
         
     | 
| 
       254 
322 
     | 
    
         
             
                end
         
     | 
| 
       255 
323 
     | 
    
         | 
| 
         @@ -262,21 +330,6 @@ module NA 
     | 
|
| 
       262 
330 
     | 
    
         
             
                  File.open(file, 'w') { |f| f.puts dirs.join("\n") }
         
     | 
| 
       263 
331 
     | 
    
         
             
                end
         
     | 
| 
       264 
332 
     | 
    
         | 
| 
       265 
     | 
    
         
            -
                def weed_cache_file
         
     | 
| 
       266 
     | 
    
         
            -
                  db_dir = File.expand_path('~/.local/share/na')
         
     | 
| 
       267 
     | 
    
         
            -
                  db_file = 'tdlist.txt'
         
     | 
| 
       268 
     | 
    
         
            -
                  file = File.join(db_dir, db_file)
         
     | 
| 
       269 
     | 
    
         
            -
                  if File.exist?(file)
         
     | 
| 
       270 
     | 
    
         
            -
                    dirs = IO.read(file).split("\n")
         
     | 
| 
       271 
     | 
    
         
            -
                    dirs.delete_if { |f| !File.exist?(f) }
         
     | 
| 
       272 
     | 
    
         
            -
                    File.open(file, 'w') { |f| f.puts dirs.join("\n") }
         
     | 
| 
       273 
     | 
    
         
            -
                  end
         
     | 
| 
       274 
     | 
    
         
            -
                end
         
     | 
| 
       275 
     | 
    
         
            -
             
     | 
| 
       276 
     | 
    
         
            -
                def edit_file(file: nil, app: nil)
         
     | 
| 
       277 
     | 
    
         
            -
                  os_open(file, app: app) if file && File.exist?(file)
         
     | 
| 
       278 
     | 
    
         
            -
                end
         
     | 
| 
       279 
     | 
    
         
            -
             
     | 
| 
       280 
333 
     | 
    
         
             
                def darwin_open(file, app: nil)
         
     | 
| 
       281 
334 
     | 
    
         
             
                  if app
         
     | 
| 
       282 
335 
     | 
    
         
             
                    `open -a "#{app}" #{Shellwords.escape(file)}`
         
     | 
| 
         @@ -296,22 +349,5 @@ module NA 
     | 
|
| 
       296 
349 
     | 
    
         
             
                    notify('{r}Unable to determine executable for `xdg-open`.')
         
     | 
| 
       297 
350 
     | 
    
         
             
                  end
         
     | 
| 
       298 
351 
     | 
    
         
             
                end
         
     | 
| 
       299 
     | 
    
         
            -
             
     | 
| 
       300 
     | 
    
         
            -
                ##
         
     | 
| 
       301 
     | 
    
         
            -
                ## Platform-agnostic open command
         
     | 
| 
       302 
     | 
    
         
            -
                ##
         
     | 
| 
       303 
     | 
    
         
            -
                ## @param      file  [String] The file to open
         
     | 
| 
       304 
     | 
    
         
            -
                ##
         
     | 
| 
       305 
     | 
    
         
            -
                def os_open(file, app: nil)
         
     | 
| 
       306 
     | 
    
         
            -
                  os = RbConfig::CONFIG['target_os']
         
     | 
| 
       307 
     | 
    
         
            -
                  case os
         
     | 
| 
       308 
     | 
    
         
            -
                  when /darwin.*/i
         
     | 
| 
       309 
     | 
    
         
            -
                    darwin_open(file, app: app)
         
     | 
| 
       310 
     | 
    
         
            -
                  when /mingw|mswin/i
         
     | 
| 
       311 
     | 
    
         
            -
                    win_open(file)
         
     | 
| 
       312 
     | 
    
         
            -
                  else
         
     | 
| 
       313 
     | 
    
         
            -
                    linux_open(file)
         
     | 
| 
       314 
     | 
    
         
            -
                  end
         
     | 
| 
       315 
     | 
    
         
            -
                end
         
     | 
| 
       316 
352 
     | 
    
         
             
              end
         
     | 
| 
       317 
353 
     | 
    
         
             
            end
         
     | 
    
        data/lib/na/string.rb
    CHANGED
    
    | 
         @@ -24,6 +24,14 @@ class ::String 
     | 
|
| 
       24 
24 
     | 
    
         
             
                gsub(/(\s|m)(@[^ ("']+)(?:(\()(.*?)(\)))?/, "\\1#{tag_color}\\2#{paren_color}\\3#{value_color}\\4#{paren_color}\\5#{last_color}")
         
     | 
| 
       25 
25 
     | 
    
         
             
              end
         
     | 
| 
       26 
26 
     | 
    
         | 
| 
      
 27 
     | 
    
         
            +
              def dir_to_rx
         
     | 
| 
      
 28 
     | 
    
         
            +
                split(%r{[/:]}).join('.*?/.*?') + '[^/]+$'
         
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              def dir_matches(any: [], all: [])
         
     | 
| 
      
 32 
     | 
    
         
            +
                matches_any(any.map { |token| token.dir_to_rx }) && matches_all(all.map { |token| token.dir_to_rx })
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
       27 
35 
     | 
    
         
             
              def matches(any: [], all: [], none: [])
         
     | 
| 
       28 
36 
     | 
    
         
             
                matches_any(any) && matches_all(all) && matches_none(none)
         
     | 
| 
       29 
37 
     | 
    
         
             
              end
         
     | 
| 
         @@ -36,6 +44,7 @@ class ::String 
     | 
|
| 
       36 
44 
     | 
    
         
             
              end
         
     | 
| 
       37 
45 
     | 
    
         | 
| 
       38 
46 
     | 
    
         
             
              def matches_any(regexes)
         
     | 
| 
      
 47 
     | 
    
         
            +
                string = dup
         
     | 
| 
       39 
48 
     | 
    
         
             
                regexes.each do |rx|
         
     | 
| 
       40 
49 
     | 
    
         
             
                  return true if match(Regexp.new(rx, Regexp::IGNORECASE))
         
     | 
| 
       41 
50 
     | 
    
         
             
                end
         
     | 
| 
         @@ -49,6 +58,10 @@ class ::String 
     | 
|
| 
       49 
58 
     | 
    
         
             
                true
         
     | 
| 
       50 
59 
     | 
    
         
             
              end
         
     | 
| 
       51 
60 
     | 
    
         | 
| 
      
 61 
     | 
    
         
            +
              def wildcard_to_rx
         
     | 
| 
      
 62 
     | 
    
         
            +
                gsub(/\./, '\\.').gsub(/\*/, '.*?').gsub(/\?/, '.')
         
     | 
| 
      
 63 
     | 
    
         
            +
              end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
       52 
65 
     | 
    
         
             
              def cap_first!
         
     | 
| 
       53 
66 
     | 
    
         
             
                replace cap_first
         
     | 
| 
       54 
67 
     | 
    
         
             
              end
         
     | 
    
        data/lib/na/version.rb
    CHANGED
    
    
    
        data/lib/na.rb
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
             
     | 
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
     | 
    
         
            -
            # you just need to require this one file in your bin file
         
     | 
| 
      
 3 
     | 
    
         
            +
            require 'na/version'
         
     | 
| 
       5 
4 
     | 
    
         
             
            require 'fileutils'
         
     | 
| 
       6 
5 
     | 
    
         
             
            require 'shellwords'
         
     | 
| 
       7 
6 
     | 
    
         
             
            require 'tty-screen'
         
     | 
| 
       8 
7 
     | 
    
         
             
            require 'tty-reader'
         
     | 
| 
       9 
8 
     | 
    
         
             
            require 'tty-which'
         
     | 
| 
       10 
     | 
    
         
            -
            require 'na/ 
     | 
| 
       11 
     | 
    
         
            -
            require 'na/ 
     | 
| 
       12 
     | 
    
         
            -
            require 'na/ 
     | 
| 
       13 
     | 
    
         
            -
            require 'na/ 
     | 
| 
       14 
     | 
    
         
            -
            require 'na/ 
     | 
| 
      
 9 
     | 
    
         
            +
            require 'na/hash'
         
     | 
| 
      
 10 
     | 
    
         
            +
            require 'na/colors'
         
     | 
| 
      
 11 
     | 
    
         
            +
            require 'na/string'
         
     | 
| 
      
 12 
     | 
    
         
            +
            require 'na/action'
         
     | 
| 
      
 13 
     | 
    
         
            +
            require 'na/next_action'
         
     | 
| 
      
 14 
     | 
    
         
            +
            require 'na/prompt'
         
     | 
    
        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. 
     | 
| 
      
 12 
     | 
    
         
            +
            The current version of `na` is <!--VER-->1.1.12<!--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,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: na
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 1.1. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 1.1.13
         
     | 
| 
       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- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2022-10-06 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: rake
         
     | 
| 
         @@ -169,6 +169,7 @@ files: 
     | 
|
| 
       169 
169 
     | 
    
         
             
            - lib/na.rb
         
     | 
| 
       170 
170 
     | 
    
         
             
            - lib/na/action.rb
         
     | 
| 
       171 
171 
     | 
    
         
             
            - lib/na/colors.rb
         
     | 
| 
      
 172 
     | 
    
         
            +
            - lib/na/hash.rb
         
     | 
| 
       172 
173 
     | 
    
         
             
            - lib/na/next_action.rb
         
     | 
| 
       173 
174 
     | 
    
         
             
            - lib/na/prompt.rb
         
     | 
| 
       174 
175 
     | 
    
         
             
            - lib/na/string.rb
         
     |