na 1.2.80 → 1.2.82
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/.rubocop.yml +8 -2
 - data/.rubocop_todo.yml +33 -538
 - data/CHANGELOG.md +27 -0
 - data/Gemfile +2 -0
 - data/Gemfile.lock +27 -10
 - data/README.md +66 -21
 - data/Rakefile +6 -0
 - data/bin/commands/next.rb +4 -0
 - data/bin/commands/scan.rb +84 -0
 - data/bin/commands/update.rb +291 -14
 - data/bin/na +7 -7
 - data/lib/na/action.rb +103 -38
 - data/lib/na/actions.rb +79 -77
 - data/lib/na/array.rb +11 -7
 - data/lib/na/benchmark.rb +21 -9
 - data/lib/na/colors.rb +84 -86
 - data/lib/na/editor.rb +22 -22
 - data/lib/na/hash.rb +32 -9
 - data/lib/na/help_monkey_patch.rb +9 -1
 - data/lib/na/next_action.rb +347 -305
 - data/lib/na/pager.rb +38 -14
 - data/lib/na/project.rb +14 -1
 - data/lib/na/prompt.rb +25 -3
 - data/lib/na/string.rb +94 -133
 - data/lib/na/theme.rb +37 -31
 - data/lib/na/todo.rb +153 -132
 - data/lib/na/version.rb +3 -1
 - data/lib/na.rb +1 -0
 - data/na.gemspec +4 -2
 - data/scripts/generate-fish-completions.rb +18 -21
 - data/src/_README.md +17 -5
 - data/test_performance.rb +5 -5
 - metadata +53 -24
 
    
        data/lib/na/pager.rb
    CHANGED
    
    | 
         @@ -7,22 +7,21 @@ module NA 
     | 
|
| 
       7 
7 
     | 
    
         
             
              module Pager
         
     | 
| 
       8 
8 
     | 
    
         
             
                class << self
         
     | 
| 
       9 
9 
     | 
    
         
             
                  # Boolean determines whether output is paginated
         
     | 
| 
      
 10 
     | 
    
         
            +
                  #
         
     | 
| 
      
 11 
     | 
    
         
            +
                  # @return [Boolean] true if paginated
         
     | 
| 
       10 
12 
     | 
    
         
             
                  def paginate
         
     | 
| 
       11 
13 
     | 
    
         
             
                    @paginate ||= false
         
     | 
| 
       12 
14 
     | 
    
         
             
                  end
         
     | 
| 
       13 
15 
     | 
    
         | 
| 
       14 
16 
     | 
    
         
             
                  # Enable/disable pagination
         
     | 
| 
       15 
17 
     | 
    
         
             
                  #
         
     | 
| 
       16 
     | 
    
         
            -
                  # @ 
     | 
| 
       17 
     | 
    
         
            -
                   
     | 
| 
       18 
     | 
    
         
            -
                    @paginate = should_paginate
         
     | 
| 
       19 
     | 
    
         
            -
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
      
 19 
     | 
    
         
            +
                  attr_writer :paginate
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
                  # Page output. If @paginate is false, just dump to
         
     | 
| 
       22 
     | 
    
         
            -
                  # STDOUT
         
     | 
| 
       23 
     | 
    
         
            -
                  #
         
     | 
| 
       24 
     | 
    
         
            -
                  # @param      text  [String] text to paginate
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # Page output. If @paginate is false, just dump to STDOUT
         
     | 
| 
       25 
22 
     | 
    
         
             
                  #
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # @param text [String] text to paginate
         
     | 
| 
      
 24 
     | 
    
         
            +
                  # @return [Boolean, nil] true if paged, false if not, nil if no pager
         
     | 
| 
       26 
25 
     | 
    
         
             
                  def page(text)
         
     | 
| 
       27 
26 
     | 
    
         
             
                    unless @paginate
         
     | 
| 
       28 
27 
     | 
    
         
             
                      puts text
         
     | 
| 
         @@ -53,31 +52,53 @@ module NA 
     | 
|
| 
       53 
52 
     | 
    
         
             
                      # Wait for pager to complete
         
     | 
| 
       54 
53 
     | 
    
         
             
                      _, status = Process.waitpid2(pid)
         
     | 
| 
       55 
54 
     | 
    
         
             
                      status.success?
         
     | 
| 
       56 
     | 
    
         
            -
                    rescue SystemCallError 
     | 
| 
      
 55 
     | 
    
         
            +
                    rescue SystemCallError
         
     | 
| 
       57 
56 
     | 
    
         
             
                      # Clean up on error
         
     | 
| 
       58 
     | 
    
         
            -
                       
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
                       
     | 
| 
      
 57 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 58 
     | 
    
         
            +
                        write_io.close
         
     | 
| 
      
 59 
     | 
    
         
            +
                      rescue StandardError
         
     | 
| 
      
 60 
     | 
    
         
            +
                        nil
         
     | 
| 
      
 61 
     | 
    
         
            +
                      end
         
     | 
| 
      
 62 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 63 
     | 
    
         
            +
                        Process.kill('TERM', pid)
         
     | 
| 
      
 64 
     | 
    
         
            +
                      rescue StandardError
         
     | 
| 
      
 65 
     | 
    
         
            +
                        nil
         
     | 
| 
      
 66 
     | 
    
         
            +
                      end
         
     | 
| 
      
 67 
     | 
    
         
            +
                      begin
         
     | 
| 
      
 68 
     | 
    
         
            +
                        Process.waitpid(pid)
         
     | 
| 
      
 69 
     | 
    
         
            +
                      rescue StandardError
         
     | 
| 
      
 70 
     | 
    
         
            +
                        nil
         
     | 
| 
      
 71 
     | 
    
         
            +
                      end
         
     | 
| 
       61 
72 
     | 
    
         
             
                      false
         
     | 
| 
       62 
73 
     | 
    
         
             
                    end
         
     | 
| 
       63 
74 
     | 
    
         
             
                  end
         
     | 
| 
       64 
75 
     | 
    
         | 
| 
       65 
76 
     | 
    
         
             
                  private
         
     | 
| 
       66 
77 
     | 
    
         | 
| 
      
 78 
     | 
    
         
            +
                  # Get the git pager command if available
         
     | 
| 
      
 79 
     | 
    
         
            +
                  #
         
     | 
| 
      
 80 
     | 
    
         
            +
                  # @return [String, nil] git pager command
         
     | 
| 
       67 
81 
     | 
    
         
             
                  def git_pager
         
     | 
| 
       68 
82 
     | 
    
         
             
                    TTY::Which.exist?('git') ? `#{TTY::Which.which('git')} config --get-all core.pager` : nil
         
     | 
| 
       69 
83 
     | 
    
         
             
                  end
         
     | 
| 
       70 
84 
     | 
    
         | 
| 
      
 85 
     | 
    
         
            +
                  # List of possible pager commands
         
     | 
| 
      
 86 
     | 
    
         
            +
                  #
         
     | 
| 
      
 87 
     | 
    
         
            +
                  # @return [Array<String>] pager commands
         
     | 
| 
       71 
88 
     | 
    
         
             
                  def pagers
         
     | 
| 
       72 
89 
     | 
    
         
             
                    [
         
     | 
| 
       73 
     | 
    
         
            -
                      ENV 
     | 
| 
      
 90 
     | 
    
         
            +
                      ENV.fetch('PAGER', nil),
         
     | 
| 
       74 
91 
     | 
    
         
             
                      'less -FXr',
         
     | 
| 
       75 
     | 
    
         
            -
                      ENV 
     | 
| 
      
 92 
     | 
    
         
            +
                      ENV.fetch('GIT_PAGER', nil),
         
     | 
| 
       76 
93 
     | 
    
         
             
                      git_pager,
         
     | 
| 
       77 
94 
     | 
    
         
             
                      'more -r'
         
     | 
| 
       78 
95 
     | 
    
         
             
                    ].remove_bad
         
     | 
| 
       79 
96 
     | 
    
         
             
                  end
         
     | 
| 
       80 
97 
     | 
    
         | 
| 
      
 98 
     | 
    
         
            +
                  # Find the first available executable pager command
         
     | 
| 
      
 99 
     | 
    
         
            +
                  #
         
     | 
| 
      
 100 
     | 
    
         
            +
                  # @param commands [Array<String>] commands to check
         
     | 
| 
      
 101 
     | 
    
         
            +
                  # @return [String, nil] first available command
         
     | 
| 
       81 
102 
     | 
    
         
             
                  def find_executable(*commands)
         
     | 
| 
       82 
103 
     | 
    
         
             
                    execs = commands.empty? ? pagers : commands
         
     | 
| 
       83 
104 
     | 
    
         
             
                    execs
         
     | 
| 
         @@ -85,6 +106,9 @@ module NA 
     | 
|
| 
       85 
106 
     | 
    
         
             
                      .find { |cmd| TTY::Which.exist?(cmd.split.first) }
         
     | 
| 
       86 
107 
     | 
    
         
             
                  end
         
     | 
| 
       87 
108 
     | 
    
         | 
| 
      
 109 
     | 
    
         
            +
                  # Determine which pager to use
         
     | 
| 
      
 110 
     | 
    
         
            +
                  #
         
     | 
| 
      
 111 
     | 
    
         
            +
                  # @return [String, nil] pager command
         
     | 
| 
       88 
112 
     | 
    
         
             
                  def which_pager
         
     | 
| 
       89 
113 
     | 
    
         
             
                    @which_pager ||= find_executable(*pagers)
         
     | 
| 
       90 
114 
     | 
    
         
             
                  end
         
     | 
    
        data/lib/na/project.rb
    CHANGED
    
    | 
         @@ -4,6 +4,13 @@ module NA 
     | 
|
| 
       4 
4 
     | 
    
         
             
              class Project < Hash
         
     | 
| 
       5 
5 
     | 
    
         
             
                attr_accessor :project, :indent, :line, :last_line
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
      
 7 
     | 
    
         
            +
                # Initialize a Project object
         
     | 
| 
      
 8 
     | 
    
         
            +
                #
         
     | 
| 
      
 9 
     | 
    
         
            +
                # @param project [String] Project name
         
     | 
| 
      
 10 
     | 
    
         
            +
                # @param indent [Integer] Indentation level
         
     | 
| 
      
 11 
     | 
    
         
            +
                # @param line [Integer] Starting line number
         
     | 
| 
      
 12 
     | 
    
         
            +
                # @param last_line [Integer] Ending line number
         
     | 
| 
      
 13 
     | 
    
         
            +
                # @return [void]
         
     | 
| 
       7 
14 
     | 
    
         
             
                def initialize(project, indent = 0, line = 0, last_line = 0)
         
     | 
| 
       8 
15 
     | 
    
         
             
                  super()
         
     | 
| 
       9 
16 
     | 
    
         
             
                  @project = project
         
     | 
| 
         @@ -12,17 +19,23 @@ module NA 
     | 
|
| 
       12 
19 
     | 
    
         
             
                  @last_line = last_line
         
     | 
| 
       13 
20 
     | 
    
         
             
                end
         
     | 
| 
       14 
21 
     | 
    
         | 
| 
      
 22 
     | 
    
         
            +
                # String representation of the project
         
     | 
| 
      
 23 
     | 
    
         
            +
                #
         
     | 
| 
      
 24 
     | 
    
         
            +
                # @return [String]
         
     | 
| 
       15 
25 
     | 
    
         
             
                def to_s
         
     | 
| 
       16 
26 
     | 
    
         
             
                  { project: @project, indent: @indent, line: @line, last_line: @last_line }.to_s
         
     | 
| 
       17 
27 
     | 
    
         
             
                end
         
     | 
| 
       18 
28 
     | 
    
         | 
| 
      
 29 
     | 
    
         
            +
                # Inspect the project object
         
     | 
| 
      
 30 
     | 
    
         
            +
                #
         
     | 
| 
      
 31 
     | 
    
         
            +
                # @return [String]
         
     | 
| 
       19 
32 
     | 
    
         
             
                def inspect
         
     | 
| 
       20 
33 
     | 
    
         
             
                  [
         
     | 
| 
       21 
34 
     | 
    
         
             
                    "@project: #{@project}",
         
     | 
| 
       22 
35 
     | 
    
         
             
                    "@indent: #{@indent}",
         
     | 
| 
       23 
36 
     | 
    
         
             
                    "@line: #{@line}",
         
     | 
| 
       24 
37 
     | 
    
         
             
                    "@last_line: #{@last_line}"
         
     | 
| 
       25 
     | 
    
         
            -
                  ].join( 
     | 
| 
      
 38 
     | 
    
         
            +
                  ].join(' ')
         
     | 
| 
       26 
39 
     | 
    
         
             
                end
         
     | 
| 
       27 
40 
     | 
    
         
             
              end
         
     | 
| 
       28 
41 
     | 
    
         
             
            end
         
     | 
    
        data/lib/na/prompt.rb
    CHANGED
    
    | 
         @@ -4,6 +4,10 @@ module NA 
     | 
|
| 
       4 
4 
     | 
    
         
             
              # Prompt Hooks
         
     | 
| 
       5 
5 
     | 
    
         
             
              module Prompt
         
     | 
| 
       6 
6 
     | 
    
         
             
                class << self
         
     | 
| 
      
 7 
     | 
    
         
            +
                  # Generate the shell prompt hook script for na
         
     | 
| 
      
 8 
     | 
    
         
            +
                  #
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # @param shell [Symbol] Shell type (:zsh, :fish, :bash)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  # @return [String] Shell script for prompt hook
         
     | 
| 
       7 
11 
     | 
    
         
             
                  def prompt_hook(shell)
         
     | 
| 
       8 
12 
     | 
    
         
             
                    case shell
         
     | 
| 
       9 
13 
     | 
    
         
             
                    when :zsh
         
     | 
| 
         @@ -14,7 +18,9 @@ module NA 
     | 
|
| 
       14 
18 
     | 
    
         
             
                              when :tag
         
     | 
| 
       15 
19 
     | 
    
         
             
                                'na tagged $(basename "$PWD")'
         
     | 
| 
       16 
20 
     | 
    
         
             
                              else
         
     | 
| 
       17 
     | 
    
         
            -
                                NA.notify( 
     | 
| 
      
 21 
     | 
    
         
            +
                                NA.notify(
         
     | 
| 
      
 22 
     | 
    
         
            +
                                  "#{NA.theme[:error]}When using a global file, a prompt hook requires `--cwd_as [tag|project]`", exit_code: 1
         
     | 
| 
      
 23 
     | 
    
         
            +
                                )
         
     | 
| 
       18 
24 
     | 
    
         
             
                              end
         
     | 
| 
       19 
25 
     | 
    
         
             
                            else
         
     | 
| 
       20 
26 
     | 
    
         
             
                              'na next'
         
     | 
| 
         @@ -31,7 +37,9 @@ module NA 
     | 
|
| 
       31 
37 
     | 
    
         
             
                              when :tag
         
     | 
| 
       32 
38 
     | 
    
         
             
                                'na tagged (basename "$PWD")'
         
     | 
| 
       33 
39 
     | 
    
         
             
                              else
         
     | 
| 
       34 
     | 
    
         
            -
                                NA.notify( 
     | 
| 
      
 40 
     | 
    
         
            +
                                NA.notify(
         
     | 
| 
      
 41 
     | 
    
         
            +
                                  "#{NA.theme[:error]}When using a global file, a prompt hook requires `--cwd_as [tag|project]`", exit_code: 1
         
     | 
| 
      
 42 
     | 
    
         
            +
                                )
         
     | 
| 
       35 
43 
     | 
    
         
             
                              end
         
     | 
| 
       36 
44 
     | 
    
         
             
                            else
         
     | 
| 
       37 
45 
     | 
    
         
             
                              'na next'
         
     | 
| 
         @@ -50,7 +58,9 @@ module NA 
     | 
|
| 
       50 
58 
     | 
    
         
             
                              when :tag
         
     | 
| 
       51 
59 
     | 
    
         
             
                                'na tagged $(basename "$PWD")'
         
     | 
| 
       52 
60 
     | 
    
         
             
                              else
         
     | 
| 
       53 
     | 
    
         
            -
                                NA.notify( 
     | 
| 
      
 61 
     | 
    
         
            +
                                NA.notify(
         
     | 
| 
      
 62 
     | 
    
         
            +
                                  "#{NA.theme[:error]}When using a global file, a prompt hook requires `--cwd_as [tag|project]`", exit_code: 1
         
     | 
| 
      
 63 
     | 
    
         
            +
                                )
         
     | 
| 
       54 
64 
     | 
    
         
             
                              end
         
     | 
| 
       55 
65 
     | 
    
         
             
                            else
         
     | 
| 
       56 
66 
     | 
    
         
             
                              'na next'
         
     | 
| 
         @@ -70,6 +80,10 @@ module NA 
     | 
|
| 
       70 
80 
     | 
    
         
             
                    end
         
     | 
| 
       71 
81 
     | 
    
         
             
                  end
         
     | 
| 
       72 
82 
     | 
    
         | 
| 
      
 83 
     | 
    
         
            +
                  # Get the configuration file path for the given shell
         
     | 
| 
      
 84 
     | 
    
         
            +
                  #
         
     | 
| 
      
 85 
     | 
    
         
            +
                  # @param shell [Symbol] Shell type
         
     | 
| 
      
 86 
     | 
    
         
            +
                  # @return [String] Path to shell config file
         
     | 
| 
       73 
87 
     | 
    
         
             
                  def prompt_file(shell)
         
     | 
| 
       74 
88 
     | 
    
         
             
                    files = {
         
     | 
| 
       75 
89 
     | 
    
         
             
                      zsh: '~/.zshrc',
         
     | 
| 
         @@ -80,6 +94,10 @@ module NA 
     | 
|
| 
       80 
94 
     | 
    
         
             
                    files[shell]
         
     | 
| 
       81 
95 
     | 
    
         
             
                  end
         
     | 
| 
       82 
96 
     | 
    
         | 
| 
      
 97 
     | 
    
         
            +
                  # Display the prompt hook script and notify user of config file
         
     | 
| 
      
 98 
     | 
    
         
            +
                  #
         
     | 
| 
      
 99 
     | 
    
         
            +
                  # @param shell [Symbol] Shell type
         
     | 
| 
      
 100 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
       83 
101 
     | 
    
         
             
                  def show_prompt_hook(shell)
         
     | 
| 
       84 
102 
     | 
    
         
             
                    file = prompt_file(shell)
         
     | 
| 
       85 
103 
     | 
    
         | 
| 
         @@ -87,6 +105,10 @@ module NA 
     | 
|
| 
       87 
105 
     | 
    
         
             
                    puts prompt_hook(shell)
         
     | 
| 
       88 
106 
     | 
    
         
             
                  end
         
     | 
| 
       89 
107 
     | 
    
         | 
| 
      
 108 
     | 
    
         
            +
                  # Install the prompt hook script into the shell config file
         
     | 
| 
      
 109 
     | 
    
         
            +
                  #
         
     | 
| 
      
 110 
     | 
    
         
            +
                  # @param shell [Symbol] Shell type
         
     | 
| 
      
 111 
     | 
    
         
            +
                  # @return [void]
         
     | 
| 
       90 
112 
     | 
    
         
             
                  def install_prompt_hook(shell)
         
     | 
| 
       91 
113 
     | 
    
         
             
                    file = prompt_file(shell)
         
     | 
| 
       92 
114 
     | 
    
         | 
    
        data/lib/na/string.rb
    CHANGED
    
    | 
         @@ -6,30 +6,20 @@ REGEX_TIME = /^#{REGEX_CLOCK}$/i.freeze 
     | 
|
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
            # String helpers
         
     | 
| 
       8 
8 
     | 
    
         
             
            class ::String
         
     | 
| 
       9 
     | 
    
         
            -
               
     | 
| 
       10 
     | 
    
         
            -
               
     | 
| 
       11 
     | 
    
         
            -
               
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
              ##
         
     | 
| 
       14 
     | 
    
         
            -
              def comment(char = "#")
         
     | 
| 
       15 
     | 
    
         
            -
                split(/\n/).map { |l| "# #{l}" }.join("\n")
         
     | 
| 
      
 9 
     | 
    
         
            +
              # Insert a comment character at the start of every line
         
     | 
| 
      
 10 
     | 
    
         
            +
              # @param char [String] The character to insert (default #)
         
     | 
| 
      
 11 
     | 
    
         
            +
              def comment(_char = '#')
         
     | 
| 
      
 12 
     | 
    
         
            +
                split("\n").map { |l| "# #{l}" }.join("\n")
         
     | 
| 
       16 
13 
     | 
    
         
             
              end
         
     | 
| 
       17 
14 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
               
     | 
| 
       19 
     | 
    
         
            -
               
     | 
| 
       20 
     | 
    
         
            -
              ##
         
     | 
| 
       21 
     | 
    
         
            -
              ## @return     [Boolean] true if object is defined and
         
     | 
| 
       22 
     | 
    
         
            -
              ##             has content
         
     | 
| 
       23 
     | 
    
         
            -
              ##
         
     | 
| 
      
 15 
     | 
    
         
            +
              # Tests if object is nil or empty
         
     | 
| 
      
 16 
     | 
    
         
            +
              # @return [Boolean] true if object is defined and has content
         
     | 
| 
       24 
17 
     | 
    
         
             
              def good?
         
     | 
| 
       25 
18 
     | 
    
         
             
                !strip.empty?
         
     | 
| 
       26 
19 
     | 
    
         
             
              end
         
     | 
| 
       27 
20 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
               
     | 
| 
       29 
     | 
    
         
            -
               
     | 
| 
       30 
     | 
    
         
            -
              ##
         
     | 
| 
       31 
     | 
    
         
            -
              ## @return     [Boolean] line is empty or comment
         
     | 
| 
       32 
     | 
    
         
            -
              ##
         
     | 
| 
      
 21 
     | 
    
         
            +
              # Test if line should be ignored
         
     | 
| 
      
 22 
     | 
    
         
            +
              # @return [Boolean] line is empty or comment
         
     | 
| 
       33 
23 
     | 
    
         
             
              def ignore?
         
     | 
| 
       34 
24 
     | 
    
         
             
                line = self
         
     | 
| 
       35 
25 
     | 
    
         
             
                line =~ /^#/ || line.strip.empty?
         
     | 
| 
         @@ -50,19 +40,16 @@ class ::String 
     | 
|
| 
       50 
40 
     | 
    
         
             
                end
         
     | 
| 
       51 
41 
     | 
    
         | 
| 
       52 
42 
     | 
    
         
             
                # IO.read(file).force_encoding('ASCII-8BIT').encode('UTF-8', invalid: :replace, undef: :replace, replace: '?')
         
     | 
| 
       53 
     | 
    
         
            -
                 
     | 
| 
      
 43 
     | 
    
         
            +
                File.read(file).force_encoding('utf-8')
         
     | 
| 
       54 
44 
     | 
    
         
             
              end
         
     | 
| 
       55 
45 
     | 
    
         | 
| 
       56 
     | 
    
         
            -
               
     | 
| 
       57 
     | 
    
         
            -
               
     | 
| 
       58 
     | 
    
         
            -
              ##
         
     | 
| 
       59 
     | 
    
         
            -
              ## @return     [Number] number of indents detected
         
     | 
| 
       60 
     | 
    
         
            -
              ##
         
     | 
| 
      
 46 
     | 
    
         
            +
              # Determine indentation level of line
         
     | 
| 
      
 47 
     | 
    
         
            +
              # @return [Number] number of indents detected
         
     | 
| 
       61 
48 
     | 
    
         
             
              def indent_level
         
     | 
| 
       62 
49 
     | 
    
         
             
                prefix = match(/(^[ \t]+)/)
         
     | 
| 
       63 
50 
     | 
    
         
             
                return 0 if prefix.nil?
         
     | 
| 
       64 
51 
     | 
    
         | 
| 
       65 
     | 
    
         
            -
                prefix[1].gsub( 
     | 
| 
      
 52 
     | 
    
         
            +
                prefix[1].gsub('    ', "\t").scan("\t").count
         
     | 
| 
       66 
53 
     | 
    
         
             
              end
         
     | 
| 
       67 
54 
     | 
    
         | 
| 
       68 
55 
     | 
    
         
             
              def action?
         
     | 
| 
         @@ -94,31 +81,22 @@ class ::String 
     | 
|
| 
       94 
81 
     | 
    
         
             
                self =~ /@#{NA.na_tag}\b/
         
     | 
| 
       95 
82 
     | 
    
         
             
              end
         
     | 
| 
       96 
83 
     | 
    
         | 
| 
       97 
     | 
    
         
            -
               
     | 
| 
       98 
     | 
    
         
            -
               
     | 
| 
       99 
     | 
    
         
            -
              ##
         
     | 
| 
       100 
     | 
    
         
            -
              ## @return     Colorized string
         
     | 
| 
       101 
     | 
    
         
            -
              ##
         
     | 
| 
      
 84 
     | 
    
         
            +
              # Colorize the dirname and filename of a path
         
     | 
| 
      
 85 
     | 
    
         
            +
              # @return [String] Colorized string
         
     | 
| 
       102 
86 
     | 
    
         
             
              def highlight_filename
         
     | 
| 
       103 
87 
     | 
    
         
             
                dir = File.dirname(self).shorten_path.trunc_middle(TTY::Screen.columns / 3)
         
     | 
| 
       104 
88 
     | 
    
         
             
                file = NA.include_ext ? File.basename(self) : File.basename(self, ".#{NA.extension}")
         
     | 
| 
       105 
89 
     | 
    
         
             
                "#{NA.theme[:dirname]}#{dir}/#{NA.theme[:filename]}#{file}{x}"
         
     | 
| 
       106 
90 
     | 
    
         
             
              end
         
     | 
| 
       107 
91 
     | 
    
         | 
| 
       108 
     | 
    
         
            -
               
     | 
| 
       109 
     | 
    
         
            -
               
     | 
| 
       110 
     | 
    
         
            -
               
     | 
| 
       111 
     | 
    
         
            -
               
     | 
| 
       112 
     | 
    
         
            -
               
     | 
| 
       113 
     | 
    
         
            -
               
     | 
| 
       114 
     | 
    
         
            -
               
     | 
| 
       115 
     | 
    
         
            -
             
     | 
| 
       116 
     | 
    
         
            -
              ## @param      last_color  [String] Color to restore after
         
     | 
| 
       117 
     | 
    
         
            -
              ##                         tag highlight
         
     | 
| 
       118 
     | 
    
         
            -
              ##
         
     | 
| 
       119 
     | 
    
         
            -
              ## @return     [String] string with @tags highlighted
         
     | 
| 
       120 
     | 
    
         
            -
              ##
         
     | 
| 
       121 
     | 
    
         
            -
              def highlight_tags(color: NA.theme[:tags], value: NA.theme[:value], parens: NA.theme[:value_parens], last_color: NA.theme[:action])
         
     | 
| 
      
 92 
     | 
    
         
            +
              # Colorize @tags with ANSI escapes
         
     | 
| 
      
 93 
     | 
    
         
            +
              # @param color [String] color (see #Color)
         
     | 
| 
      
 94 
     | 
    
         
            +
              # @param value [String] The value color template
         
     | 
| 
      
 95 
     | 
    
         
            +
              # @param parens [String] The parens color template
         
     | 
| 
      
 96 
     | 
    
         
            +
              # @param last_color [String] Color to restore after tag highlight
         
     | 
| 
      
 97 
     | 
    
         
            +
              # @return [String] string with @tags highlighted
         
     | 
| 
      
 98 
     | 
    
         
            +
              def highlight_tags(color: NA.theme[:tags], value: NA.theme[:value], parens: NA.theme[:value_parens],
         
     | 
| 
      
 99 
     | 
    
         
            +
                                 last_color: NA.theme[:action])
         
     | 
| 
       122 
100 
     | 
    
         
             
                tag_color = NA::Color.template(color)
         
     | 
| 
       123 
101 
     | 
    
         
             
                paren_color = NA::Color.template(parens)
         
     | 
| 
       124 
102 
     | 
    
         
             
                value_color = NA::Color.template(value)
         
     | 
| 
         @@ -132,21 +110,16 @@ class ::String 
     | 
|
| 
       132 
110 
     | 
    
         
             
                end
         
     | 
| 
       133 
111 
     | 
    
         
             
              end
         
     | 
| 
       134 
112 
     | 
    
         | 
| 
       135 
     | 
    
         
            -
               
     | 
| 
       136 
     | 
    
         
            -
               
     | 
| 
       137 
     | 
    
         
            -
               
     | 
| 
       138 
     | 
    
         
            -
               
     | 
| 
       139 
     | 
    
         
            -
              ##                         search
         
     | 
| 
       140 
     | 
    
         
            -
              ## @param      color       [String] The highlight color
         
     | 
| 
       141 
     | 
    
         
            -
              ##                         template
         
     | 
| 
       142 
     | 
    
         
            -
              ## @param      last_color  [String] Color to restore after
         
     | 
| 
       143 
     | 
    
         
            -
              ##                         highlight
         
     | 
| 
       144 
     | 
    
         
            -
              ##
         
     | 
| 
      
 113 
     | 
    
         
            +
              # Highlight search results
         
     | 
| 
      
 114 
     | 
    
         
            +
              # @param regexes [Array] The regexes for the search
         
     | 
| 
      
 115 
     | 
    
         
            +
              # @param color [String] The highlight color template
         
     | 
| 
      
 116 
     | 
    
         
            +
              # @param last_color [String] Color to restore after highlight
         
     | 
| 
       145 
117 
     | 
    
         
             
              def highlight_search(regexes, color: NA.theme[:search_highlight], last_color: NA.theme[:action])
         
     | 
| 
       146 
118 
     | 
    
         
             
                string = dup
         
     | 
| 
       147 
119 
     | 
    
         
             
                color = NA::Color.template(color.dup)
         
     | 
| 
       148 
120 
     | 
    
         
             
                regexes.each do |rx|
         
     | 
| 
       149 
121 
     | 
    
         
             
                  next if rx.nil?
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
       150 
123 
     | 
    
         
             
                  rx = Regexp.new(rx, Regexp::IGNORECASE) if rx.is_a?(String)
         
     | 
| 
       151 
124 
     | 
    
         | 
| 
       152 
125 
     | 
    
         
             
                  string.gsub!(rx) do
         
     | 
| 
         @@ -158,70 +131,71 @@ class ::String 
     | 
|
| 
       158 
131 
     | 
    
         
             
                string
         
     | 
| 
       159 
132 
     | 
    
         
             
              end
         
     | 
| 
       160 
133 
     | 
    
         | 
| 
      
 134 
     | 
    
         
            +
              # Truncate the string in the middle, replacing the removed section with '[...]'.
         
     | 
| 
      
 135 
     | 
    
         
            +
              # @param max [Integer] Maximum allowed length of the string
         
     | 
| 
      
 136 
     | 
    
         
            +
              # @return [String] Truncated string with middle replaced if necessary
         
     | 
| 
       161 
137 
     | 
    
         
             
              def trunc_middle(max)
         
     | 
| 
       162 
138 
     | 
    
         
             
                return self unless length > max
         
     | 
| 
       163 
139 
     | 
    
         | 
| 
       164 
140 
     | 
    
         
             
                half = (max / 2).floor - 3
         
     | 
| 
       165 
     | 
    
         
            -
                chars =  
     | 
| 
      
 141 
     | 
    
         
            +
                chars = chars
         
     | 
| 
       166 
142 
     | 
    
         
             
                pre = chars.slice(0, half)
         
     | 
| 
       167 
143 
     | 
    
         
             
                post = chars.reverse.slice(0, half).reverse
         
     | 
| 
       168 
     | 
    
         
            -
                "#{pre.join 
     | 
| 
      
 144 
     | 
    
         
            +
                "#{pre.join}[...]#{post.join}"
         
     | 
| 
       169 
145 
     | 
    
         
             
              end
         
     | 
| 
       170 
146 
     | 
    
         | 
| 
      
 147 
     | 
    
         
            +
              # Wrap the string to a given width, indenting each line and preserving tag formatting.
         
     | 
| 
      
 148 
     | 
    
         
            +
              # @param width [Integer] The maximum line width
         
     | 
| 
      
 149 
     | 
    
         
            +
              # @param indent [Integer] Number of spaces to indent each line
         
     | 
| 
      
 150 
     | 
    
         
            +
              # @return [String] Wrapped string
         
     | 
| 
       171 
151 
     | 
    
         
             
              def wrap(width, indent)
         
     | 
| 
       172 
152 
     | 
    
         
             
                return "\n#{self}" if width <= 80
         
     | 
| 
       173 
153 
     | 
    
         | 
| 
       174 
154 
     | 
    
         
             
                output = []
         
     | 
| 
       175 
155 
     | 
    
         
             
                line = []
         
     | 
| 
       176 
     | 
    
         
            -
                length =  
     | 
| 
      
 156 
     | 
    
         
            +
                length = 0
         
     | 
| 
       177 
157 
     | 
    
         
             
                gsub!(/(@\S+)\((.*?)\)/) { "#{Regexp.last_match(1)}(#{Regexp.last_match(2).gsub(/ /, '†')})" }
         
     | 
| 
       178 
158 
     | 
    
         | 
| 
       179 
159 
     | 
    
         
             
                split(' ').each do |word|
         
     | 
| 
       180 
160 
     | 
    
         
             
                  uncolored = NA::Color.uncolor(word)
         
     | 
| 
       181 
     | 
    
         
            -
                  if (length + uncolored.length + 1)  
     | 
| 
      
 161 
     | 
    
         
            +
                  if (length + uncolored.length + 1) <= width
         
     | 
| 
       182 
162 
     | 
    
         
             
                    line << word
         
     | 
| 
       183 
163 
     | 
    
         
             
                    length += uncolored.length + 1
         
     | 
| 
       184 
164 
     | 
    
         
             
                  else
         
     | 
| 
       185 
165 
     | 
    
         
             
                    output << line.join(' ')
         
     | 
| 
       186 
166 
     | 
    
         
             
                    line = [word]
         
     | 
| 
       187 
     | 
    
         
            -
                    length =  
     | 
| 
      
 167 
     | 
    
         
            +
                    length = uncolored.length + 1
         
     | 
| 
       188 
168 
     | 
    
         
             
                  end
         
     | 
| 
       189 
169 
     | 
    
         
             
                end
         
     | 
| 
       190 
170 
     | 
    
         
             
                output << line.join(' ')
         
     | 
| 
       191 
     | 
    
         
            -
                 
     | 
| 
      
 171 
     | 
    
         
            +
                # Indent all lines after the first
         
     | 
| 
      
 172 
     | 
    
         
            +
                output.each_with_index.map { |l, i| i.zero? ? l : (' ' * indent) + l }.join("\n").gsub(/†/, ' ')
         
     | 
| 
       192 
173 
     | 
    
         
             
              end
         
     | 
| 
       193 
174 
     | 
    
         | 
| 
       194 
175 
     | 
    
         
             
              # Returns the last escape sequence from a string.
         
     | 
| 
       195 
     | 
    
         
            -
              #
         
     | 
| 
       196 
     | 
    
         
            -
              # @ 
     | 
| 
       197 
     | 
    
         
            -
              #             assumption that the result of inserting them
         
     | 
| 
       198 
     | 
    
         
            -
              #             will generate the same color as was set at
         
     | 
| 
       199 
     | 
    
         
            -
              #             the end of the string. Because you can send
         
     | 
| 
       200 
     | 
    
         
            -
              #             modifiers like dark and bold separate from
         
     | 
| 
       201 
     | 
    
         
            -
              #             color codes, only using the last code may
         
     | 
| 
       202 
     | 
    
         
            -
              #             not render the same style.
         
     | 
| 
       203 
     | 
    
         
            -
              #
         
     | 
| 
       204 
     | 
    
         
            -
              # @return     [String]  All escape codes in string
         
     | 
| 
       205 
     | 
    
         
            -
              #
         
     | 
| 
      
 176 
     | 
    
         
            +
              # @note Actually returns all escape codes, with the assumption that the result of inserting them will generate the same color as was set at end of the string. Because you can send modifiers like dark and bold separate from color codes, only using the last code may not render the same style.
         
     | 
| 
      
 177 
     | 
    
         
            +
              # @return [String] All escape codes in string
         
     | 
| 
       206 
178 
     | 
    
         
             
              def last_color
         
     | 
| 
       207 
     | 
    
         
            -
                scan(/\e\[[\d;]+m/).join 
     | 
| 
      
 179 
     | 
    
         
            +
                scan(/\e\[[\d;]+m/).join.gsub("\e[0m", '')
         
     | 
| 
       208 
180 
     | 
    
         
             
              end
         
     | 
| 
       209 
181 
     | 
    
         | 
| 
       210 
     | 
    
         
            -
               
     | 
| 
       211 
     | 
    
         
            -
               
     | 
| 
       212 
     | 
    
         
            -
               
     | 
| 
       213 
     | 
    
         
            -
               
     | 
| 
       214 
     | 
    
         
            -
              ##             between characters, joins segments with
         
     | 
| 
       215 
     | 
    
         
            -
              ##             slashes and requires that last segment
         
     | 
| 
       216 
     | 
    
         
            -
              ##             match last segment of target path
         
     | 
| 
       217 
     | 
    
         
            -
              ##
         
     | 
| 
       218 
     | 
    
         
            -
              ## @param      distance      The distance allowed between characters
         
     | 
| 
       219 
     | 
    
         
            -
              ## @param      require_last  Require match to be last element in path
         
     | 
| 
       220 
     | 
    
         
            -
              ##
         
     | 
| 
      
 182 
     | 
    
         
            +
              # Convert a directory path to a regular expression
         
     | 
| 
      
 183 
     | 
    
         
            +
              # @note Splits at / or :, adds variable distance between characters, joins segments with slashes and requires that last segment match last segment of target path
         
     | 
| 
      
 184 
     | 
    
         
            +
              # @param distance [Integer] The distance allowed between characters
         
     | 
| 
      
 185 
     | 
    
         
            +
              # @param require_last [Boolean] Require match to be last element in path
         
     | 
| 
       221 
186 
     | 
    
         
             
              def dir_to_rx(distance: 1, require_last: true)
         
     | 
| 
       222 
     | 
    
         
            -
                "#{split(%r{[/:]}).map  
     | 
| 
      
 187 
     | 
    
         
            +
                "#{split(%r{[/:]}).map do |comp|
         
     | 
| 
      
 188 
     | 
    
         
            +
                  comp.chars.join(".{0,#{distance}}").gsub('*', '[^ ]*?')
         
     | 
| 
      
 189 
     | 
    
         
            +
                end.join('.*?/.*?')}#{require_last ? '[^/]*?$' : ''}"
         
     | 
| 
       223 
190 
     | 
    
         
             
              end
         
     | 
| 
       224 
191 
     | 
    
         | 
| 
      
 192 
     | 
    
         
            +
              # Check if the string matches directory patterns using any, all, and none criteria.
         
     | 
| 
      
 193 
     | 
    
         
            +
              # @param any [Array] Patterns where any match is sufficient
         
     | 
| 
      
 194 
     | 
    
         
            +
              # @param all [Array] Patterns where all must match
         
     | 
| 
      
 195 
     | 
    
         
            +
              # @param none [Array] Patterns where none must match
         
     | 
| 
      
 196 
     | 
    
         
            +
              # @param require_last [Boolean] Require last segment match
         
     | 
| 
      
 197 
     | 
    
         
            +
              # @param distance [Integer] Allowed character distance in regex
         
     | 
| 
      
 198 
     | 
    
         
            +
              # @return [Boolean] True if matches criteria
         
     | 
| 
       225 
199 
     | 
    
         
             
              def dir_matches(any: [], all: [], none: [], require_last: true, distance: 1)
         
     | 
| 
       226 
200 
     | 
    
         
             
                any_rx = any.map { |q| q.dir_to_rx(distance: distance, require_last: require_last) }
         
     | 
| 
       227 
201 
     | 
    
         
             
                all_rx = all.map { |q| q.dir_to_rx(distance: distance, require_last: require_last) }
         
     | 
| 
         @@ -229,29 +203,29 @@ class ::String 
     | 
|
| 
       229 
203 
     | 
    
         
             
                matches_any(any_rx) && matches_all(all_rx) && matches_none(none_rx)
         
     | 
| 
       230 
204 
     | 
    
         
             
              end
         
     | 
| 
       231 
205 
     | 
    
         | 
| 
      
 206 
     | 
    
         
            +
              # Check if the string matches any, all, and none regex patterns.
         
     | 
| 
      
 207 
     | 
    
         
            +
              # @param any [Array] Patterns where any match is sufficient
         
     | 
| 
      
 208 
     | 
    
         
            +
              # @param all [Array] Patterns where all must match
         
     | 
| 
      
 209 
     | 
    
         
            +
              # @param none [Array] Patterns where none must match
         
     | 
| 
      
 210 
     | 
    
         
            +
              # @return [Boolean] True if matches criteria
         
     | 
| 
       232 
211 
     | 
    
         
             
              def matches(any: [], all: [], none: [])
         
     | 
| 
       233 
212 
     | 
    
         
             
                matches_any(any) && matches_all(all) && matches_none(none)
         
     | 
| 
       234 
213 
     | 
    
         
             
              end
         
     | 
| 
       235 
214 
     | 
    
         | 
| 
       236 
     | 
    
         
            -
               
     | 
| 
       237 
     | 
    
         
            -
               
     | 
| 
       238 
     | 
    
         
            -
              ##
         
     | 
| 
       239 
     | 
    
         
            -
              ## @return     [String] Regex string
         
     | 
| 
       240 
     | 
    
         
            -
              ##
         
     | 
| 
      
 215 
     | 
    
         
            +
              # Convert wildcard characters to regular expressions
         
     | 
| 
      
 216 
     | 
    
         
            +
              # @return [String] Regex string
         
     | 
| 
       241 
217 
     | 
    
         
             
              def wildcard_to_rx
         
     | 
| 
       242 
     | 
    
         
            -
                gsub( 
     | 
| 
      
 218 
     | 
    
         
            +
                gsub('.', '\\.').gsub('?', '.').gsub('*', '[^ ]*?')
         
     | 
| 
       243 
219 
     | 
    
         
             
              end
         
     | 
| 
       244 
220 
     | 
    
         | 
| 
      
 221 
     | 
    
         
            +
              # Capitalize the first character of the string in place.
         
     | 
| 
      
 222 
     | 
    
         
            +
              # @return [String] The modified string
         
     | 
| 
       245 
223 
     | 
    
         
             
              def cap_first!
         
     | 
| 
       246 
224 
     | 
    
         
             
                replace cap_first
         
     | 
| 
       247 
225 
     | 
    
         
             
              end
         
     | 
| 
       248 
226 
     | 
    
         | 
| 
       249 
     | 
    
         
            -
               
     | 
| 
       250 
     | 
    
         
            -
               
     | 
| 
       251 
     | 
    
         
            -
              ## capitalization in place
         
     | 
| 
       252 
     | 
    
         
            -
              ##
         
     | 
| 
       253 
     | 
    
         
            -
              ## @return     [String] capitalized string
         
     | 
| 
       254 
     | 
    
         
            -
              ##
         
     | 
| 
      
 227 
     | 
    
         
            +
              # Capitalize first character, leaving other capitalization in place
         
     | 
| 
      
 228 
     | 
    
         
            +
              # @return [String] capitalized string
         
     | 
| 
       255 
229 
     | 
    
         
             
              def cap_first
         
     | 
| 
       256 
230 
     | 
    
         
             
                sub(/^([a-z])(.*)$/) do
         
     | 
| 
       257 
231 
     | 
    
         
             
                  m = Regexp.last_match
         
     | 
| 
         @@ -259,24 +233,14 @@ class ::String 
     | 
|
| 
       259 
233 
     | 
    
         
             
                end
         
     | 
| 
       260 
234 
     | 
    
         
             
              end
         
     | 
| 
       261 
235 
     | 
    
         | 
| 
       262 
     | 
    
         
            -
               
     | 
| 
       263 
     | 
    
         
            -
               
     | 
| 
       264 
     | 
    
         
            -
              ##
         
     | 
| 
       265 
     | 
    
         
            -
              ## @return     [String] shortened path
         
     | 
| 
       266 
     | 
    
         
            -
              ##
         
     | 
| 
      
 236 
     | 
    
         
            +
              # Replace home directory with tilde
         
     | 
| 
      
 237 
     | 
    
         
            +
              # @return [String] shortened path
         
     | 
| 
       267 
238 
     | 
    
         
             
              def shorten_path
         
     | 
| 
       268 
     | 
    
         
            -
                sub(/^#{ 
     | 
| 
      
 239 
     | 
    
         
            +
                sub(/^#{Dir.home}/, '~')
         
     | 
| 
       269 
240 
     | 
    
         
             
              end
         
     | 
| 
       270 
241 
     | 
    
         | 
| 
       271 
     | 
    
         
            -
               
     | 
| 
       272 
     | 
    
         
            -
               
     | 
| 
       273 
     | 
    
         
            -
              ## within configured date tags (tags whose value is
         
     | 
| 
       274 
     | 
    
         
            -
              ## expected to be a date). Modifies string in place.
         
     | 
| 
       275 
     | 
    
         
            -
              ##
         
     | 
| 
       276 
     | 
    
         
            -
              ## @param      additional_tags  [Array] An array of
         
     | 
| 
       277 
     | 
    
         
            -
              ##                              additional tags to
         
     | 
| 
       278 
     | 
    
         
            -
              ##                              consider date_tags
         
     | 
| 
       279 
     | 
    
         
            -
              ##
         
     | 
| 
      
 242 
     | 
    
         
            +
              # Convert (chronify) natural language dates within configured date tags (tags whose value is expected to be a date). Modifies string in place.
         
     | 
| 
      
 243 
     | 
    
         
            +
              # @param additional_tags [Array] An array of additional tags to consider date_tags
         
     | 
| 
       280 
244 
     | 
    
         
             
              def expand_date_tags(additional_tags = nil)
         
     | 
| 
       281 
245 
     | 
    
         
             
                iso_rx = /\d{4}-\d\d-\d\d \d\d:\d\d/
         
     | 
| 
       282 
246 
     | 
    
         | 
| 
         @@ -312,27 +276,14 @@ class ::String 
     | 
|
| 
       312 
276 
     | 
    
         
             
                end
         
     | 
| 
       313 
277 
     | 
    
         
             
              end
         
     | 
| 
       314 
278 
     | 
    
         | 
| 
       315 
     | 
    
         
            -
               
     | 
| 
       316 
     | 
    
         
            -
               
     | 
| 
       317 
     | 
    
         
            -
               
     | 
| 
       318 
     | 
    
         
            -
               
     | 
| 
       319 
     | 
    
         
            -
               
     | 
| 
       320 
     | 
    
         
            -
               
     | 
| 
       321 
     | 
    
         
            -
               
     | 
| 
       322 
     | 
    
         
            -
               
     | 
| 
       323 
     | 
    
         
            -
              ##               PDT'
         
     | 
| 
       324 
     | 
    
         
            -
              ##
         
     | 
| 
       325 
     | 
    
         
            -
              ## @param      options  Additional options
         
     | 
| 
       326 
     | 
    
         
            -
              ##
         
     | 
| 
       327 
     | 
    
         
            -
              ## @option options :future [Boolean] assume future date
         
     | 
| 
       328 
     | 
    
         
            -
              ##                                   (default: false)
         
     | 
| 
       329 
     | 
    
         
            -
              ##
         
     | 
| 
       330 
     | 
    
         
            -
              ## @option options :guess  [Symbol] :begin or :end to
         
     | 
| 
       331 
     | 
    
         
            -
              ##                                   assume beginning or end of
         
     | 
| 
       332 
     | 
    
         
            -
              ##                                   arbitrary time range
         
     | 
| 
       333 
     | 
    
         
            -
              ##
         
     | 
| 
       334 
     | 
    
         
            -
              ## @return     [DateTime] result
         
     | 
| 
       335 
     | 
    
         
            -
              ##
         
     | 
| 
      
 279 
     | 
    
         
            +
              # Converts input string into a Time object when input takes on the following formats:
         
     | 
| 
      
 280 
     | 
    
         
            +
              #   - interval format e.g. '1d2h30m', '45m' etc.
         
     | 
| 
      
 281 
     | 
    
         
            +
              #   - a semantic phrase e.g. 'yesterday 5:30pm'
         
     | 
| 
      
 282 
     | 
    
         
            +
              #   - a strftime e.g. '2016-03-15 15:32:04 PDT'
         
     | 
| 
      
 283 
     | 
    
         
            +
              # @param options [Hash] Additional options
         
     | 
| 
      
 284 
     | 
    
         
            +
              # @option options :future [Boolean] assume future date (default: false)
         
     | 
| 
      
 285 
     | 
    
         
            +
              # @option options :guess [Symbol] :begin or :end to assume beginning or end of arbitrary time range
         
     | 
| 
      
 286 
     | 
    
         
            +
              # @return [DateTime] result
         
     | 
| 
       336 
287 
     | 
    
         
             
              def chronify(**options)
         
     | 
| 
       337 
288 
     | 
    
         
             
                now = Time.now
         
     | 
| 
       338 
289 
     | 
    
         
             
                raise StandardError, "Invalid time expression #{inspect}" if to_s.strip == ''
         
     | 
| 
         @@ -353,7 +304,7 @@ class ::String 
     | 
|
| 
       353 
304 
     | 
    
         
             
                else
         
     | 
| 
       354 
305 
     | 
    
         
             
                  date_string = dup
         
     | 
| 
       355 
306 
     | 
    
         
             
                  date_string = 'today' if date_string.match(REGEX_DAY) && now.strftime('%a') =~ /^#{Regexp.last_match(1)}/i
         
     | 
| 
       356 
     | 
    
         
            -
                  date_string = "#{options[:context] 
     | 
| 
      
 307 
     | 
    
         
            +
                  date_string = "#{options[:context]} #{date_string}" if date_string =~ REGEX_TIME && options[:context]
         
     | 
| 
       357 
308 
     | 
    
         | 
| 
       358 
309 
     | 
    
         
             
                  require 'chronic' unless defined?(Chronic)
         
     | 
| 
       359 
310 
     | 
    
         
             
                  res = Chronic.parse(date_string, {
         
     | 
| 
         @@ -368,8 +319,12 @@ class ::String 
     | 
|
| 
       368 
319 
     | 
    
         
             
                res
         
     | 
| 
       369 
320 
     | 
    
         
             
              end
         
     | 
| 
       370 
321 
     | 
    
         | 
| 
      
 322 
     | 
    
         
            +
              # Private helper methods for pattern matching
         
     | 
| 
       371 
323 
     | 
    
         
             
              private
         
     | 
| 
       372 
324 
     | 
    
         | 
| 
      
 325 
     | 
    
         
            +
              # Returns true if none of the regexes match the string.
         
     | 
| 
      
 326 
     | 
    
         
            +
              # @param regexes [Array] Array of regex patterns
         
     | 
| 
      
 327 
     | 
    
         
            +
              # @return [Boolean] True if none match
         
     | 
| 
       373 
328 
     | 
    
         
             
              def matches_none(regexes)
         
     | 
| 
       374 
329 
     | 
    
         
             
                regexes.each do |rx|
         
     | 
| 
       375 
330 
     | 
    
         
             
                  return false if match(Regexp.new(rx, Regexp::IGNORECASE))
         
     | 
| 
         @@ -377,6 +332,9 @@ class ::String 
     | 
|
| 
       377 
332 
     | 
    
         
             
                true
         
     | 
| 
       378 
333 
     | 
    
         
             
              end
         
     | 
| 
       379 
334 
     | 
    
         | 
| 
      
 335 
     | 
    
         
            +
              # Returns true if any of the regexes match the string.
         
     | 
| 
      
 336 
     | 
    
         
            +
              # @param regexes [Array] Array of regex patterns
         
     | 
| 
      
 337 
     | 
    
         
            +
              # @return [Boolean] True if any match
         
     | 
| 
       380 
338 
     | 
    
         
             
              def matches_any(regexes)
         
     | 
| 
       381 
339 
     | 
    
         
             
                regexes.each do |rx|
         
     | 
| 
       382 
340 
     | 
    
         
             
                  return true if match(Regexp.new(rx, Regexp::IGNORECASE))
         
     | 
| 
         @@ -384,6 +342,9 @@ class ::String 
     | 
|
| 
       384 
342 
     | 
    
         
             
                false
         
     | 
| 
       385 
343 
     | 
    
         
             
              end
         
     | 
| 
       386 
344 
     | 
    
         | 
| 
      
 345 
     | 
    
         
            +
              # Returns true if all of the regexes match the string.
         
     | 
| 
      
 346 
     | 
    
         
            +
              # @param regexes [Array] Array of regex patterns
         
     | 
| 
      
 347 
     | 
    
         
            +
              # @return [Boolean] True if all match
         
     | 
| 
       387 
348 
     | 
    
         
             
              def matches_all(regexes)
         
     | 
| 
       388 
349 
     | 
    
         
             
                regexes.each do |rx|
         
     | 
| 
       389 
350 
     | 
    
         
             
                  return false unless match(Regexp.new(rx, Regexp::IGNORECASE))
         
     |