shellopts 2.0.8 → 2.0.11
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/TODO +5 -5
 - data/lib/ext/array.rb +3 -1
 - data/lib/shellopts/analyzer.rb +47 -18
 - data/lib/shellopts/formatter.rb +64 -72
 - data/lib/shellopts/grammar.rb +16 -11
 - data/lib/shellopts/parser.rb +13 -18
 - data/lib/shellopts/program.rb +13 -4
 - data/lib/shellopts/renderer.rb +37 -48
 - data/lib/shellopts/version.rb +1 -1
 - data/lib/shellopts.rb +31 -157
 - data/main +4 -1
 - metadata +2 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 485e81c58a890240d2a77e24a5f3a7a291551da968f395f3aaa4f5b2dac3cc9b
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 2d6c92ca01ce1207f6311cf9b0e9bab3e555e309142ba6916d589396f215afb8
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 31f3801b98585bed378ff77ae6632d18951ef37a49b3cfde843df9718dc5b46da40e777dff3f7b29e01bf8b32f2ebe0e1e7ab76ddb66744a461acf0061f75b1f
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: d72bcf1d58e9afdf4f92477af1f2824e01031a46b4cacf5283878ce1045ad91dd930c1c1a15cd588a2552a4ad90fe5ac3add6019b50e099693aa184b7747153f
         
     | 
    
        data/TODO
    CHANGED
    
    | 
         @@ -1,18 +1,13 @@ 
     | 
|
| 
       1 
1 
     | 
    
         | 
| 
       2 
     | 
    
         
            -
            o Add brackets to optional option arguments: '--all=FILE?' -> '--all[=FILE]'
         
     | 
| 
       3 
2 
     | 
    
         
             
            o Ignore all text after ' # ' (doesn't conflict with option flag)
         
     | 
| 
       4 
3 
     | 
    
         
             
            o Command aliases
         
     | 
| 
       5 
4 
     | 
    
         
             
            o Add user-defined setions
         
     | 
| 
       6 
5 
     | 
    
         
             
            o Add a SOURCE section with link to git repo
         
     | 
| 
       7 
6 
     | 
    
         
             
            o Bullet-lists
         
     | 
| 
       8 
7 
     | 
    
         
             
            o Allow a USAGE section (and NAME)
         
     | 
| 
       9 
     | 
    
         
            -
            o Find source in code an adjust line number in error messages 
         
     | 
| 
       10 
     | 
    
         
            -
            o Rename line and char to lineno and charno
         
     | 
| 
       11 
8 
     | 
    
         
             
            o Client-defined argument types
         
     | 
| 
       12 
9 
     | 
    
         
             
            o Rename Expr -> ?
         
     | 
| 
       13 
10 
     | 
    
         
             
            o Find clean(er) procedural object model
         
     | 
| 
       14 
     | 
    
         
            -
            o Allow assignment to options (this makes practical stuff easier)
         
     | 
| 
       15 
     | 
    
         
            -
            o Special handling of --help arguments so that '--help command' is possible
         
     | 
| 
       16 
11 
     | 
    
         
             
            o Support for paging of help:
         
     | 
| 
       17 
12 
     | 
    
         
             
                begin 
         
     | 
| 
       18 
13 
     | 
    
         
             
                  file = Tempfile.new("prick") 
         
     | 
| 
         @@ -23,6 +18,11 @@ o Support for paging of help: 
     | 
|
| 
       23 
18 
     | 
    
         
             
                  file.close 
         
     | 
| 
       24 
19 
     | 
    
         
             
                end 
         
     | 
| 
       25 
20 
     | 
    
         | 
| 
      
 21 
     | 
    
         
            +
            + Special handling of --help arguments so that '--help command' is possible
         
     | 
| 
      
 22 
     | 
    
         
            +
            + Allow assignment to options (this makes practical stuff easier)
         
     | 
| 
      
 23 
     | 
    
         
            +
            + Rename line and char to lineno and charno
         
     | 
| 
      
 24 
     | 
    
         
            +
            + Find source in code an adjust line number in error messages 
         
     | 
| 
      
 25 
     | 
    
         
            +
            + Add brackets to optional option arguments: '--all=FILE?' -> '--all[=FILE]'
         
     | 
| 
       26 
26 
     | 
    
         
             
            + Bold text output
         
     | 
| 
       27 
27 
     | 
    
         
             
            + Recursive format of commands
         
     | 
| 
       28 
28 
     | 
    
         
             
            + Rename Compiler -> Interpreter
         
     | 
    
        data/lib/ext/array.rb
    CHANGED
    
    | 
         @@ -41,7 +41,9 @@ module Ext 
     | 
|
| 
       41 
41 
     | 
    
         | 
| 
       42 
42 
     | 
    
         
             
                module Wrap
         
     | 
| 
       43 
43 
     | 
    
         
             
                  refine ::Array do
         
     | 
| 
       44 
     | 
    
         
            -
                    # Concatenate  
     | 
| 
      
 44 
     | 
    
         
            +
                    # Concatenate array of words into lines that are at most +width+
         
     | 
| 
      
 45 
     | 
    
         
            +
                    # characters wide. +curr+ is the initial number of characters already
         
     | 
| 
      
 46 
     | 
    
         
            +
                    # used on the first line
         
     | 
| 
       45 
47 
     | 
    
         
             
                    def wrap(width, curr = 0)
         
     | 
| 
       46 
48 
     | 
    
         
             
                      lines = [[]]
         
     | 
| 
       47 
49 
     | 
    
         
             
                      curr -= 1 # Simplifies conditions below
         
     | 
    
        data/lib/shellopts/analyzer.rb
    CHANGED
    
    | 
         @@ -20,10 +20,6 @@ module ShellOpts 
     | 
|
| 
       20 
20 
     | 
    
         
             
                end
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
       22 
22 
     | 
    
         
             
                class Command
         
     | 
| 
       23 
     | 
    
         
            -
                  def set_supercommand
         
     | 
| 
       24 
     | 
    
         
            -
                    commands.each { |child| child.instance_variable_set(:@supercommand, self) }
         
     | 
| 
       25 
     | 
    
         
            -
                  end
         
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
23 
     | 
    
         
             
                  def collect_options
         
     | 
| 
       28 
24 
     | 
    
         
             
                    @options = option_groups.map(&:options).flatten
         
     | 
| 
       29 
25 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -54,9 +50,9 @@ module ShellOpts 
     | 
|
| 
       54 
50 
     | 
    
         
             
                    }
         
     | 
| 
       55 
51 
     | 
    
         
             
                  end
         
     | 
| 
       56 
52 
     | 
    
         | 
| 
      
 53 
     | 
    
         
            +
                  # TODO Check for dash-collision
         
     | 
| 
       57 
54 
     | 
    
         
             
                  def compute_command_hashes
         
     | 
| 
       58 
55 
     | 
    
         
             
                    commands.each { |command|
         
     | 
| 
       59 
     | 
    
         
            -
                      # TODO Check for dash-collision
         
     | 
| 
       60 
56 
     | 
    
         
             
                      !@commands_hash.key?(command.name) or 
         
     | 
| 
       61 
57 
     | 
    
         
             
                          analyzer_error command.token, "Duplicate command name: #{command.name}"
         
     | 
| 
       62 
58 
     | 
    
         
             
                      @commands_hash[command.name] = command
         
     | 
| 
         @@ -76,41 +72,74 @@ module ShellOpts 
     | 
|
| 
       76 
72 
     | 
    
         
             
                  @grammar = grammar
         
     | 
| 
       77 
73 
     | 
    
         
             
                end
         
     | 
| 
       78 
74 
     | 
    
         | 
| 
       79 
     | 
    
         
            -
                 
     | 
| 
       80 
     | 
    
         
            -
             
     | 
| 
      
 75 
     | 
    
         
            +
                def create_implicit_commands(cmd)
         
     | 
| 
      
 76 
     | 
    
         
            +
                  path = cmd.path[0..-2]
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                  
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                # Link up commands with supercommands. This is only done for commands that
         
     | 
| 
      
 82 
     | 
    
         
            +
                # are nested within a different command than it belongs to. The
         
     | 
| 
      
 83 
     | 
    
         
            +
                # parent/child relationship is not changed Example:
         
     | 
| 
      
 84 
     | 
    
         
            +
                #
         
     | 
| 
      
 85 
     | 
    
         
            +
                #   cmd!
         
     | 
| 
      
 86 
     | 
    
         
            +
                #   cmd.subcmd! 
         
     | 
| 
      
 87 
     | 
    
         
            +
                #
         
     | 
| 
      
 88 
     | 
    
         
            +
                # Here subcmd is added to cmd's list of commands. It keeps its position in
         
     | 
| 
      
 89 
     | 
    
         
            +
                # the program's parent/child relationship so that documentation will print the
         
     | 
| 
      
 90 
     | 
    
         
            +
                # commands in the given order and with the given indentation level
         
     | 
| 
      
 91 
     | 
    
         
            +
                #
         
     | 
| 
      
 92 
     | 
    
         
            +
                def link_commands
         
     | 
| 
       81 
93 
     | 
    
         
             
                  # We can't use Command#[] at this point so we collect the commands here
         
     | 
| 
       82 
94 
     | 
    
         
             
                  h = {}
         
     | 
| 
       83 
95 
     | 
    
         
             
                  @grammar.traverse(Grammar::Command) { |command|
         
     | 
| 
       84 
96 
     | 
    
         
             
                    h[command.path] = command
         
     | 
| 
      
 97 
     | 
    
         
            +
                    # TODO: Pick up parent-less commands
         
     | 
| 
      
 98 
     | 
    
         
            +
                  }
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                  # Command to link
         
     | 
| 
      
 101 
     | 
    
         
            +
                  link = []
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                  # Create implicit commands
         
     | 
| 
      
 104 
     | 
    
         
            +
                  h.sort { |l,r| l.size <=> r.size }.each { |path, command|
         
     | 
| 
      
 105 
     | 
    
         
            +
                    path = path[0..-2]
         
     | 
| 
      
 106 
     | 
    
         
            +
                    while !h.key?(path)
         
     | 
| 
      
 107 
     | 
    
         
            +
                      cmd = Grammar::Command.new(nil, command.token)
         
     | 
| 
      
 108 
     | 
    
         
            +
                      cmd.set_name(path.last.to_s.sub(/!/, ""), path.dup)
         
     | 
| 
      
 109 
     | 
    
         
            +
                      link << cmd
         
     | 
| 
      
 110 
     | 
    
         
            +
                      h[cmd.path] = cmd
         
     | 
| 
      
 111 
     | 
    
         
            +
                      path.pop
         
     | 
| 
      
 112 
     | 
    
         
            +
                    end
         
     | 
| 
       85 
113 
     | 
    
         
             
                  }
         
     | 
| 
       86 
114 
     | 
    
         | 
| 
       87 
     | 
    
         
            -
                  # Find commands to  
     | 
| 
      
 115 
     | 
    
         
            +
                  # Find commands to link
         
     | 
| 
       88 
116 
     | 
    
         
             
                  #
         
     | 
| 
       89 
     | 
    
         
            -
                  # Commands are  
     | 
| 
       90 
     | 
    
         
            -
                  # not defined when the data structure changes beneath it
         
     | 
| 
       91 
     | 
    
         
            -
                   
     | 
| 
      
 117 
     | 
    
         
            +
                  # Commands are linked in two steps because the behaviour of #traverse is
         
     | 
| 
      
 118 
     | 
    
         
            +
                  # not defined when the data structure changes beneath it. (FIXME: Does it
         
     | 
| 
      
 119 
     | 
    
         
            +
                  # change when we don't touch the parent/child relationship?)
         
     | 
| 
       92 
120 
     | 
    
         
             
                  @grammar.traverse(Grammar::Command) { |command|
         
     | 
| 
       93 
121 
     | 
    
         
             
                    if command.path.size > 1 && command.parent && command.parent.path != command.path[0..-2]
         
     | 
| 
       94 
     | 
    
         
            -
             
     | 
| 
      
 122 
     | 
    
         
            +
            #       if command.path.size > 1 && command.parent.path != command.path[0..-2]
         
     | 
| 
      
 123 
     | 
    
         
            +
                      link << command
         
     | 
| 
       95 
124 
     | 
    
         
             
                    else
         
     | 
| 
       96 
125 
     | 
    
         
             
                      command.instance_variable_set(:@command, command.parent)
         
     | 
| 
       97 
126 
     | 
    
         
             
                    end
         
     | 
| 
       98 
127 
     | 
    
         
             
                  }
         
     | 
| 
       99 
128 
     | 
    
         | 
| 
       100 
     | 
    
         
            -
                  #  
     | 
| 
       101 
     | 
    
         
            -
                   
     | 
| 
       102 
     | 
    
         
            -
                     
     | 
| 
       103 
     | 
    
         
            -
                     
     | 
| 
      
 129 
     | 
    
         
            +
                  # Link commands but do not change parent/child relationship
         
     | 
| 
      
 130 
     | 
    
         
            +
                  link.each { |command|
         
     | 
| 
      
 131 
     | 
    
         
            +
                    path = command.path[0..-2]
         
     | 
| 
      
 132 
     | 
    
         
            +
                    path.pop while (supercommand = h[path]).nil?
         
     | 
| 
      
 133 
     | 
    
         
            +
                    command.parent.commands.delete(command) if command.parent
         
     | 
| 
       104 
134 
     | 
    
         
             
                    supercommand.commands << command
         
     | 
| 
       105 
135 
     | 
    
         
             
                    command.instance_variable_set(:@command, supercommand)
         
     | 
| 
       106 
136 
     | 
    
         
             
                  }
         
     | 
| 
       107 
137 
     | 
    
         
             
                end
         
     | 
| 
       108 
138 
     | 
    
         | 
| 
       109 
139 
     | 
    
         
             
                def analyze()
         
     | 
| 
       110 
     | 
    
         
            -
                   
     | 
| 
      
 140 
     | 
    
         
            +
                  link_commands
         
     | 
| 
       111 
141 
     | 
    
         | 
| 
       112 
142 
     | 
    
         
             
                  @grammar.traverse(Grammar::Command) { |command|
         
     | 
| 
       113 
     | 
    
         
            -
                    command.set_supercommand
         
     | 
| 
       114 
143 
     | 
    
         
             
                    command.reorder_options
         
     | 
| 
       115 
144 
     | 
    
         
             
                    command.collect_options
         
     | 
| 
       116 
145 
     | 
    
         
             
                    command.compute_option_hashes
         
     | 
    
        data/lib/shellopts/formatter.rb
    CHANGED
    
    | 
         @@ -1,12 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require 'terminfo'
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            # TODO: Move to ext/indented_io.rb
         
     | 
| 
       4 
     | 
    
         
            -
            module IndentedIO
         
     | 
| 
       5 
     | 
    
         
            -
              class IndentedIO
         
     | 
| 
       6 
     | 
    
         
            -
                def margin() combined_indent.size end
         
     | 
| 
       7 
     | 
    
         
            -
              end
         
     | 
| 
       8 
     | 
    
         
            -
            end
         
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
3 
     | 
    
         
             
            module ShellOpts
         
     | 
| 
       11 
4 
     | 
    
         
             
              module Grammar
         
     | 
| 
       12 
5 
     | 
    
         
             
                class Node
         
     | 
| 
         @@ -81,7 +74,15 @@ module ShellOpts 
     | 
|
| 
       81 
74 
     | 
    
         
             
                  end
         
     | 
| 
       82 
75 
     | 
    
         | 
| 
       83 
76 
     | 
    
         
             
                  def puts_descr(prefix, brief: !self.brief.nil?, name: :path)
         
     | 
| 
       84 
     | 
    
         
            -
                     
     | 
| 
      
 77 
     | 
    
         
            +
                    # Use one-line mode if all options are declared on one line
         
     | 
| 
      
 78 
     | 
    
         
            +
                    if options.all? { |option| option.token.lineno == token.lineno }
         
     | 
| 
      
 79 
     | 
    
         
            +
                      puts Ansi.bold([prefix, render(:single, Formatter.rest)].flatten.compact.join(" "))
         
     | 
| 
      
 80 
     | 
    
         
            +
                      puts_options = false
         
     | 
| 
      
 81 
     | 
    
         
            +
                    else
         
     | 
| 
      
 82 
     | 
    
         
            +
                      puts Ansi.bold([prefix, render(:abbr, Formatter.rest)].flatten.compact.join(" "))
         
     | 
| 
      
 83 
     | 
    
         
            +
                      puts_options = true
         
     | 
| 
      
 84 
     | 
    
         
            +
                    end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
       85 
86 
     | 
    
         
             
                    indent {
         
     | 
| 
       86 
87 
     | 
    
         
             
                      if brief
         
     | 
| 
       87 
88 
     | 
    
         
             
                        puts self.brief.words.wrap(Formatter.rest)
         
     | 
| 
         @@ -93,7 +94,10 @@ module ShellOpts 
     | 
|
| 
       93 
94 
     | 
    
         | 
| 
       94 
95 
     | 
    
         
             
                          if child.is_a?(Command)
         
     | 
| 
       95 
96 
     | 
    
         
             
                            child.puts_descr(prefix, name: :path)
         
     | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
      
 97 
     | 
    
         
            +
                          elsif child.is_a?(OptionGroup)
         
     | 
| 
      
 98 
     | 
    
         
            +
                            child.puts_descr if puts_options
         
     | 
| 
      
 99 
     | 
    
         
            +
                            newline = false
         
     | 
| 
      
 100 
     | 
    
         
            +
                          else
         
     | 
| 
       97 
101 
     | 
    
         
             
                            child.puts_descr
         
     | 
| 
       98 
102 
     | 
    
         
             
                          end
         
     | 
| 
       99 
103 
     | 
    
         
             
                        }
         
     | 
| 
         @@ -112,37 +116,34 @@ module ShellOpts 
     | 
|
| 
       112 
116 
     | 
    
         | 
| 
       113 
117 
     | 
    
         
             
                    section = {
         
     | 
| 
       114 
118 
     | 
    
         
             
                      Paragraph => "DESCRIPTION",
         
     | 
| 
       115 
     | 
    
         
            -
                      OptionGroup => " 
     | 
| 
       116 
     | 
    
         
            -
                      Command => " 
     | 
| 
      
 119 
     | 
    
         
            +
                      OptionGroup => "OPTION",
         
     | 
| 
      
 120 
     | 
    
         
            +
                      Command => "COMMAND"
         
     | 
| 
       117 
121 
     | 
    
         
             
                    }
         
     | 
| 
       118 
122 
     | 
    
         | 
| 
      
 123 
     | 
    
         
            +
                    seen_sections = {}
         
     | 
| 
       119 
124 
     | 
    
         
             
                    newline = false # True if a newline should be printed before child 
         
     | 
| 
       120 
125 
     | 
    
         
             
                    indent {
         
     | 
| 
       121 
126 
     | 
    
         
             
                      children.each { |child|
         
     | 
| 
       122 
     | 
    
         
            -
                         
     | 
| 
       123 
     | 
    
         
            -
            # 
     | 
| 
       124 
     | 
    
         
            -
                           
     | 
| 
       125 
     | 
    
         
            -
                          indent(-1).puts Ansi.bold child.name
         
     | 
| 
       126 
     | 
    
         
            -
                          section.delete_if { |_,v| v == child.name }
         
     | 
| 
      
 127 
     | 
    
         
            +
                        klass = child.is_a?(Section) ?  section.key(child.name) : child.class
         
     | 
| 
      
 128 
     | 
    
         
            +
                        if s = section[klass] # Implicit section
         
     | 
| 
      
 129 
     | 
    
         
            +
                          section.delete(klass)
         
     | 
| 
       127 
130 
     | 
    
         
             
                          section.delete(Paragraph)
         
     | 
| 
       128 
     | 
    
         
            -
                           
     | 
| 
       129 
     | 
    
         
            -
             
     | 
| 
       130 
     | 
    
         
            -
             
     | 
| 
       131 
     | 
    
         
            -
             
     | 
| 
       132 
     | 
    
         
            -
                           
     | 
| 
      
 131 
     | 
    
         
            +
                          if klass <= OptionGroup
         
     | 
| 
      
 132 
     | 
    
         
            +
                            s += "S" if options.size > 1
         
     | 
| 
      
 133 
     | 
    
         
            +
                          elsif klass <= Command
         
     | 
| 
      
 134 
     | 
    
         
            +
                            s += "S" if commands.size > 1 || commands.size == 1 && commands.first.commands.size > 1
         
     | 
| 
      
 135 
     | 
    
         
            +
                          end
         
     | 
| 
      
 136 
     | 
    
         
            +
                          puts
         
     | 
| 
       133 
137 
     | 
    
         
             
                          indent(-1).puts Ansi.bold s
         
     | 
| 
       134 
     | 
    
         
            -
                          section.delete(child.class)
         
     | 
| 
       135 
     | 
    
         
            -
                          section.delete(Paragraph)
         
     | 
| 
       136 
138 
     | 
    
         
             
                          newline = false
         
     | 
| 
       137 
     | 
    
         
            -
             
     | 
| 
       138 
     | 
    
         
            -
            # 
     | 
| 
      
 139 
     | 
    
         
            +
                          next if child.is_a?(Section)
         
     | 
| 
      
 140 
     | 
    
         
            +
                        else # Any other node adds a newline
         
     | 
| 
       139 
141 
     | 
    
         
             
                          puts if newline
         
     | 
| 
       140 
142 
     | 
    
         
             
                          newline = true
         
     | 
| 
       141 
143 
     | 
    
         
             
                        end
         
     | 
| 
       142 
144 
     | 
    
         | 
| 
       143 
145 
     | 
    
         
             
                        if child.is_a?(Command)
         
     | 
| 
       144 
     | 
    
         
            -
             
     | 
| 
       145 
     | 
    
         
            -
                          prefix = child.supercommand == self ? nil : child.supercommand&.name
         
     | 
| 
      
 146 
     | 
    
         
            +
                          prefix = child.path[path.size..-2].map { |sym| sym.to_s.sub(/!/, "") }
         
     | 
| 
       146 
147 
     | 
    
         
             
                          child.puts_descr(prefix, brief: false, name: :path)
         
     | 
| 
       147 
148 
     | 
    
         
             
                          newline = true
         
     | 
| 
       148 
149 
     | 
    
         
             
                         else
         
     | 
| 
         @@ -153,9 +154,10 @@ module ShellOpts 
     | 
|
| 
       153 
154 
     | 
    
         | 
| 
       154 
155 
     | 
    
         
             
                      # Also emit commands not declared in nested scope
         
     | 
| 
       155 
156 
     | 
    
         
             
                      (commands - children.select { |child| child.is_a?(Command) }).each { |cmd|
         
     | 
| 
      
 157 
     | 
    
         
            +
                        next if cmd.parent.nil? # Skip implicit commands
         
     | 
| 
       156 
158 
     | 
    
         
             
                        puts if newline
         
     | 
| 
       157 
159 
     | 
    
         
             
                        newline = true
         
     | 
| 
       158 
     | 
    
         
            -
                        prefix = cmd. 
     | 
| 
      
 160 
     | 
    
         
            +
                        prefix = cmd.command == self ? nil : cmd.command&.name
         
     | 
| 
       159 
161 
     | 
    
         
             
                        cmd.puts_descr(prefix, brief: false, name: path)
         
     | 
| 
       160 
162 
     | 
    
         
             
                      }
         
     | 
| 
       161 
163 
     | 
    
         
             
                    }
         
     | 
| 
         @@ -207,7 +209,10 @@ module ShellOpts 
     | 
|
| 
       207 
209 
     | 
    
         
             
                BRIEF_COL1_MAX_WIDTH = 40
         
     | 
| 
       208 
210 
     | 
    
         | 
| 
       209 
211 
     | 
    
         
             
                # Minimum width of second column in brief option and command lists
         
     | 
| 
       210 
     | 
    
         
            -
                 
     | 
| 
      
 212 
     | 
    
         
            +
                BRIEF_COL2_MIN_WIDTH = 30
         
     | 
| 
      
 213 
     | 
    
         
            +
             
     | 
| 
      
 214 
     | 
    
         
            +
                # Maximum width of second column in brief option and command lists
         
     | 
| 
      
 215 
     | 
    
         
            +
                BRIEF_COL2_MAX_WIDTH = 70
         
     | 
| 
       211 
216 
     | 
    
         | 
| 
       212 
217 
     | 
    
         
             
                # Indent to use in help output
         
     | 
| 
       213 
218 
     | 
    
         
             
                HELP_INDENT = 4
         
     | 
| 
         @@ -217,29 +222,21 @@ module ShellOpts 
     | 
|
| 
       217 
222 
     | 
    
         | 
| 
       218 
223 
     | 
    
         
             
                # Usage string in error messages
         
     | 
| 
       219 
224 
     | 
    
         
             
                def self.usage(subject)
         
     | 
| 
       220 
     | 
    
         
            -
                   
     | 
| 
       221 
     | 
    
         
            -
                  @command_prefix =  
     | 
| 
      
 225 
     | 
    
         
            +
                  command = Grammar::Command.command(subject)
         
     | 
| 
      
 226 
     | 
    
         
            +
                  @command_prefix = command.ancestors.map { |node| node.name + " " }.join
         
     | 
| 
       222 
227 
     | 
    
         
             
                  setup_indent(1) {
         
     | 
| 
       223 
228 
     | 
    
         
             
                    print lead = "#{USAGE_STRING}: "
         
     | 
| 
       224 
     | 
    
         
            -
                    indent(lead.size, ' ', bol: false) {  
     | 
| 
      
 229 
     | 
    
         
            +
                    indent(lead.size, ' ', bol: false) { command.puts_usage }
         
     | 
| 
       225 
230 
     | 
    
         
             
                  }
         
     | 
| 
       226 
231 
     | 
    
         
             
                end
         
     | 
| 
       227 
232 
     | 
    
         | 
| 
       228 
     | 
    
         
            -
            #   # TODO
         
     | 
| 
       229 
     | 
    
         
            -
            #   def self.usage=(usage_lambda)
         
     | 
| 
       230 
     | 
    
         
            -
            #   end
         
     | 
| 
       231 
     | 
    
         
            -
             
     | 
| 
       232 
233 
     | 
    
         
             
                # When the user gives a -h option
         
     | 
| 
       233 
     | 
    
         
            -
                def self.brief( 
     | 
| 
       234 
     | 
    
         
            -
                  command = Grammar::Command.command( 
     | 
| 
      
 234 
     | 
    
         
            +
                def self.brief(subject)
         
     | 
| 
      
 235 
     | 
    
         
            +
                  command = Grammar::Command.command(subject)
         
     | 
| 
       235 
236 
     | 
    
         
             
                  @command_prefix = command.ancestors.map { |node| node.name + " " }.join
         
     | 
| 
       236 
237 
     | 
    
         
             
                  setup_indent(BRIEF_INDENT) { command.puts_brief }
         
     | 
| 
       237 
238 
     | 
    
         
             
                end
         
     | 
| 
       238 
239 
     | 
    
         | 
| 
       239 
     | 
    
         
            -
            #   # TODO
         
     | 
| 
       240 
     | 
    
         
            -
            #   def self.brief=(brief_lambda)
         
     | 
| 
       241 
     | 
    
         
            -
            #   end
         
     | 
| 
       242 
     | 
    
         
            -
             
     | 
| 
       243 
240 
     | 
    
         
             
                # When the user gives a --help option
         
     | 
| 
       244 
241 
     | 
    
         
             
                def self.help(subject)
         
     | 
| 
       245 
242 
     | 
    
         
             
                  subject = Grammar::Command.command(subject)
         
     | 
| 
         @@ -253,18 +250,6 @@ module ShellOpts 
     | 
|
| 
       253 
250 
     | 
    
         
             
                  obj.is_a?(Grammar::Command) ? obj : obj.__grammar__
         
     | 
| 
       254 
251 
     | 
    
         
             
                end
         
     | 
| 
       255 
252 
     | 
    
         | 
| 
       256 
     | 
    
         
            -
            #   # TODO
         
     | 
| 
       257 
     | 
    
         
            -
            #   def self.help_w_lambda(program)
         
     | 
| 
       258 
     | 
    
         
            -
            #     if @help_lambda
         
     | 
| 
       259 
     | 
    
         
            -
            #       #
         
     | 
| 
       260 
     | 
    
         
            -
            #     else
         
     | 
| 
       261 
     | 
    
         
            -
            #       program = Grammar::Command.command(program)
         
     | 
| 
       262 
     | 
    
         
            -
            #       setup_indent(HELP_INDENT) { program.puts_descr }
         
     | 
| 
       263 
     | 
    
         
            -
            #     end
         
     | 
| 
       264 
     | 
    
         
            -
            #   end
         
     | 
| 
       265 
     | 
    
         
            -
            #
         
     | 
| 
       266 
     | 
    
         
            -
            #   def self.help=(help_lambda) @help_lambda end
         
     | 
| 
       267 
     | 
    
         
            -
             
     | 
| 
       268 
253 
     | 
    
         
             
                def self.puts_columns(widths, fields)
         
     | 
| 
       269 
254 
     | 
    
         
             
                  l = []
         
     | 
| 
       270 
255 
     | 
    
         
             
                  first_width, second_width = *widths
         
     | 
| 
         @@ -275,40 +260,47 @@ module ShellOpts 
     | 
|
| 
       275 
260 
     | 
    
         
             
                      puts first
         
     | 
| 
       276 
261 
     | 
    
         
             
                      indent(first_width + BRIEF_COL_SEP, ' ') { puts second.wrap(second_width) } if second
         
     | 
| 
       277 
262 
     | 
    
         
             
                    elsif second
         
     | 
| 
       278 
     | 
    
         
            -
                       
     | 
| 
       279 
     | 
    
         
            -
                       
     | 
| 
      
 263 
     | 
    
         
            +
                      indent_size = first_width + BRIEF_COL_SEP
         
     | 
| 
      
 264 
     | 
    
         
            +
                      printf "%-#{indent_size}s", first
         
     | 
| 
      
 265 
     | 
    
         
            +
                      indent(indent_size, ' ', bol: false) { puts second.wrap(second_width) }
         
     | 
| 
       280 
266 
     | 
    
         
             
                    else
         
     | 
| 
       281 
267 
     | 
    
         
             
                      puts first
         
     | 
| 
       282 
268 
     | 
    
         
             
                    end
         
     | 
| 
       283 
269 
     | 
    
         
             
                  end
         
     | 
| 
       284 
270 
     | 
    
         
             
                end
         
     | 
| 
       285 
271 
     | 
    
         | 
| 
      
 272 
     | 
    
         
            +
                # Returns a tuple of [first-column-width, second-column-width]. +width+ is
         
     | 
| 
      
 273 
     | 
    
         
            +
                # the maximum width of the colunms and the BRIEF_COL_SEP separator.
         
     | 
| 
      
 274 
     | 
    
         
            +
                # +fields+ is an array of [subject-string, descr-text] tuples where the
         
     | 
| 
      
 275 
     | 
    
         
            +
                # descr is an array of words
         
     | 
| 
       286 
276 
     | 
    
         
             
                def self.compute_columns(width, fields)
         
     | 
| 
       287 
     | 
    
         
            -
                  first_max =  
     | 
| 
       288 
     | 
    
         
            -
             
     | 
| 
       289 
     | 
    
         
            -
             
     | 
| 
       290 
     | 
    
         
            -
                   
     | 
| 
       291 
     | 
    
         
            -
                   
     | 
| 
       292 
     | 
    
         
            -
             
     | 
| 
       293 
     | 
    
         
            -
                   
     | 
| 
       294 
     | 
    
         
            -
             
     | 
| 
       295 
     | 
    
         
            -
                     
     | 
| 
       296 
     | 
    
         
            -
             
     | 
| 
       297 
     | 
    
         
            -
                    first_width = first_max
         
     | 
| 
       298 
     | 
    
         
            -
                    second_width = width - first_width - BRIEF_COL_SEP
         
     | 
| 
      
 277 
     | 
    
         
            +
                  first_max = 
         
     | 
| 
      
 278 
     | 
    
         
            +
                      fields.map { |first, _| first.size }.select { |size| size <= BRIEF_COL1_MAX_WIDTH }.max ||
         
     | 
| 
      
 279 
     | 
    
         
            +
                      BRIEF_COL1_MIN_WIDTH
         
     | 
| 
      
 280 
     | 
    
         
            +
                  second_max = fields.map { |_, second| second ? second&.map(&:size).sum + second.size - 1 : 0 }.max
         
     | 
| 
      
 281 
     | 
    
         
            +
                  first_width = [[first_max, BRIEF_COL1_MIN_WIDTH].max, BRIEF_COL1_MAX_WIDTH].min
         
     | 
| 
      
 282 
     | 
    
         
            +
                  rest = width - first_width - BRIEF_COL_SEP
         
     | 
| 
      
 283 
     | 
    
         
            +
                  second_min = [BRIEF_COL2_MIN_WIDTH, second_max].min
         
     | 
| 
      
 284 
     | 
    
         
            +
                  if rest < second_min
         
     | 
| 
      
 285 
     | 
    
         
            +
                    first_width = [first_max, width - second_min - BRIEF_COL_SEP].max
         
     | 
| 
      
 286 
     | 
    
         
            +
                    second_width = [width - first_width - BRIEF_COL_SEP, BRIEF_COL2_MIN_WIDTH].max
         
     | 
| 
       299 
287 
     | 
    
         
             
                  else
         
     | 
| 
       300 
     | 
    
         
            -
                     
     | 
| 
       301 
     | 
    
         
            -
                    second_width = BRIEF_COL2_MAX_WIDTH
         
     | 
| 
      
 288 
     | 
    
         
            +
                    second_width = [[rest, BRIEF_COL2_MIN_WIDTH].max, BRIEF_COL2_MAX_WIDTH].min
         
     | 
| 
       302 
289 
     | 
    
         
             
                  end
         
     | 
| 
       303 
     | 
    
         
            -
             
     | 
| 
       304 
290 
     | 
    
         
             
                  [first_width, second_width]
         
     | 
| 
       305 
291 
     | 
    
         
             
                end
         
     | 
| 
       306 
292 
     | 
    
         | 
| 
       307 
293 
     | 
    
         
             
                def self.width()
         
     | 
| 
       308 
294 
     | 
    
         
             
                  @width ||= TermInfo.screen_width - MARGIN_RIGHT
         
     | 
| 
      
 295 
     | 
    
         
            +
                  @width
         
     | 
| 
      
 296 
     | 
    
         
            +
                end
         
     | 
| 
      
 297 
     | 
    
         
            +
             
     | 
| 
      
 298 
     | 
    
         
            +
                # Used in rspec
         
     | 
| 
      
 299 
     | 
    
         
            +
                def self.width=(width)
         
     | 
| 
      
 300 
     | 
    
         
            +
                  @width = width
         
     | 
| 
       309 
301 
     | 
    
         
             
                end
         
     | 
| 
       310 
302 
     | 
    
         | 
| 
       311 
     | 
    
         
            -
                def self.rest() width - $stdout. 
     | 
| 
      
 303 
     | 
    
         
            +
                def self.rest() width - $stdout.tab end
         
     | 
| 
       312 
304 
     | 
    
         | 
| 
       313 
305 
     | 
    
         
             
              private
         
     | 
| 
       314 
306 
     | 
    
         
             
                # TODO Get rid of?
         
     | 
    
        data/lib/shellopts/grammar.rb
    CHANGED
    
    | 
         @@ -45,16 +45,18 @@ module ShellOpts 
     | 
|
| 
       45 
45 
     | 
    
         
             
                end
         
     | 
| 
       46 
46 
     | 
    
         | 
| 
       47 
47 
     | 
    
         
             
                class IdrNode < Node
         
     | 
| 
       48 
     | 
    
         
            -
                  # Command of this object 
     | 
| 
       49 
     | 
    
         
            -
                  #  
     | 
| 
       50 
     | 
    
         
            -
                  # Initialized by the analyzer
         
     | 
| 
      
 48 
     | 
    
         
            +
                  # Command of this object (nil for the top-level Program object). This is
         
     | 
| 
      
 49 
     | 
    
         
            +
                  # different from #parent when a subcommand is nested textually on a
         
     | 
| 
      
 50 
     | 
    
         
            +
                  # higher level than its supercommand.  Initialized by the analyzer
         
     | 
| 
       51 
51 
     | 
    
         
             
                  attr_reader :command
         
     | 
| 
       52 
52 
     | 
    
         | 
| 
       53 
53 
     | 
    
         
             
                  # Unique identifier of node (String) within the context of a program. nil
         
     | 
| 
       54 
     | 
    
         
            -
                  # for the Program object. It is the  
     | 
| 
       55 
     | 
    
         
            -
                  #  
     | 
| 
       56 
     | 
    
         
            -
                  #  
     | 
| 
       57 
     | 
    
         
            -
                   
     | 
| 
      
 54 
     | 
    
         
            +
                  # for the Program object. It is the dot-joined elements of path with
         
     | 
| 
      
 55 
     | 
    
         
            +
                  # internal exclamation marks removed (eg. "cmd.opt" or "cmd.cmd!").
         
     | 
| 
      
 56 
     | 
    
         
            +
                  # Initialize by the analyzer
         
     | 
| 
      
 57 
     | 
    
         
            +
                  def uid() 
         
     | 
| 
      
 58 
     | 
    
         
            +
                    @uid ||= command && [command.uid, ident].compact.join(".").sub(/!\./, ".") 
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
       58 
60 
     | 
    
         | 
| 
       59 
61 
     | 
    
         
             
                  # Path from Program object and down to this node. Array of identifiers.
         
     | 
| 
       60 
62 
     | 
    
         
             
                  # Empty for the Program object. Initialized by the parser
         
     | 
| 
         @@ -84,6 +86,13 @@ module ShellOpts 
     | 
|
| 
       84 
86 
     | 
    
         
             
                  # #ident is a reserved word. Initialized by the parser
         
     | 
| 
       85 
87 
     | 
    
         
             
                  attr_reader :attr
         
     | 
| 
       86 
88 
     | 
    
         | 
| 
      
 89 
     | 
    
         
            +
                  def set_name(name, path)
         
     | 
| 
      
 90 
     | 
    
         
            +
                    @name = name.to_s
         
     | 
| 
      
 91 
     | 
    
         
            +
                    @path = path
         
     | 
| 
      
 92 
     | 
    
         
            +
                    @ident = @path.last || :!
         
     | 
| 
      
 93 
     | 
    
         
            +
                    @attr = ::ShellOpts::Command::RESERVED_OPTION_NAMES.include?(@ident.to_s) ? nil : @ident
         
     | 
| 
      
 94 
     | 
    
         
            +
                  end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
       87 
96 
     | 
    
         
             
                protected
         
     | 
| 
       88 
97 
     | 
    
         
             
                  def lookup(path)
         
     | 
| 
       89 
98 
     | 
    
         
             
                    path.empty? or raise ArgumentError, "Argument should be empty"
         
     | 
| 
         @@ -189,10 +198,6 @@ module ShellOpts 
     | 
|
| 
       189 
198 
     | 
    
         
             
                # methods are initialized by the analyzer
         
     | 
| 
       190 
199 
     | 
    
         
             
                #
         
     | 
| 
       191 
200 
     | 
    
         
             
                class Command < IdrNode
         
     | 
| 
       192 
     | 
    
         
            -
                  # Supercommand or nil if this is the top-level Program object.
         
     | 
| 
       193 
     | 
    
         
            -
                  # Initialized by the analyzer
         
     | 
| 
       194 
     | 
    
         
            -
                  attr_reader :supercommand
         
     | 
| 
       195 
     | 
    
         
            -
             
     | 
| 
       196 
201 
     | 
    
         
             
                  # Brief description of command
         
     | 
| 
       197 
202 
     | 
    
         
             
                  attr_accessor :brief
         
     | 
| 
       198 
203 
     | 
    
         | 
    
        data/lib/shellopts/parser.rb
    CHANGED
    
    | 
         @@ -16,9 +16,9 @@ module ShellOpts 
     | 
|
| 
       16 
16 
     | 
    
         
             
                class IdrNode
         
     | 
| 
       17 
17 
     | 
    
         
             
                  # Assumes that @name and @path has been defined
         
     | 
| 
       18 
18 
     | 
    
         
             
                  def parse
         
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
      
 19 
     | 
    
         
            +
            #       @ident = @path.last || :!
         
     | 
| 
      
 20 
     | 
    
         
            +
            #       @attr = ::ShellOpts::Command::RESERVED_OPTION_NAMES.include?(ident.to_s) ? nil : ident
         
     | 
| 
      
 21 
     | 
    
         
            +
            #       @uid = parent && @path.join(".").sub(/!\./, ".") # uid is nil for the Program object
         
     | 
| 
       22 
22 
     | 
    
         
             
                  end
         
     | 
| 
       23 
23 
     | 
    
         
             
                end
         
     | 
| 
       24 
24 
     | 
    
         | 
| 
         @@ -56,8 +56,9 @@ module ShellOpts 
     | 
|
| 
       56 
56 
     | 
    
         
             
                    @long_names = names.map { |name| "--#{name}" }
         
     | 
| 
       57 
57 
     | 
    
         
             
                    @long_idents = names.map { |name| name.tr("-", "_").to_sym }
         
     | 
| 
       58 
58 
     | 
    
         | 
| 
       59 
     | 
    
         
            -
                     
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
      
 59 
     | 
    
         
            +
                    set_name(
         
     | 
| 
      
 60 
     | 
    
         
            +
                      @long_names.first || @short_names.first,
         
     | 
| 
      
 61 
     | 
    
         
            +
                      command.path + [@long_idents.first || @short_idents.first])
         
     | 
| 
       61 
62 
     | 
    
         | 
| 
       62 
63 
     | 
    
         
             
                    @argument = !arg.nil?
         
     | 
| 
       63 
64 
     | 
    
         | 
| 
         @@ -108,11 +109,11 @@ module ShellOpts 
     | 
|
| 
       108 
109 
     | 
    
         
             
                  def parse
         
     | 
| 
       109 
110 
     | 
    
         
             
                    if parent
         
     | 
| 
       110 
111 
     | 
    
         
             
                      path_names = token.source.sub("!", "").split(".")
         
     | 
| 
       111 
     | 
    
         
            -
                       
     | 
| 
       112 
     | 
    
         
            -
             
     | 
| 
      
 112 
     | 
    
         
            +
                      set_name(
         
     | 
| 
      
 113 
     | 
    
         
            +
                          path_names.last,
         
     | 
| 
      
 114 
     | 
    
         
            +
                          path_names.map { |cmd| "#{cmd}!".to_sym })
         
     | 
| 
       113 
115 
     | 
    
         
             
                    else
         
     | 
| 
       114 
     | 
    
         
            -
                       
     | 
| 
       115 
     | 
    
         
            -
                      @name = token.source
         
     | 
| 
      
 116 
     | 
    
         
            +
                      set_name(token.source, [])
         
     | 
| 
       116 
117 
     | 
    
         
             
                    end
         
     | 
| 
       117 
118 
     | 
    
         
             
                    super
         
     | 
| 
       118 
119 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -123,13 +124,15 @@ module ShellOpts 
     | 
|
| 
       123 
124 
     | 
    
         
             
                    super(nil, token)
         
     | 
| 
       124 
125 
     | 
    
         
             
                  end
         
     | 
| 
       125 
126 
     | 
    
         | 
| 
       126 
     | 
    
         
            -
                  def  
     | 
| 
      
 127 
     | 
    
         
            +
                  def add_version_option
         
     | 
| 
       127 
128 
     | 
    
         
             
                    option_token = Token.new(:option, 1, 1, "--version")
         
     | 
| 
       128 
129 
     | 
    
         
             
                    brief_token = Token.new(:brief, 1, 1, "Write version number and exit")
         
     | 
| 
       129 
130 
     | 
    
         
             
                    group = OptionGroup.new(self, option_token)
         
     | 
| 
       130 
131 
     | 
    
         
             
                    option = Option.parse(group, option_token)
         
     | 
| 
       131 
132 
     | 
    
         
             
                    brief = Brief.parse(group, brief_token)
         
     | 
| 
      
 133 
     | 
    
         
            +
                  end
         
     | 
| 
       132 
134 
     | 
    
         | 
| 
      
 135 
     | 
    
         
            +
                  def add_help_options
         
     | 
| 
       133 
136 
     | 
    
         
             
                    option_token = Token.new(:option, 1, 1, "-h,help")
         
     | 
| 
       134 
137 
     | 
    
         
             
                    brief_token = Token.new(:brief, 1, 1, "Write help text and exit")
         
     | 
| 
       135 
138 
     | 
    
         
             
                    paragraph_token = Token.new(:text, 1, 1, 
         
     | 
| 
         @@ -163,14 +166,6 @@ module ShellOpts 
     | 
|
| 
       163 
166 
     | 
    
         
             
                  @nodes = {}
         
     | 
| 
       164 
167 
     | 
    
         
             
                end
         
     | 
| 
       165 
168 
     | 
    
         | 
| 
       166 
     | 
    
         
            -
            #   def add_stdopts
         
     | 
| 
       167 
     | 
    
         
            -
            #     version_token = Token.new(:option, 1, 1, "--version")
         
     | 
| 
       168 
     | 
    
         
            -
            #     version_brief = Token.new(:brief, 1, 1, "Gryf gryf")
         
     | 
| 
       169 
     | 
    
         
            -
            #     group = Grammar::OptionGroup.new(@program, version_token)
         
     | 
| 
       170 
     | 
    
         
            -
            #     option = Grammar::Option.parse(group, version_token)
         
     | 
| 
       171 
     | 
    
         
            -
            #     brief = Grammr::Brief.parse(option, version_brief)
         
     | 
| 
       172 
     | 
    
         
            -
            #   end
         
     | 
| 
       173 
     | 
    
         
            -
             
     | 
| 
       174 
169 
     | 
    
         
             
                def parse()
         
     | 
| 
       175 
170 
     | 
    
         
             
                  @program = Grammar::Program.parse(@tokens.shift)
         
     | 
| 
       176 
171 
     | 
    
         
             
                  oneline = @tokens.first.lineno == @tokens.last.lineno
         
     | 
    
        data/lib/shellopts/program.rb
    CHANGED
    
    | 
         @@ -49,10 +49,10 @@ module ShellOpts 
     | 
|
| 
       49 
49 
     | 
    
         
             
                    singleton_method_removed singleton_method_undefined
         
     | 
| 
       50 
50 
     | 
    
         
             
                )
         
     | 
| 
       51 
51 
     | 
    
         | 
| 
       52 
     | 
    
         
            -
                # These methods can be overridden by an option (the value is not used -
         
     | 
| 
      
 52 
     | 
    
         
            +
                # These methods can be overridden by an option or a command (the value is not used -
         
     | 
| 
       53 
53 
     | 
    
         
             
                # this is just for informational purposes)
         
     | 
| 
       54 
     | 
    
         
            -
                 
     | 
| 
       55 
     | 
    
         
            -
                    subcommand
         
     | 
| 
      
 54 
     | 
    
         
            +
                OVERRIDEABLE_METHOD_NAMES = %w(
         
     | 
| 
      
 55 
     | 
    
         
            +
                    subcommand subcommand! supercommand!
         
     | 
| 
       56 
56 
     | 
    
         
             
                )
         
     | 
| 
       57 
57 
     | 
    
         | 
| 
       58 
58 
     | 
    
         
             
                # Redefine ::new to call #__initialize__
         
     | 
| 
         @@ -107,6 +107,7 @@ module ShellOpts 
     | 
|
| 
       107 
107 
     | 
    
         
             
                #
         
     | 
| 
       108 
108 
     | 
    
         
             
                # Note: Can be overridden by option, in that case use #__subcommand__ or
         
     | 
| 
       109 
109 
     | 
    
         
             
                # ShellOpts.subcommand(object) instead
         
     | 
| 
      
 110 
     | 
    
         
            +
                #
         
     | 
| 
       110 
111 
     | 
    
         
             
                def subcommand() __subcommand__ end
         
     | 
| 
       111 
112 
     | 
    
         | 
| 
       112 
113 
     | 
    
         
             
                # The subcommand object or nil if not present. Per-subcommand methods
         
     | 
| 
         @@ -120,7 +121,12 @@ module ShellOpts 
     | 
|
| 
       120 
121 
     | 
    
         
             
                def subcommand!() __subcommand__! end
         
     | 
| 
       121 
122 
     | 
    
         | 
| 
       122 
123 
     | 
    
         
             
                # The parent command or nil. Initialized by #add_command
         
     | 
| 
       123 
     | 
    
         
            -
                 
     | 
| 
      
 124 
     | 
    
         
            +
                #
         
     | 
| 
      
 125 
     | 
    
         
            +
                # Note: Can be overridden by a subcommand declaration (but not an
         
     | 
| 
      
 126 
     | 
    
         
            +
                # option), in that case use #__supercommand__! or
         
     | 
| 
      
 127 
     | 
    
         
            +
                # ShellOpts.supercommand!(object) instead
         
     | 
| 
      
 128 
     | 
    
         
            +
                #
         
     | 
| 
      
 129 
     | 
    
         
            +
                def supercommand!() __supercommand__ end
         
     | 
| 
       124 
130 
     | 
    
         | 
| 
       125 
131 
     | 
    
         
             
                # UID of command/program
         
     | 
| 
       126 
132 
     | 
    
         
             
                def __uid__() @__grammar__.uid end
         
     | 
| 
         @@ -149,6 +155,9 @@ module ShellOpts 
     | 
|
| 
       149 
155 
     | 
    
         
             
                # Map from identifier to option object or to a list of option objects if
         
     | 
| 
       150 
156 
     | 
    
         
             
                # the option is repeatable
         
     | 
| 
       151 
157 
     | 
    
         
             
                attr_reader :__option_hash__
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
                # The parent command or nil. Initialized by #add_command
         
     | 
| 
      
 160 
     | 
    
         
            +
                attr_accessor :__supercommand__
         
     | 
| 
       152 
161 
     | 
    
         | 
| 
       153 
162 
     | 
    
         
             
                # The subcommand identifier (a Symbol incl. the exclamation mark) or nil
         
     | 
| 
       154 
163 
     | 
    
         
             
                # if not present. Use #subcommand!, or the dynamically generated
         
     | 
    
        data/lib/shellopts/renderer.rb
    CHANGED
    
    | 
         @@ -38,13 +38,19 @@ module ShellOpts 
     | 
|
| 
       38 
38 
     | 
    
         
             
                  #
         
     | 
| 
       39 
39 
     | 
    
         
             
                  def render(format)
         
     | 
| 
       40 
40 
     | 
    
         
             
                    constrain format, :enum, :long, :short
         
     | 
| 
       41 
     | 
    
         
            -
                     
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
      
 41 
     | 
    
         
            +
                    s = 
         
     | 
| 
      
 42 
     | 
    
         
            +
                        case format
         
     | 
| 
      
 43 
     | 
    
         
            +
                          when :enum; names.join(", ")
         
     | 
| 
      
 44 
     | 
    
         
            +
                          when :long; name
         
     | 
| 
      
 45 
     | 
    
         
            +
                          when :short; short_names.first || name
         
     | 
| 
      
 46 
     | 
    
         
            +
                        else
         
     | 
| 
      
 47 
     | 
    
         
            +
                          raise ArgumentError, "Illegal format: #{format.inspect}"
         
     | 
| 
      
 48 
     | 
    
         
            +
                        end 
         
     | 
| 
      
 49 
     | 
    
         
            +
                    if argument?
         
     | 
| 
      
 50 
     | 
    
         
            +
                      s + (optional? ? "[=#{argument_name}]" : "=#{argument_name}")
         
     | 
| 
       45 
51 
     | 
    
         
             
                    else
         
     | 
| 
       46 
     | 
    
         
            -
                       
     | 
| 
       47 
     | 
    
         
            -
                    end 
     | 
| 
      
 52 
     | 
    
         
            +
                      s
         
     | 
| 
      
 53 
     | 
    
         
            +
                    end
         
     | 
| 
       48 
54 
     | 
    
         
             
                  end
         
     | 
| 
       49 
55 
     | 
    
         
             
                end
         
     | 
| 
       50 
56 
     | 
    
         | 
| 
         @@ -75,17 +81,19 @@ module ShellOpts 
     | 
|
| 
       75 
81 
     | 
    
         
             
                  COMMANDS_ABBR = "[COMMANDS]"
         
     | 
| 
       76 
82 
     | 
    
         
             
                  DESCRS_ABBR = "ARGS..."
         
     | 
| 
       77 
83 
     | 
    
         | 
| 
       78 
     | 
    
         
            -
                  # Format can be one of :single, :enum, or :multi. : 
     | 
| 
       79 
     | 
    
         
            -
                  #  
     | 
| 
       80 
     | 
    
         
            -
                  #  
     | 
| 
       81 
     | 
    
         
            -
                  #  
     | 
| 
       82 
     | 
    
         
            -
                  #  
     | 
| 
      
 84 
     | 
    
         
            +
                  # Format can be one of :abbr, :single, :enum, or :multi. :abbr
         
     | 
| 
      
 85 
     | 
    
         
            +
                  # lists the command on one line with options abbreviated. :single force
         
     | 
| 
      
 86 
     | 
    
         
            +
                  # one-line output and compacts options and commands if needed. :enum
         
     | 
| 
      
 87 
     | 
    
         
            +
                  # outputs a :single line for each argument specification/description,
         
     | 
| 
      
 88 
     | 
    
         
            +
                  # :multi tries one-line output but wrap options if needed. Multiple
         
     | 
| 
      
 89 
     | 
    
         
            +
                  # argument specifications/descriptions are always compacted
         
     | 
| 
       83 
90 
     | 
    
         
             
                  #
         
     | 
| 
       84 
91 
     | 
    
         
             
                  def render(format, width, root: false, **opts)
         
     | 
| 
       85 
92 
     | 
    
         
             
                    case format
         
     | 
| 
      
 93 
     | 
    
         
            +
                      when :abbr; render_abbr
         
     | 
| 
       86 
94 
     | 
    
         
             
                      when :single; render_single(width, **opts)
         
     | 
| 
       87 
95 
     | 
    
         
             
                      when :enum; render_enum(width, **opts)
         
     | 
| 
       88 
     | 
    
         
            -
                      when :multi;  
     | 
| 
      
 96 
     | 
    
         
            +
                      when :multi; render_multi(width, **opts)
         
     | 
| 
       89 
97 
     | 
    
         
             
                    else
         
     | 
| 
       90 
98 
     | 
    
         
             
                      raise ArgumentError, "Illegal format: #{format.inspect}"
         
     | 
| 
       91 
99 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -96,6 +104,21 @@ module ShellOpts 
     | 
|
| 
       96 
104 
     | 
    
         
             
                  end
         
     | 
| 
       97 
105 
     | 
    
         | 
| 
       98 
106 
     | 
    
         
             
                protected
         
     | 
| 
      
 107 
     | 
    
         
            +
                  # TODO: Refactor and implement recursive detection of any argument
         
     | 
| 
      
 108 
     | 
    
         
            +
                  def get_args(args: nil)
         
     | 
| 
      
 109 
     | 
    
         
            +
                    case descrs.size
         
     | 
| 
      
 110 
     | 
    
         
            +
                      when 0; []
         
     | 
| 
      
 111 
     | 
    
         
            +
                      when 1; [descrs.first.text]
         
     | 
| 
      
 112 
     | 
    
         
            +
                      else [DESCRS_ABBR]
         
     | 
| 
      
 113 
     | 
    
         
            +
                    end
         
     | 
| 
      
 114 
     | 
    
         
            +
                  end
         
     | 
| 
      
 115 
     | 
    
         
            +
             
     | 
| 
      
 116 
     | 
    
         
            +
                  # Force one line and compact options to "[OPTIONS]"
         
     | 
| 
      
 117 
     | 
    
         
            +
                  def render_abbr
         
     | 
| 
      
 118 
     | 
    
         
            +
                    args = get_args 
         
     | 
| 
      
 119 
     | 
    
         
            +
                    ([name] + [options.empty? ? nil : "[OPTIONS]"] + args).compact.join(" ")
         
     | 
| 
      
 120 
     | 
    
         
            +
                  end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
       99 
122 
     | 
    
         
             
                  # Force one line. Compact options, commands, arguments if needed
         
     | 
| 
       100 
123 
     | 
    
         
             
                  def render_single(width, args: nil)
         
     | 
| 
       101 
124 
     | 
    
         
             
                    long_options = options.map { |option| option.render(:long) }
         
     | 
| 
         @@ -104,13 +127,7 @@ module ShellOpts 
     | 
|
| 
       104 
127 
     | 
    
         
             
                    short_commands = commands.empty? ? [] : ["[#{commands.map(&:name).join("|")}]"]
         
     | 
| 
       105 
128 
     | 
    
         
             
                    compact_commands = commands.empty? ? [] : [COMMANDS_ABBR]
         
     | 
| 
       106 
129 
     | 
    
         | 
| 
       107 
     | 
    
         
            -
                     
     | 
| 
       108 
     | 
    
         
            -
                    args ||= 
         
     | 
| 
       109 
     | 
    
         
            -
                        case descrs.size
         
     | 
| 
       110 
     | 
    
         
            -
                          when 0; args = []
         
     | 
| 
       111 
     | 
    
         
            -
                          when 1; [descrs.first.text]
         
     | 
| 
       112 
     | 
    
         
            -
                          else [DESCRS_ABBR]
         
     | 
| 
       113 
     | 
    
         
            -
                        end
         
     | 
| 
      
 130 
     | 
    
         
            +
                    args ||= get_args
         
     | 
| 
       114 
131 
     | 
    
         | 
| 
       115 
132 
     | 
    
         
             
                    begin # to be able to use 'break' below
         
     | 
| 
       116 
133 
     | 
    
         
             
                      words = [name] + long_options + short_commands + args
         
     | 
| 
         @@ -149,36 +166,8 @@ module ShellOpts 
     | 
|
| 
       149 
166 
     | 
    
         
             
                    short_options = options.map { |option| option.render(:short) }
         
     | 
| 
       150 
167 
     | 
    
         
             
                    short_commands = commands.empty? ? [] : ["[#{commands.map(&:name).join("|")}]"]
         
     | 
| 
       151 
168 
     | 
    
         
             
                    compact_commands = [COMMANDS_ABBR]
         
     | 
| 
       152 
     | 
    
         
            -
                    args ||= self.descrs.size != 1 ? [DESCRS_ABBR] : descrs.map(&:text)
         
     | 
| 
       153 
     | 
    
         
            -
             
     | 
| 
       154 
     | 
    
         
            -
                    # On one line
         
     | 
| 
       155 
     | 
    
         
            -
                    words = long_options + short_commands + args
         
     | 
| 
       156 
     | 
    
         
            -
                    return [words.join(" ")] if pass?(words, width)
         
     | 
| 
       157 
     | 
    
         
            -
                    words = short_options + short_commands + args
         
     | 
| 
       158 
     | 
    
         
            -
                    return [words.join(" ")] if pass?(words, width)
         
     | 
| 
       159 
     | 
    
         
            -
             
     | 
| 
       160 
     | 
    
         
            -
                    # On multiple lines
         
     | 
| 
       161 
     | 
    
         
            -
                    options = long_options.wrap(width)
         
     | 
| 
       162 
     | 
    
         
            -
                    commands = [[short_commands, args].join(" ")]
         
     | 
| 
       163 
     | 
    
         
            -
                    return options + commands if pass?(commands, width)
         
     | 
| 
       164 
     | 
    
         
            -
                    options + [[compact_commands, args].join(" ")]
         
     | 
| 
       165 
     | 
    
         
            -
                  end
         
     | 
| 
       166 
     | 
    
         
            -
             
     | 
| 
       167 
     | 
    
         
            -
                  # Try to keep on one line but wrap options if needed. Multiple argument
         
     | 
| 
       168 
     | 
    
         
            -
                  # specifications/descriptions are always compacted
         
     | 
| 
       169 
     | 
    
         
            -
                  def render_multi2(width, args: nil)
         
     | 
| 
       170 
     | 
    
         
            -
                    long_options = options.map { |option| option.render(:long) }
         
     | 
| 
       171 
     | 
    
         
            -
                    short_options = options.map { |option| option.render(:short) }
         
     | 
| 
       172 
     | 
    
         
            -
                    short_commands = commands.empty? ? [] : ["[#{commands.map(&:name).join("|")}]"]
         
     | 
| 
       173 
     | 
    
         
            -
                    compact_commands = [COMMANDS_ABBR]
         
     | 
| 
       174 
169 
     | 
    
         | 
| 
       175 
     | 
    
         
            -
                     
     | 
| 
       176 
     | 
    
         
            -
                    args ||= 
         
     | 
| 
       177 
     | 
    
         
            -
                        case descrs.size
         
     | 
| 
       178 
     | 
    
         
            -
                          when 0; args = []
         
     | 
| 
       179 
     | 
    
         
            -
                          when 1; [descrs.first.text]
         
     | 
| 
       180 
     | 
    
         
            -
                          else [DESCRS_ABBR]
         
     | 
| 
       181 
     | 
    
         
            -
                        end
         
     | 
| 
      
 170 
     | 
    
         
            +
                    args ||= get_args
         
     | 
| 
       182 
171 
     | 
    
         | 
| 
       183 
172 
     | 
    
         
             
                    # On one line
         
     | 
| 
       184 
173 
     | 
    
         
             
                    words = [name] + long_options + short_commands + args
         
     | 
    
        data/lib/shellopts/version.rb
    CHANGED
    
    
    
        data/lib/shellopts.rb
    CHANGED
    
    | 
         @@ -1,12 +1,6 @@ 
     | 
|
| 
       1 
1 
     | 
    
         | 
| 
       2 
     | 
    
         
            -
            $quiet = nil
         
     | 
| 
       3 
     | 
    
         
            -
            $verb = nil
         
     | 
| 
       4 
     | 
    
         
            -
            $debug = nil
         
     | 
| 
       5 
     | 
    
         
            -
            $shellopts = nil
         
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
2 
     | 
    
         
             
            require 'indented_io'
         
     | 
| 
       8 
3 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
            #$LOAD_PATH.unshift "../constrain/lib"
         
     | 
| 
       10 
4 
     | 
    
         
             
            require 'constrain'
         
     | 
| 
       11 
5 
     | 
    
         
             
            include Constrain
         
     | 
| 
       12 
6 
     | 
    
         | 
| 
         @@ -98,14 +92,22 @@ module ShellOpts 
     | 
|
| 
       98 
92 
     | 
    
         
             
                # Array of remaining arguments. Initialized by #interpret
         
     | 
| 
       99 
93 
     | 
    
         
             
                attr_reader :args
         
     | 
| 
       100 
94 
     | 
    
         | 
| 
       101 
     | 
    
         
            -
                #  
     | 
| 
       102 
     | 
    
         
            -
                 
     | 
| 
       103 
     | 
    
         
            -
                attr_accessor :msgopts
         
     | 
| 
      
 95 
     | 
    
         
            +
                # Automatically add a -h and a --help option if true
         
     | 
| 
      
 96 
     | 
    
         
            +
                attr_reader :help
         
     | 
| 
       104 
97 
     | 
    
         | 
| 
       105 
     | 
    
         
            -
                # Version of client program.  
     | 
| 
       106 
     | 
    
         
            -
                 
     | 
| 
      
 98 
     | 
    
         
            +
                # Version of client program. If not nil a --version option is added to the program
         
     | 
| 
      
 99 
     | 
    
         
            +
                def version
         
     | 
| 
      
 100 
     | 
    
         
            +
                  return @version if @version
         
     | 
| 
      
 101 
     | 
    
         
            +
                  exe = caller.find { |line| line =~ /`<top \(required\)>'$/ }&.sub(/:.*/, "")
         
     | 
| 
      
 102 
     | 
    
         
            +
                  file = Dir.glob(File.dirname(exe) + "/../lib/*/version.rb").first
         
     | 
| 
      
 103 
     | 
    
         
            +
                  @version = IO.read(file).sub(/^.*VERSION\s*=\s*"(.*?)".*$/m, '\1') or
         
     | 
| 
      
 104 
     | 
    
         
            +
                      raise ArgumentError, "ShellOpts needs an explicit version"
         
     | 
| 
      
 105 
     | 
    
         
            +
                end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                # Add message options (TODO)
         
     | 
| 
      
 108 
     | 
    
         
            +
                attr_accessor :msgopts
         
     | 
| 
       107 
109 
     | 
    
         | 
| 
       108 
     | 
    
         
            -
                #  
     | 
| 
      
 110 
     | 
    
         
            +
                # Floating options
         
     | 
| 
       109 
111 
     | 
    
         
             
                attr_accessor :float
         
     | 
| 
       110 
112 
     | 
    
         | 
| 
       111 
113 
     | 
    
         
             
                # True if ShellOpts lets exceptions through instead of writing an error
         
     | 
| 
         @@ -117,11 +119,14 @@ module ShellOpts 
     | 
|
| 
       117 
119 
     | 
    
         | 
| 
       118 
120 
     | 
    
         
             
                # Debug: Internal variables made public
         
     | 
| 
       119 
121 
     | 
    
         
             
                attr_reader :tokens
         
     | 
| 
       120 
     | 
    
         
            -
                alias_method :ast, :grammar 
     | 
| 
      
 122 
     | 
    
         
            +
                alias_method :ast, :grammar
         
     | 
| 
       121 
123 
     | 
    
         | 
| 
       122 
     | 
    
         
            -
                def initialize(name: nil,  
     | 
| 
      
 124 
     | 
    
         
            +
                def initialize(name: nil, help: true, version: true, msgopts: false, float: true, exception: false)
         
     | 
| 
       123 
125 
     | 
    
         
             
                  @name = name || File.basename($PROGRAM_NAME)
         
     | 
| 
       124 
     | 
    
         
            -
                  @ 
     | 
| 
      
 126 
     | 
    
         
            +
                  @help = help
         
     | 
| 
      
 127 
     | 
    
         
            +
                  @use_version = version ? true : false
         
     | 
| 
      
 128 
     | 
    
         
            +
                  @version = @use_version && @version != true ? @version : nil
         
     | 
| 
      
 129 
     | 
    
         
            +
                  @msgopts, @float, @exception = msgopts, float, exception
         
     | 
| 
       125 
130 
     | 
    
         
             
                end
         
     | 
| 
       126 
131 
     | 
    
         | 
| 
       127 
132 
     | 
    
         
             
                # Compile source and return grammar object. Also sets #spec and #grammar.
         
     | 
| 
         @@ -133,7 +138,8 @@ module ShellOpts 
     | 
|
| 
       133 
138 
     | 
    
         
             
                    @file = find_caller_file
         
     | 
| 
       134 
139 
     | 
    
         
             
                    @tokens = Lexer.lex(name, @spec, @oneline)
         
     | 
| 
       135 
140 
     | 
    
         
             
                    ast = Parser.parse(tokens)
         
     | 
| 
       136 
     | 
    
         
            -
                    ast. 
     | 
| 
      
 141 
     | 
    
         
            +
                    ast.add_version_option if @use_version
         
     | 
| 
      
 142 
     | 
    
         
            +
                    ast.add_help_options if @help
         
     | 
| 
       137 
143 
     | 
    
         
             
                    @grammar = Analyzer.analyze(ast)
         
     | 
| 
       138 
144 
     | 
    
         
             
                  }
         
     | 
| 
       139 
145 
     | 
    
         
             
                  self
         
     | 
| 
         @@ -146,19 +152,16 @@ module ShellOpts 
     | 
|
| 
       146 
152 
     | 
    
         
             
                  handle_exceptions { 
         
     | 
| 
       147 
153 
     | 
    
         
             
                    @argv = argv.dup
         
     | 
| 
       148 
154 
     | 
    
         
             
                    @program, @args = Interpreter.interpret(grammar, argv, float: float, exception: exception)
         
     | 
| 
       149 
     | 
    
         
            -
                    if  
     | 
| 
       150 
     | 
    
         
            -
                       
     | 
| 
       151 
     | 
    
         
            -
             
     | 
| 
       152 
     | 
    
         
            -
             
     | 
| 
       153 
     | 
    
         
            -
             
     | 
| 
       154 
     | 
    
         
            -
             
     | 
| 
       155 
     | 
    
         
            -
             
     | 
| 
       156 
     | 
    
         
            -
             
     | 
| 
       157 
     | 
    
         
            -
                        else
         
     | 
| 
       158 
     | 
    
         
            -
                          ShellOpts.help
         
     | 
| 
       159 
     | 
    
         
            -
                        end
         
     | 
| 
       160 
     | 
    
         
            -
                        exit
         
     | 
| 
      
 155 
     | 
    
         
            +
                    if @program.version?
         
     | 
| 
      
 156 
     | 
    
         
            +
                      puts version 
         
     | 
| 
      
 157 
     | 
    
         
            +
                      exit
         
     | 
| 
      
 158 
     | 
    
         
            +
                    elsif @program.help?
         
     | 
| 
      
 159 
     | 
    
         
            +
                      if @program[:help].name == "-h"
         
     | 
| 
      
 160 
     | 
    
         
            +
                        ShellOpts.brief
         
     | 
| 
      
 161 
     | 
    
         
            +
                      else
         
     | 
| 
      
 162 
     | 
    
         
            +
                        ShellOpts.help
         
     | 
| 
       161 
163 
     | 
    
         
             
                      end
         
     | 
| 
      
 164 
     | 
    
         
            +
                      exit
         
     | 
| 
       162 
165 
     | 
    
         
             
                    end
         
     | 
| 
       163 
166 
     | 
    
         
             
                  }
         
     | 
| 
       164 
167 
     | 
    
         
             
                  self
         
     | 
| 
         @@ -364,132 +367,3 @@ module ShellOpts 
     | 
|
| 
       364 
367 
     | 
    
         
             
              end
         
     | 
| 
       365 
368 
     | 
    
         
             
            end
         
     | 
| 
       366 
369 
     | 
    
         | 
| 
       367 
     | 
    
         
            -
             
     | 
| 
       368 
     | 
    
         
            -
             
     | 
| 
       369 
     | 
    
         
            -
             
     | 
| 
       370 
     | 
    
         
            -
             
     | 
| 
       371 
     | 
    
         
            -
             
     | 
| 
       372 
     | 
    
         
            -
             
     | 
| 
       373 
     | 
    
         
            -
             
     | 
| 
       374 
     | 
    
         
            -
            __END__
         
     | 
| 
       375 
     | 
    
         
            -
             
     | 
| 
       376 
     | 
    
         
            -
            require "shellopts/version"
         
     | 
| 
       377 
     | 
    
         
            -
             
     | 
| 
       378 
     | 
    
         
            -
            require "ext/algorithm.rb"
         
     | 
| 
       379 
     | 
    
         
            -
            require "ext/ruby_env.rb"
         
     | 
| 
       380 
     | 
    
         
            -
             
     | 
| 
       381 
     | 
    
         
            -
            require "shellopts/constants.rb"
         
     | 
| 
       382 
     | 
    
         
            -
            require "shellopts/exceptions.rb"
         
     | 
| 
       383 
     | 
    
         
            -
             
     | 
| 
       384 
     | 
    
         
            -
            require "shellopts/grammar/analyzer.rb"
         
     | 
| 
       385 
     | 
    
         
            -
            require "shellopts/grammar/lexer.rb"
         
     | 
| 
       386 
     | 
    
         
            -
            require "shellopts/grammar/parser.rb"
         
     | 
| 
       387 
     | 
    
         
            -
            require "shellopts/grammar/command.rb"
         
     | 
| 
       388 
     | 
    
         
            -
            require "shellopts/grammar/option.rb"
         
     | 
| 
       389 
     | 
    
         
            -
             
     | 
| 
       390 
     | 
    
         
            -
            require "shellopts/ast/parser.rb"
         
     | 
| 
       391 
     | 
    
         
            -
            require "shellopts/ast/command.rb"
         
     | 
| 
       392 
     | 
    
         
            -
            require "shellopts/ast/option.rb"
         
     | 
| 
       393 
     | 
    
         
            -
             
     | 
| 
       394 
     | 
    
         
            -
            require "shellopts/args.rb"
         
     | 
| 
       395 
     | 
    
         
            -
            require "shellopts/formatter.rb"
         
     | 
| 
       396 
     | 
    
         
            -
             
     | 
| 
       397 
     | 
    
         
            -
            if RUBY_ENV == "development"
         
     | 
| 
       398 
     | 
    
         
            -
              require "shellopts/grammar/dump.rb"
         
     | 
| 
       399 
     | 
    
         
            -
              require "shellopts/ast/dump.rb"
         
     | 
| 
       400 
     | 
    
         
            -
            end
         
     | 
| 
       401 
     | 
    
         
            -
             
     | 
| 
       402 
     | 
    
         
            -
            $verb = nil
         
     | 
| 
       403 
     | 
    
         
            -
            $quiet = nil
         
     | 
| 
       404 
     | 
    
         
            -
            $shellopts = nil
         
     | 
| 
       405 
     | 
    
         
            -
             
     | 
| 
       406 
     | 
    
         
            -
            module ShellOpts
         
     | 
| 
       407 
     | 
    
         
            -
              class ShellOpts
         
     | 
| 
       408 
     | 
    
         
            -
                attr_reader :name # Name of program. Defaults to the name of the executable
         
     | 
| 
       409 
     | 
    
         
            -
                attr_reader :spec
         
     | 
| 
       410 
     | 
    
         
            -
                attr_reader :argv
         
     | 
| 
       411 
     | 
    
         
            -
             
     | 
| 
       412 
     | 
    
         
            -
                attr_reader :grammar
         
     | 
| 
       413 
     | 
    
         
            -
                attr_reader :program
         
     | 
| 
       414 
     | 
    
         
            -
                attr_reader :arguments
         
     | 
| 
       415 
     | 
    
         
            -
             
     | 
| 
       416 
     | 
    
         
            -
                def initialize(spec, argv, name: nil, exception: false)
         
     | 
| 
       417 
     | 
    
         
            -
                  @name = name || File.basename($PROGRAM_NAME)
         
     | 
| 
       418 
     | 
    
         
            -
                  @spec, @argv = spec, argv.dup
         
     | 
| 
       419 
     | 
    
         
            -
                  exprs = Grammar::Lexer.lex(@spec)
         
     | 
| 
       420 
     | 
    
         
            -
                  commands = Grammar::Parser.parse(@name, exprs)
         
     | 
| 
       421 
     | 
    
         
            -
                  @grammar = Grammar::Analyzer.analyze(commands)
         
     | 
| 
       422 
     | 
    
         
            -
             
     | 
| 
       423 
     | 
    
         
            -
                  begin
         
     | 
| 
       424 
     | 
    
         
            -
                    @program, @arguments = Ast::Parser.parse(@grammar, @argv)
         
     | 
| 
       425 
     | 
    
         
            -
                  rescue Error => ex
         
     | 
| 
       426 
     | 
    
         
            -
                    raise if exception
         
     | 
| 
       427 
     | 
    
         
            -
                    error(ex.subject, ex.message)
         
     | 
| 
       428 
     | 
    
         
            -
                  end
         
     | 
| 
       429 
     | 
    
         
            -
                end
         
     | 
| 
       430 
     | 
    
         
            -
             
     | 
| 
       431 
     | 
    
         
            -
                def error(subject = nil, message)
         
     | 
| 
       432 
     | 
    
         
            -
                  $stderr.puts "#{name}: #{message}"
         
     | 
| 
       433 
     | 
    
         
            -
                  usage(subject, device: $stderr)
         
     | 
| 
       434 
     | 
    
         
            -
                  exit 1
         
     | 
| 
       435 
     | 
    
         
            -
                end
         
     | 
| 
       436 
     | 
    
         
            -
             
     | 
| 
       437 
     | 
    
         
            -
                def fail(message)
         
     | 
| 
       438 
     | 
    
         
            -
                  $stderr.puts "#{name}: #{message}"
         
     | 
| 
       439 
     | 
    
         
            -
                  exit 1
         
     | 
| 
       440 
     | 
    
         
            -
                end
         
     | 
| 
       441 
     | 
    
         
            -
             
     | 
| 
       442 
     | 
    
         
            -
                def usage(subject = nil, device: $stdout, levels: 1, margin: "")
         
     | 
| 
       443 
     | 
    
         
            -
                  subject = find_subject(subject)
         
     | 
| 
       444 
     | 
    
         
            -
                  device.puts Formatter.usage_string(subject, levels: levels, margin: margin)
         
     | 
| 
       445 
     | 
    
         
            -
                end
         
     | 
| 
       446 
     | 
    
         
            -
             
     | 
| 
       447 
     | 
    
         
            -
                def help(subject = nil, device: $stdout, levels: 10, margin: "", tab: "  ")
         
     | 
| 
       448 
     | 
    
         
            -
                  subject = find_subject(subject)
         
     | 
| 
       449 
     | 
    
         
            -
                  device.puts Formatter.help_string(subject, levels: levels, margin: margin, tab: tab)
         
     | 
| 
       450 
     | 
    
         
            -
                end
         
     | 
| 
       451 
     | 
    
         
            -
             
     | 
| 
       452 
     | 
    
         
            -
              private
         
     | 
| 
       453 
     | 
    
         
            -
                def lookup(name)
         
     | 
| 
       454 
     | 
    
         
            -
                  a = name.split(".")
         
     | 
| 
       455 
     | 
    
         
            -
                  cmd = grammar
         
     | 
| 
       456 
     | 
    
         
            -
                  while element = a.shift
         
     | 
| 
       457 
     | 
    
         
            -
                    cmd = cmd.commands[element]
         
     | 
| 
       458 
     | 
    
         
            -
                  end
         
     | 
| 
       459 
     | 
    
         
            -
                  cmd
         
     | 
| 
       460 
     | 
    
         
            -
                end
         
     | 
| 
       461 
     | 
    
         
            -
             
     | 
| 
       462 
     | 
    
         
            -
                def find_subject(obj)
         
     | 
| 
       463 
     | 
    
         
            -
                  case obj
         
     | 
| 
       464 
     | 
    
         
            -
                    when String; lookup(obj)
         
     | 
| 
       465 
     | 
    
         
            -
                    when Ast::Command; Command.grammar(obj)
         
     | 
| 
       466 
     | 
    
         
            -
                    when Grammar::Command; obj
         
     | 
| 
       467 
     | 
    
         
            -
                    when NilClass; grammar
         
     | 
| 
       468 
     | 
    
         
            -
                  else
         
     | 
| 
       469 
     | 
    
         
            -
                    raise Internal, "Illegal object: #{obj.class}"
         
     | 
| 
       470 
     | 
    
         
            -
                  end
         
     | 
| 
       471 
     | 
    
         
            -
                end
         
     | 
| 
       472 
     | 
    
         
            -
              end
         
     | 
| 
       473 
     | 
    
         
            -
             
     | 
| 
       474 
     | 
    
         
            -
              def self.process(spec, argv, name: nil, exception: false)
         
     | 
| 
       475 
     | 
    
         
            -
                $shellopts = ShellOpts.new(spec, argv, name: name, exception: exception)
         
     | 
| 
       476 
     | 
    
         
            -
                [$shellopts.program, $shellopts.arguments]
         
     | 
| 
       477 
     | 
    
         
            -
              end
         
     | 
| 
       478 
     | 
    
         
            -
             
     | 
| 
       479 
     | 
    
         
            -
              def self.error(subject = nil, message)
         
     | 
| 
       480 
     | 
    
         
            -
                $shellopts.error(subject, message)
         
     | 
| 
       481 
     | 
    
         
            -
              end
         
     | 
| 
       482 
     | 
    
         
            -
             
     | 
| 
       483 
     | 
    
         
            -
              def self.fail(message)
         
     | 
| 
       484 
     | 
    
         
            -
                $shellopts.fail(message)
         
     | 
| 
       485 
     | 
    
         
            -
              end
         
     | 
| 
       486 
     | 
    
         
            -
             
     | 
| 
       487 
     | 
    
         
            -
              def self.help(subject = nil, device: $stdout, levels: 10, margin: "", tab: "  ")
         
     | 
| 
       488 
     | 
    
         
            -
                $shellopts.help(subject, device: device, levels: levels, margin: margin, tab: tab)
         
     | 
| 
       489 
     | 
    
         
            -
              end
         
     | 
| 
       490 
     | 
    
         
            -
             
     | 
| 
       491 
     | 
    
         
            -
              def self.usage(subject = nil, device: $stdout, levels: 1, margin: "")
         
     | 
| 
       492 
     | 
    
         
            -
                $shellopts.usage(subject, device: device, levels: levels, margin: margin)
         
     | 
| 
       493 
     | 
    
         
            -
              end
         
     | 
| 
       494 
     | 
    
         
            -
            end
         
     | 
| 
       495 
     | 
    
         
            -
             
     | 
    
        data/main
    CHANGED
    
    | 
         @@ -7,12 +7,15 @@ require 'shellopts' 
     | 
|
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
            include ShellOpts
         
     | 
| 
       9 
9 
     | 
    
         | 
| 
      
 10 
     | 
    
         
            +
            p ShellOpts::ShellOpts.default_version
         
     | 
| 
      
 11 
     | 
    
         
            +
            exit
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
       10 
13 
     | 
    
         
             
            VERSION = "1.2.3"
         
     | 
| 
       11 
14 
     | 
    
         | 
| 
       12 
15 
     | 
    
         
             
            SPEC = %(
         
     | 
| 
       13 
16 
     | 
    
         
             
              -a @ An option
         
     | 
| 
       14 
17 
     | 
    
         
             
            )
         
     | 
| 
       15 
     | 
    
         
            -
            opts, args = ShellOpts 
     | 
| 
      
 18 
     | 
    
         
            +
            opts, args = ShellOpts.process(SPEC, ARGV, version: VERSION)
         
     | 
| 
       16 
19 
     | 
    
         
             
            #ShellOpts::ShellOpts.help
         
     | 
| 
       17 
20 
     | 
    
         | 
| 
       18 
21 
     | 
    
         | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: shellopts
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 2.0. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 2.0.11
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Claus Rasmussen
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: exe
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2022-03- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2022-03-14 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: forward_to
         
     |