llm-shell 0.9.2 → 0.10.0

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.
Files changed (258) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +61 -66
  3. data/lib/llm/shell/command.rb +40 -40
  4. data/lib/llm/shell/commands/clear_screen.rb +4 -18
  5. data/lib/llm/shell/commands/debug_mode.rb +12 -0
  6. data/lib/llm/shell/commands/dir_import.rb +4 -20
  7. data/lib/llm/shell/commands/disable_tool.rb +33 -0
  8. data/lib/llm/shell/commands/enable_tool.rb +33 -0
  9. data/lib/llm/shell/commands/file_import.rb +4 -20
  10. data/lib/llm/shell/commands/help.rb +23 -36
  11. data/lib/llm/shell/commands/show_chat.rb +4 -19
  12. data/lib/llm/shell/commands/show_version.rb +4 -20
  13. data/lib/llm/shell/commands/system_prompt.rb +4 -18
  14. data/lib/llm/shell/completion.rb +5 -5
  15. data/lib/llm/shell/config.rb +4 -5
  16. data/lib/llm/shell/formatter.rb +1 -2
  17. data/lib/llm/shell/internal/coderay/lib/coderay/duo.rb +81 -0
  18. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/_map.rb +17 -0
  19. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/comment_filter.rb +25 -0
  20. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/count.rb +39 -0
  21. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/debug.rb +49 -0
  22. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/debug_lint.rb +63 -0
  23. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/div.rb +23 -0
  24. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/encoder.rb +190 -0
  25. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/filter.rb +58 -0
  26. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/html/css.rb +65 -0
  27. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/html/numbering.rb +108 -0
  28. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/html/output.rb +164 -0
  29. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/html.rb +333 -0
  30. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/json.rb +83 -0
  31. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/lines_of_code.rb +45 -0
  32. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/lint.rb +59 -0
  33. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/null.rb +18 -0
  34. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/page.rb +24 -0
  35. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/span.rb +23 -0
  36. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/statistic.rb +95 -0
  37. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/terminal.rb +195 -0
  38. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/text.rb +46 -0
  39. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/token_kind_filter.rb +111 -0
  40. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/xml.rb +72 -0
  41. data/lib/llm/shell/internal/coderay/lib/coderay/encoders/yaml.rb +50 -0
  42. data/lib/llm/shell/internal/coderay/lib/coderay/encoders.rb +18 -0
  43. data/lib/llm/shell/internal/coderay/lib/coderay/for_redcloth.rb +95 -0
  44. data/lib/llm/shell/internal/coderay/lib/coderay/helpers/file_type.rb +151 -0
  45. data/lib/llm/shell/internal/coderay/lib/coderay/helpers/plugin.rb +55 -0
  46. data/lib/llm/shell/internal/coderay/lib/coderay/helpers/plugin_host.rb +221 -0
  47. data/lib/llm/shell/internal/coderay/lib/coderay/helpers/word_list.rb +72 -0
  48. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/_map.rb +24 -0
  49. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/c.rb +189 -0
  50. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/clojure.rb +217 -0
  51. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/cpp.rb +217 -0
  52. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/css.rb +196 -0
  53. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/debug.rb +75 -0
  54. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/delphi.rb +144 -0
  55. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/diff.rb +221 -0
  56. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/erb.rb +81 -0
  57. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/go.rb +208 -0
  58. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/groovy.rb +268 -0
  59. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/haml.rb +168 -0
  60. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/html.rb +275 -0
  61. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/java/builtin_types.rb +421 -0
  62. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/java.rb +174 -0
  63. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/java_script.rb +236 -0
  64. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/json.rb +98 -0
  65. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/lua.rb +280 -0
  66. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/php.rb +527 -0
  67. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/python.rb +287 -0
  68. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/raydebug.rb +75 -0
  69. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/ruby/patterns.rb +178 -0
  70. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/ruby/string_state.rb +79 -0
  71. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/ruby.rb +477 -0
  72. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/sass.rb +232 -0
  73. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/scanner.rb +337 -0
  74. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/sql.rb +169 -0
  75. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/taskpaper.rb +36 -0
  76. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/text.rb +26 -0
  77. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/xml.rb +17 -0
  78. data/lib/llm/shell/internal/coderay/lib/coderay/scanners/yaml.rb +140 -0
  79. data/lib/llm/shell/internal/coderay/lib/coderay/scanners.rb +27 -0
  80. data/lib/llm/shell/internal/coderay/lib/coderay/styles/_map.rb +7 -0
  81. data/lib/llm/shell/internal/coderay/lib/coderay/styles/alpha.rb +153 -0
  82. data/lib/llm/shell/internal/coderay/lib/coderay/styles/style.rb +18 -0
  83. data/lib/llm/shell/internal/coderay/lib/coderay/styles.rb +15 -0
  84. data/lib/llm/shell/internal/coderay/lib/coderay/token_kinds.rb +85 -0
  85. data/lib/llm/shell/internal/coderay/lib/coderay/tokens.rb +164 -0
  86. data/lib/llm/shell/internal/coderay/lib/coderay/tokens_proxy.rb +55 -0
  87. data/lib/llm/shell/internal/coderay/lib/coderay/version.rb +3 -0
  88. data/lib/llm/shell/internal/coderay/lib/coderay.rb +284 -0
  89. data/lib/llm/shell/internal/io-line/lib/io/line/multiple.rb +19 -0
  90. data/lib/{io → llm/shell/internal/io-line/lib/io}/line.rb +2 -0
  91. data/lib/llm/shell/internal/llm.rb/lib/llm/bot/builder.rb +31 -0
  92. data/lib/llm/shell/internal/llm.rb/lib/llm/bot/conversable.rb +37 -0
  93. data/lib/llm/shell/internal/llm.rb/lib/llm/bot/prompt/completion.rb +49 -0
  94. data/lib/llm/shell/internal/llm.rb/lib/llm/bot/prompt/respond.rb +49 -0
  95. data/lib/llm/shell/internal/llm.rb/lib/llm/bot.rb +150 -0
  96. data/lib/llm/shell/internal/llm.rb/lib/llm/buffer.rb +162 -0
  97. data/lib/llm/shell/internal/llm.rb/lib/llm/client.rb +36 -0
  98. data/lib/llm/shell/internal/llm.rb/lib/llm/error.rb +49 -0
  99. data/lib/llm/shell/internal/llm.rb/lib/llm/eventhandler.rb +44 -0
  100. data/lib/llm/shell/internal/llm.rb/lib/llm/eventstream/event.rb +69 -0
  101. data/lib/llm/shell/internal/llm.rb/lib/llm/eventstream/parser.rb +88 -0
  102. data/lib/llm/shell/internal/llm.rb/lib/llm/eventstream.rb +8 -0
  103. data/lib/llm/shell/internal/llm.rb/lib/llm/file.rb +91 -0
  104. data/lib/llm/shell/internal/llm.rb/lib/llm/function.rb +177 -0
  105. data/lib/llm/shell/internal/llm.rb/lib/llm/message.rb +178 -0
  106. data/lib/llm/shell/internal/llm.rb/lib/llm/mime.rb +140 -0
  107. data/lib/llm/shell/internal/llm.rb/lib/llm/multipart.rb +101 -0
  108. data/lib/llm/shell/internal/llm.rb/lib/llm/object/builder.rb +38 -0
  109. data/lib/llm/shell/internal/llm.rb/lib/llm/object/kernel.rb +53 -0
  110. data/lib/llm/shell/internal/llm.rb/lib/llm/object.rb +89 -0
  111. data/lib/llm/shell/internal/llm.rb/lib/llm/provider.rb +352 -0
  112. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/error_handler.rb +36 -0
  113. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/files.rb +155 -0
  114. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/format/completion_format.rb +88 -0
  115. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/format.rb +29 -0
  116. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/models.rb +54 -0
  117. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/response/completion.rb +39 -0
  118. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/response/enumerable.rb +11 -0
  119. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/response/file.rb +23 -0
  120. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/response/web_search.rb +21 -0
  121. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/stream_parser.rb +66 -0
  122. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic.rb +138 -0
  123. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/deepseek/format/completion_format.rb +68 -0
  124. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/deepseek/format.rb +27 -0
  125. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/deepseek.rb +75 -0
  126. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/audio.rb +73 -0
  127. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/error_handler.rb +47 -0
  128. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/files.rb +146 -0
  129. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/format/completion_format.rb +69 -0
  130. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/format.rb +39 -0
  131. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/images.rb +133 -0
  132. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/models.rb +60 -0
  133. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/response/completion.rb +35 -0
  134. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/response/embedding.rb +8 -0
  135. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/response/file.rb +11 -0
  136. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/response/files.rb +15 -0
  137. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/response/image.rb +31 -0
  138. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/response/models.rb +15 -0
  139. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/response/web_search.rb +22 -0
  140. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/stream_parser.rb +86 -0
  141. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini.rb +173 -0
  142. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/llamacpp.rb +74 -0
  143. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/ollama/error_handler.rb +36 -0
  144. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/ollama/format/completion_format.rb +77 -0
  145. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/ollama/format.rb +29 -0
  146. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/ollama/models.rb +56 -0
  147. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/ollama/response/completion.rb +28 -0
  148. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/ollama/response/embedding.rb +9 -0
  149. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/ollama/stream_parser.rb +44 -0
  150. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/ollama.rb +116 -0
  151. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/audio.rb +91 -0
  152. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/error_handler.rb +46 -0
  153. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/files.rb +134 -0
  154. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/format/completion_format.rb +90 -0
  155. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/format/moderation_format.rb +35 -0
  156. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/format/respond_format.rb +72 -0
  157. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/format.rb +54 -0
  158. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/images.rb +109 -0
  159. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/models.rb +55 -0
  160. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/moderations.rb +65 -0
  161. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/response/audio.rb +7 -0
  162. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/response/completion.rb +40 -0
  163. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/response/embedding.rb +9 -0
  164. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/response/enumerable.rb +23 -0
  165. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/response/file.rb +7 -0
  166. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/response/image.rb +16 -0
  167. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/response/moderations.rb +34 -0
  168. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/response/responds.rb +48 -0
  169. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/response/web_search.rb +21 -0
  170. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/responses/stream_parser.rb +76 -0
  171. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/responses.rb +99 -0
  172. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/stream_parser.rb +86 -0
  173. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/vector_stores.rb +228 -0
  174. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai.rb +206 -0
  175. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/xai/images.rb +58 -0
  176. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/xai.rb +72 -0
  177. data/lib/llm/shell/internal/llm.rb/lib/llm/providers/zai.rb +74 -0
  178. data/lib/llm/shell/internal/llm.rb/lib/llm/response.rb +67 -0
  179. data/lib/llm/shell/internal/llm.rb/lib/llm/schema/array.rb +26 -0
  180. data/lib/llm/shell/internal/llm.rb/lib/llm/schema/boolean.rb +13 -0
  181. data/lib/llm/shell/internal/llm.rb/lib/llm/schema/integer.rb +43 -0
  182. data/lib/llm/shell/internal/llm.rb/lib/llm/schema/leaf.rb +78 -0
  183. data/lib/llm/shell/internal/llm.rb/lib/llm/schema/null.rb +13 -0
  184. data/lib/llm/shell/internal/llm.rb/lib/llm/schema/number.rb +43 -0
  185. data/lib/llm/shell/internal/llm.rb/lib/llm/schema/object.rb +41 -0
  186. data/lib/llm/shell/internal/llm.rb/lib/llm/schema/string.rb +34 -0
  187. data/lib/llm/shell/internal/llm.rb/lib/llm/schema/version.rb +8 -0
  188. data/lib/llm/shell/internal/llm.rb/lib/llm/schema.rb +81 -0
  189. data/lib/llm/shell/internal/llm.rb/lib/llm/server_tool.rb +32 -0
  190. data/lib/llm/shell/internal/llm.rb/lib/llm/tool/param.rb +75 -0
  191. data/lib/llm/shell/internal/llm.rb/lib/llm/tool.rb +78 -0
  192. data/lib/llm/shell/internal/llm.rb/lib/llm/utils.rb +19 -0
  193. data/lib/llm/shell/internal/llm.rb/lib/llm/version.rb +5 -0
  194. data/lib/llm/shell/internal/llm.rb/lib/llm.rb +121 -0
  195. data/lib/llm/shell/internal/optparse/lib/optionparser.rb +2 -0
  196. data/lib/llm/shell/internal/optparse/lib/optparse/ac.rb +70 -0
  197. data/lib/llm/shell/internal/optparse/lib/optparse/date.rb +18 -0
  198. data/lib/llm/shell/internal/optparse/lib/optparse/kwargs.rb +27 -0
  199. data/lib/llm/shell/internal/optparse/lib/optparse/shellwords.rb +7 -0
  200. data/lib/llm/shell/internal/optparse/lib/optparse/time.rb +11 -0
  201. data/lib/llm/shell/internal/optparse/lib/optparse/uri.rb +7 -0
  202. data/lib/llm/shell/internal/optparse/lib/optparse/version.rb +80 -0
  203. data/lib/llm/shell/internal/optparse/lib/optparse.rb +2469 -0
  204. data/lib/llm/shell/internal/paint/lib/paint/constants.rb +104 -0
  205. data/lib/llm/shell/internal/paint/lib/paint/pa.rb +13 -0
  206. data/lib/llm/shell/internal/paint/lib/paint/rgb_colors.rb +14 -0
  207. data/lib/llm/shell/internal/paint/lib/paint/shortcuts.rb +100 -0
  208. data/lib/llm/shell/internal/paint/lib/paint/shortcuts_version.rb +5 -0
  209. data/lib/llm/shell/internal/paint/lib/paint/util.rb +16 -0
  210. data/lib/llm/shell/internal/paint/lib/paint/version.rb +5 -0
  211. data/lib/llm/shell/internal/paint/lib/paint.rb +261 -0
  212. data/lib/llm/shell/internal/reline/lib/reline/config.rb +378 -0
  213. data/lib/llm/shell/internal/reline/lib/reline/face.rb +199 -0
  214. data/lib/llm/shell/internal/reline/lib/reline/history.rb +76 -0
  215. data/lib/llm/shell/internal/reline/lib/reline/io/ansi.rb +322 -0
  216. data/lib/llm/shell/internal/reline/lib/reline/io/dumb.rb +120 -0
  217. data/lib/llm/shell/internal/reline/lib/reline/io/windows.rb +530 -0
  218. data/lib/llm/shell/internal/reline/lib/reline/io.rb +55 -0
  219. data/lib/llm/shell/internal/reline/lib/reline/key_actor/base.rb +37 -0
  220. data/lib/llm/shell/internal/reline/lib/reline/key_actor/composite.rb +17 -0
  221. data/lib/llm/shell/internal/reline/lib/reline/key_actor/emacs.rb +517 -0
  222. data/lib/llm/shell/internal/reline/lib/reline/key_actor/vi_command.rb +518 -0
  223. data/lib/llm/shell/internal/reline/lib/reline/key_actor/vi_insert.rb +517 -0
  224. data/lib/llm/shell/internal/reline/lib/reline/key_actor.rb +8 -0
  225. data/lib/llm/shell/internal/reline/lib/reline/key_stroke.rb +119 -0
  226. data/lib/llm/shell/internal/reline/lib/reline/kill_ring.rb +125 -0
  227. data/lib/llm/shell/internal/reline/lib/reline/line_editor.rb +2356 -0
  228. data/lib/llm/shell/internal/reline/lib/reline/unicode/east_asian_width.rb +1292 -0
  229. data/lib/llm/shell/internal/reline/lib/reline/unicode.rb +421 -0
  230. data/lib/llm/shell/internal/reline/lib/reline/version.rb +3 -0
  231. data/lib/llm/shell/internal/reline/lib/reline.rb +527 -0
  232. data/lib/llm/shell/internal/tomlrb/lib/tomlrb/generated_parser.rb +712 -0
  233. data/lib/llm/shell/internal/tomlrb/lib/tomlrb/handler.rb +268 -0
  234. data/lib/llm/shell/internal/tomlrb/lib/tomlrb/local_date.rb +35 -0
  235. data/lib/llm/shell/internal/tomlrb/lib/tomlrb/local_date_time.rb +42 -0
  236. data/lib/llm/shell/internal/tomlrb/lib/tomlrb/local_time.rb +40 -0
  237. data/lib/llm/shell/internal/tomlrb/lib/tomlrb/parser.rb +21 -0
  238. data/lib/llm/shell/internal/tomlrb/lib/tomlrb/scanner.rb +92 -0
  239. data/lib/llm/shell/internal/tomlrb/lib/tomlrb/string_utils.rb +40 -0
  240. data/lib/llm/shell/internal/tomlrb/lib/tomlrb/version.rb +5 -0
  241. data/lib/llm/shell/internal/tomlrb/lib/tomlrb.rb +49 -0
  242. data/lib/llm/shell/options.rb +1 -1
  243. data/lib/llm/shell/renderer.rb +2 -3
  244. data/lib/llm/shell/repl.rb +21 -16
  245. data/lib/llm/shell/tool.rb +42 -0
  246. data/lib/llm/shell/tools/read_file.rb +15 -0
  247. data/lib/llm/shell/tools/system.rb +17 -0
  248. data/lib/llm/shell/tools/write_file.rb +16 -0
  249. data/lib/llm/shell/version.rb +1 -1
  250. data/lib/llm/shell.rb +83 -39
  251. data/libexec/llm-shell/shell +4 -6
  252. data/llm-shell.gemspec +0 -4
  253. metadata +233 -63
  254. data/lib/llm/function.rb +0 -17
  255. data/lib/llm/shell/command/extension.rb +0 -42
  256. data/lib/llm/shell/commands/utils.rb +0 -21
  257. data/lib/llm/shell/functions/read_file.rb +0 -22
  258. data/lib/llm/shell/functions/write_file.rb +0 -22
@@ -0,0 +1,2469 @@
1
+ # frozen_string_literal: true
2
+ #
3
+ # optparse.rb - command-line option analysis with the OptionParser class.
4
+ #
5
+ # Author:: Nobu Nakada
6
+ # Documentation:: Nobu Nakada and Gavin Sinclair.
7
+ #
8
+ # See OptionParser for documentation.
9
+ #
10
+ require 'set' unless defined?(Set)
11
+
12
+ #--
13
+ # == Developer Documentation (not for RDoc output)
14
+ #
15
+ # === Class tree
16
+ #
17
+ # - OptionParser:: front end
18
+ # - OptionParser::Switch:: each switches
19
+ # - OptionParser::List:: options list
20
+ # - OptionParser::ParseError:: errors on parsing
21
+ # - OptionParser::AmbiguousOption
22
+ # - OptionParser::NeedlessArgument
23
+ # - OptionParser::MissingArgument
24
+ # - OptionParser::InvalidOption
25
+ # - OptionParser::InvalidArgument
26
+ # - OptionParser::AmbiguousArgument
27
+ #
28
+ # === Object relationship diagram
29
+ #
30
+ # +--------------+
31
+ # | OptionParser |<>-----+
32
+ # +--------------+ | +--------+
33
+ # | ,-| Switch |
34
+ # on_head -------->+---------------+ / +--------+
35
+ # accept/reject -->| List |<|>-
36
+ # | |<|>- +----------+
37
+ # on ------------->+---------------+ `-| argument |
38
+ # : : | class |
39
+ # +---------------+ |==========|
40
+ # on_tail -------->| | |pattern |
41
+ # +---------------+ |----------|
42
+ # OptionParser.accept ->| DefaultList | |converter |
43
+ # reject |(shared between| +----------+
44
+ # | all instances)|
45
+ # +---------------+
46
+ #
47
+ #++
48
+ #
49
+ # == OptionParser
50
+ #
51
+ # === New to +OptionParser+?
52
+ #
53
+ # See the {Tutorial}[optparse/tutorial.rdoc].
54
+ #
55
+ # === Introduction
56
+ #
57
+ # OptionParser is a class for command-line option analysis. It is much more
58
+ # advanced, yet also easier to use, than GetoptLong, and is a more Ruby-oriented
59
+ # solution.
60
+ #
61
+ # === Features
62
+ #
63
+ # 1. The argument specification and the code to handle it are written in the
64
+ # same place.
65
+ # 2. It can output an option summary; you don't need to maintain this string
66
+ # separately.
67
+ # 3. Optional and mandatory arguments are specified very gracefully.
68
+ # 4. Arguments can be automatically converted to a specified class.
69
+ # 5. Arguments can be restricted to a certain set.
70
+ #
71
+ # All of these features are demonstrated in the examples below. See
72
+ # #make_switch for full documentation.
73
+ #
74
+ # === Minimal example
75
+ #
76
+ # require 'optparse'
77
+ #
78
+ # options = {}
79
+ # OptionParser.new do |parser|
80
+ # parser.banner = "Usage: example.rb [options]"
81
+ #
82
+ # parser.on("-v", "--[no-]verbose", "Run verbosely") do |v|
83
+ # options[:verbose] = v
84
+ # end
85
+ # end.parse!
86
+ #
87
+ # p options
88
+ # p ARGV
89
+ #
90
+ # === Generating Help
91
+ #
92
+ # OptionParser can be used to automatically generate help for the commands you
93
+ # write:
94
+ #
95
+ # require 'optparse'
96
+ #
97
+ # Options = Struct.new(:name)
98
+ #
99
+ # class Parser
100
+ # def self.parse(options)
101
+ # args = Options.new("world")
102
+ #
103
+ # opt_parser = OptionParser.new do |parser|
104
+ # parser.banner = "Usage: example.rb [options]"
105
+ #
106
+ # parser.on("-nNAME", "--name=NAME", "Name to say hello to") do |n|
107
+ # args.name = n
108
+ # end
109
+ #
110
+ # parser.on("-h", "--help", "Prints this help") do
111
+ # puts parser
112
+ # exit
113
+ # end
114
+ # end
115
+ #
116
+ # opt_parser.parse!(options)
117
+ # return args
118
+ # end
119
+ # end
120
+ # options = Parser.parse %w[--help]
121
+ #
122
+ # #=>
123
+ # # Usage: example.rb [options]
124
+ # # -n, --name=NAME Name to say hello to
125
+ # # -h, --help Prints this help
126
+ #
127
+ # === Required Arguments
128
+ #
129
+ # For options that require an argument, option specification strings may include an
130
+ # option name in all caps. If an option is used without the required argument,
131
+ # an exception will be raised.
132
+ #
133
+ # require 'optparse'
134
+ #
135
+ # options = {}
136
+ # OptionParser.new do |parser|
137
+ # parser.on("-r", "--require LIBRARY",
138
+ # "Require the LIBRARY before executing your script") do |lib|
139
+ # puts "You required #{lib}!"
140
+ # end
141
+ # end.parse!
142
+ #
143
+ # Used:
144
+ #
145
+ # $ ruby optparse-test.rb -r
146
+ # optparse-test.rb:9:in '<main>': missing argument: -r (OptionParser::MissingArgument)
147
+ # $ ruby optparse-test.rb -r my-library
148
+ # You required my-library!
149
+ #
150
+ # === Type Coercion
151
+ #
152
+ # OptionParser supports the ability to coerce command line arguments
153
+ # into objects for us.
154
+ #
155
+ # OptionParser comes with a few ready-to-use kinds of type
156
+ # coercion. They are:
157
+ #
158
+ # - Date -- Anything accepted by +Date.parse+ (need to require +optparse/date+)
159
+ # - DateTime -- Anything accepted by +DateTime.parse+ (need to require +optparse/date+)
160
+ # - Time -- Anything accepted by +Time.httpdate+ or +Time.parse+ (need to require +optparse/time+)
161
+ # - URI -- Anything accepted by +URI.parse+ (need to require +optparse/uri+)
162
+ # - Shellwords -- Anything accepted by +Shellwords.shellwords+ (need to require +optparse/shellwords+)
163
+ # - String -- Any non-empty string
164
+ # - Integer -- Any integer. Will convert octal. (e.g. 124, -3, 040)
165
+ # - Float -- Any float. (e.g. 10, 3.14, -100E+13)
166
+ # - Numeric -- Any integer, float, or rational (1, 3.4, 1/3)
167
+ # - DecimalInteger -- Like +Integer+, but no octal format.
168
+ # - OctalInteger -- Like +Integer+, but no decimal format.
169
+ # - DecimalNumeric -- Decimal integer or float.
170
+ # - TrueClass -- Accepts '+, yes, true, -, no, false' and
171
+ # defaults as +true+
172
+ # - FalseClass -- Same as +TrueClass+, but defaults to +false+
173
+ # - Array -- Strings separated by ',' (e.g. 1,2,3)
174
+ # - Regexp -- Regular expressions. Also includes options.
175
+ #
176
+ # We can also add our own coercions, which we will cover below.
177
+ #
178
+ # ==== Using Built-in Conversions
179
+ #
180
+ # As an example, the built-in +Time+ conversion is used. The other built-in
181
+ # conversions behave in the same way.
182
+ # OptionParser will attempt to parse the argument
183
+ # as a +Time+. If it succeeds, that time will be passed to the
184
+ # handler block. Otherwise, an exception will be raised.
185
+ #
186
+ # require 'optparse'
187
+ # require 'optparse/time'
188
+ # OptionParser.new do |parser|
189
+ # parser.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time|
190
+ # p time
191
+ # end
192
+ # end.parse!
193
+ #
194
+ # Used:
195
+ #
196
+ # $ ruby optparse-test.rb -t nonsense
197
+ # ... invalid argument: -t nonsense (OptionParser::InvalidArgument)
198
+ # $ ruby optparse-test.rb -t 10-11-12
199
+ # 2010-11-12 00:00:00 -0500
200
+ # $ ruby optparse-test.rb -t 9:30
201
+ # 2014-08-13 09:30:00 -0400
202
+ #
203
+ # ==== Creating Custom Conversions
204
+ #
205
+ # The +accept+ method on OptionParser may be used to create converters.
206
+ # It specifies which conversion block to call whenever a class is specified.
207
+ # The example below uses it to fetch a +User+ object before the +on+ handler receives it.
208
+ #
209
+ # require 'optparse'
210
+ #
211
+ # User = Struct.new(:id, :name)
212
+ #
213
+ # def find_user id
214
+ # not_found = ->{ raise "No User Found for id #{id}" }
215
+ # [ User.new(1, "Sam"),
216
+ # User.new(2, "Gandalf") ].find(not_found) do |u|
217
+ # u.id == id
218
+ # end
219
+ # end
220
+ #
221
+ # op = OptionParser.new
222
+ # op.accept(User) do |user_id|
223
+ # find_user user_id.to_i
224
+ # end
225
+ #
226
+ # op.on("--user ID", User) do |user|
227
+ # puts user
228
+ # end
229
+ #
230
+ # op.parse!
231
+ #
232
+ # Used:
233
+ #
234
+ # $ ruby optparse-test.rb --user 1
235
+ # #<struct User id=1, name="Sam">
236
+ # $ ruby optparse-test.rb --user 2
237
+ # #<struct User id=2, name="Gandalf">
238
+ # $ ruby optparse-test.rb --user 3
239
+ # optparse-test.rb:15:in 'block in find_user': No User Found for id 3 (RuntimeError)
240
+ #
241
+ # === Store options to a Hash
242
+ #
243
+ # The +into+ option of +order+, +parse+ and so on methods stores command line options into a Hash.
244
+ #
245
+ # require 'optparse'
246
+ #
247
+ # options = {}
248
+ # OptionParser.new do |parser|
249
+ # parser.on('-a')
250
+ # parser.on('-b NUM', Integer)
251
+ # parser.on('-v', '--verbose')
252
+ # end.parse!(into: options)
253
+ #
254
+ # p options
255
+ #
256
+ # Used:
257
+ #
258
+ # $ ruby optparse-test.rb -a
259
+ # {:a=>true}
260
+ # $ ruby optparse-test.rb -a -v
261
+ # {:a=>true, :verbose=>true}
262
+ # $ ruby optparse-test.rb -a -b 100
263
+ # {:a=>true, :b=>100}
264
+ #
265
+ # === Complete example
266
+ #
267
+ # The following example is a complete Ruby program. You can run it and see the
268
+ # effect of specifying various options. This is probably the best way to learn
269
+ # the features of +optparse+.
270
+ #
271
+ # require 'optparse'
272
+ # require 'optparse/time'
273
+ # require 'ostruct'
274
+ # require 'pp'
275
+ #
276
+ # class OptparseExample
277
+ # Version = '1.0.0'
278
+ #
279
+ # CODES = %w[iso-2022-jp shift_jis euc-jp utf8 binary]
280
+ # CODE_ALIASES = { "jis" => "iso-2022-jp", "sjis" => "shift_jis" }
281
+ #
282
+ # class ScriptOptions
283
+ # attr_accessor :library, :inplace, :encoding, :transfer_type,
284
+ # :verbose, :extension, :delay, :time, :record_separator,
285
+ # :list
286
+ #
287
+ # def initialize
288
+ # self.library = []
289
+ # self.inplace = false
290
+ # self.encoding = "utf8"
291
+ # self.transfer_type = :auto
292
+ # self.verbose = false
293
+ # end
294
+ #
295
+ # def define_options(parser)
296
+ # parser.banner = "Usage: example.rb [options]"
297
+ # parser.separator ""
298
+ # parser.separator "Specific options:"
299
+ #
300
+ # # add additional options
301
+ # perform_inplace_option(parser)
302
+ # delay_execution_option(parser)
303
+ # execute_at_time_option(parser)
304
+ # specify_record_separator_option(parser)
305
+ # list_example_option(parser)
306
+ # specify_encoding_option(parser)
307
+ # optional_option_argument_with_keyword_completion_option(parser)
308
+ # boolean_verbose_option(parser)
309
+ #
310
+ # parser.separator ""
311
+ # parser.separator "Common options:"
312
+ # # No argument, shows at tail. This will print an options summary.
313
+ # # Try it and see!
314
+ # parser.on_tail("-h", "--help", "Show this message") do
315
+ # puts parser
316
+ # exit
317
+ # end
318
+ # # Another typical switch to print the version.
319
+ # parser.on_tail("--version", "Show version") do
320
+ # puts Version
321
+ # exit
322
+ # end
323
+ # end
324
+ #
325
+ # def perform_inplace_option(parser)
326
+ # # Specifies an optional option argument
327
+ # parser.on("-i", "--inplace [EXTENSION]",
328
+ # "Edit ARGV files in place",
329
+ # "(make backup if EXTENSION supplied)") do |ext|
330
+ # self.inplace = true
331
+ # self.extension = ext || ''
332
+ # self.extension.sub!(/\A\.?(?=.)/, ".") # Ensure extension begins with dot.
333
+ # end
334
+ # end
335
+ #
336
+ # def delay_execution_option(parser)
337
+ # # Cast 'delay' argument to a Float.
338
+ # parser.on("--delay N", Float, "Delay N seconds before executing") do |n|
339
+ # self.delay = n
340
+ # end
341
+ # end
342
+ #
343
+ # def execute_at_time_option(parser)
344
+ # # Cast 'time' argument to a Time object.
345
+ # parser.on("-t", "--time [TIME]", Time, "Begin execution at given time") do |time|
346
+ # self.time = time
347
+ # end
348
+ # end
349
+ #
350
+ # def specify_record_separator_option(parser)
351
+ # # Cast to octal integer.
352
+ # parser.on("-F", "--irs [OCTAL]", OptionParser::OctalInteger,
353
+ # "Specify record separator (default \\0)") do |rs|
354
+ # self.record_separator = rs
355
+ # end
356
+ # end
357
+ #
358
+ # def list_example_option(parser)
359
+ # # List of arguments.
360
+ # parser.on("--list x,y,z", Array, "Example 'list' of arguments") do |list|
361
+ # self.list = list
362
+ # end
363
+ # end
364
+ #
365
+ # def specify_encoding_option(parser)
366
+ # # Keyword completion. We are specifying a specific set of arguments (CODES
367
+ # # and CODE_ALIASES - notice the latter is a Hash), and the user may provide
368
+ # # the shortest unambiguous text.
369
+ # code_list = (CODE_ALIASES.keys + CODES).join(', ')
370
+ # parser.on("--code CODE", CODES, CODE_ALIASES, "Select encoding",
371
+ # "(#{code_list})") do |encoding|
372
+ # self.encoding = encoding
373
+ # end
374
+ # end
375
+ #
376
+ # def optional_option_argument_with_keyword_completion_option(parser)
377
+ # # Optional '--type' option argument with keyword completion.
378
+ # parser.on("--type [TYPE]", [:text, :binary, :auto],
379
+ # "Select transfer type (text, binary, auto)") do |t|
380
+ # self.transfer_type = t
381
+ # end
382
+ # end
383
+ #
384
+ # def boolean_verbose_option(parser)
385
+ # # Boolean switch.
386
+ # parser.on("-v", "--[no-]verbose", "Run verbosely") do |v|
387
+ # self.verbose = v
388
+ # end
389
+ # end
390
+ # end
391
+ #
392
+ # #
393
+ # # Return a structure describing the options.
394
+ # #
395
+ # def parse(args)
396
+ # # The options specified on the command line will be collected in
397
+ # # *options*.
398
+ #
399
+ # @options = ScriptOptions.new
400
+ # @args = OptionParser.new do |parser|
401
+ # @options.define_options(parser)
402
+ # parser.parse!(args)
403
+ # end
404
+ # @options
405
+ # end
406
+ #
407
+ # attr_reader :parser, :options
408
+ # end # class OptparseExample
409
+ #
410
+ # example = OptparseExample.new
411
+ # options = example.parse(ARGV)
412
+ # pp options # example.options
413
+ # pp ARGV
414
+ #
415
+ # === Shell Completion
416
+ #
417
+ # For modern shells (e.g. bash, zsh, etc.), you can use shell
418
+ # completion for command line options.
419
+ #
420
+ # === Further documentation
421
+ #
422
+ # The above examples, along with the accompanying
423
+ # {Tutorial}[optparse/tutorial.rdoc],
424
+ # should be enough to learn how to use this class.
425
+ # If you have any questions, file a ticket at http://bugs.ruby-lang.org.
426
+ #
427
+ class OptionParser
428
+ # The version string
429
+ OptionParser::Version = "0.7.0.dev.2"
430
+
431
+ # :stopdoc:
432
+ NoArgument = [NO_ARGUMENT = :NONE, nil].freeze
433
+ RequiredArgument = [REQUIRED_ARGUMENT = :REQUIRED, true].freeze
434
+ OptionalArgument = [OPTIONAL_ARGUMENT = :OPTIONAL, false].freeze
435
+ # :startdoc:
436
+
437
+ #
438
+ # Keyword completion module. This allows partial arguments to be specified
439
+ # and resolved against a list of acceptable values.
440
+ #
441
+ module Completion
442
+ # :nodoc:
443
+
444
+ def self.regexp(key, icase)
445
+ Regexp.new('\A' + Regexp.quote(key).gsub(/\w+\b/, '\&\w*'), icase)
446
+ end
447
+
448
+ def self.candidate(key, icase = false, pat = nil, &block)
449
+ pat ||= Completion.regexp(key, icase)
450
+ candidates = []
451
+ block.call do |k, *v|
452
+ (if Regexp === k
453
+ kn = ""
454
+ k === key
455
+ else
456
+ kn = defined?(k.id2name) ? k.id2name : k
457
+ pat === kn
458
+ end) or next
459
+ v << k if v.empty?
460
+ candidates << [k, v, kn]
461
+ end
462
+ candidates
463
+ end
464
+
465
+ def self.completable?(key)
466
+ String.try_convert(key) or defined?(key.id2name)
467
+ end
468
+
469
+ def candidate(key, icase = false, pat = nil, &_)
470
+ Completion.candidate(key, icase, pat, &method(:each))
471
+ end
472
+
473
+ public
474
+ def complete(key, icase = false, pat = nil)
475
+ candidates = candidate(key, icase, pat, &method(:each)).sort_by {|k, v, kn| kn.size}
476
+ if candidates.size == 1
477
+ canon, sw, * = candidates[0]
478
+ elsif candidates.size > 1
479
+ canon, sw, cn = candidates.shift
480
+ candidates.each do |k, v, kn|
481
+ next if sw == v
482
+ if String === cn and String === kn
483
+ if cn.rindex(kn, 0)
484
+ canon, sw, cn = k, v, kn
485
+ next
486
+ elsif kn.rindex(cn, 0)
487
+ next
488
+ end
489
+ end
490
+ throw :ambiguous, key
491
+ end
492
+ end
493
+ if canon
494
+ block_given? or return key, *sw
495
+ yield(key, *sw)
496
+ end
497
+ end
498
+
499
+ def convert(opt = nil, val = nil, *)
500
+ val
501
+ end
502
+ end
503
+
504
+ #
505
+ # Map from option/keyword string to object with completion.
506
+ #
507
+ class OptionMap < Hash
508
+ include Completion
509
+ end
510
+
511
+ #
512
+ # Individual switch class. Not important to the user.
513
+ #
514
+ # Defined within Switch are several Switch-derived classes: NoArgument,
515
+ # RequiredArgument, etc.
516
+ #
517
+ class Switch
518
+ # :nodoc:
519
+
520
+ attr_reader :pattern, :conv, :short, :long, :arg, :desc, :block
521
+
522
+ #
523
+ # Guesses argument style from +arg+. Returns corresponding
524
+ # OptionParser::Switch class (OptionalArgument, etc.).
525
+ #
526
+ def self.guess(arg)
527
+ case arg
528
+ when ""
529
+ t = self
530
+ when /\A=?\[/
531
+ t = Switch::OptionalArgument
532
+ when /\A\s+\[/
533
+ t = Switch::PlacedArgument
534
+ else
535
+ t = Switch::RequiredArgument
536
+ end
537
+ self >= t or incompatible_argument_styles(arg, t)
538
+ t
539
+ end
540
+
541
+ def self.incompatible_argument_styles(arg, t)
542
+ raise(ArgumentError, "#{arg}: incompatible argument styles\n #{self}, #{t}",
543
+ ParseError.filter_backtrace(caller(2)))
544
+ end
545
+
546
+ def self.pattern
547
+ NilClass
548
+ end
549
+
550
+ def initialize(pattern = nil, conv = nil,
551
+ short = nil, long = nil, arg = nil,
552
+ desc = ([] if short or long), block = nil, values = nil, &_block)
553
+ raise if Array === pattern
554
+ block ||= _block
555
+ @pattern, @conv, @short, @long, @arg, @desc, @block, @values =
556
+ pattern, conv, short, long, arg, desc, block, values
557
+ end
558
+
559
+ #
560
+ # Parses +arg+ and returns rest of +arg+ and matched portion to the
561
+ # argument pattern. Yields when the pattern doesn't match substring.
562
+ #
563
+ def parse_arg(arg) # :nodoc:
564
+ pattern or return nil, [arg]
565
+ unless m = pattern.match(arg)
566
+ yield(InvalidArgument, arg)
567
+ return arg, []
568
+ end
569
+ if String === m
570
+ m = [s = m]
571
+ else
572
+ m = m.to_a
573
+ s = m[0]
574
+ return nil, m unless String === s
575
+ end
576
+ raise InvalidArgument, arg unless arg.rindex(s, 0)
577
+ return nil, m if s.length == arg.length
578
+ yield(InvalidArgument, arg) # didn't match whole arg
579
+ return arg[s.length..-1], m
580
+ end
581
+ private :parse_arg
582
+
583
+ #
584
+ # Parses argument, converts and returns +arg+, +block+ and result of
585
+ # conversion. Yields at semi-error condition instead of raising an
586
+ # exception.
587
+ #
588
+ def conv_arg(arg, val = []) # :nodoc:
589
+ v, = *val
590
+ if conv
591
+ val = conv.call(*val)
592
+ else
593
+ val = proc {|v| v}.call(*val)
594
+ end
595
+ if @values
596
+ @values.include?(val) or raise InvalidArgument, v
597
+ end
598
+ return arg, block, val
599
+ end
600
+ private :conv_arg
601
+
602
+ #
603
+ # Produces the summary text. Each line of the summary is yielded to the
604
+ # block (without newline).
605
+ #
606
+ # +sdone+:: Already summarized short style options keyed hash.
607
+ # +ldone+:: Already summarized long style options keyed hash.
608
+ # +width+:: Width of left side (option part). In other words, the right
609
+ # side (description part) starts after +width+ columns.
610
+ # +max+:: Maximum width of left side -> the options are filled within
611
+ # +max+ columns.
612
+ # +indent+:: Prefix string indents all summarized lines.
613
+ #
614
+ def summarize(sdone = {}, ldone = {}, width = 1, max = width - 1, indent = "")
615
+ sopts, lopts = [], [], nil
616
+ @short.each {|s| sdone.fetch(s) {sopts << s}; sdone[s] = true} if @short
617
+ @long.each {|s| ldone.fetch(s) {lopts << s}; ldone[s] = true} if @long
618
+ return if sopts.empty? and lopts.empty? # completely hidden
619
+
620
+ left = [sopts.join(', ')]
621
+ right = desc.dup
622
+
623
+ while s = lopts.shift
624
+ l = left[-1].length + s.length
625
+ l += arg.length if left.size == 1 && arg
626
+ l < max or sopts.empty? or left << +''
627
+ left[-1] << (left[-1].empty? ? ' ' * 4 : ', ') << s
628
+ end
629
+
630
+ if arg
631
+ left[0] << (left[1] ? arg.sub(/\A(\[?)=/, '\1') + ',' : arg)
632
+ end
633
+ mlen = left.collect {|ss| ss.length}.max.to_i
634
+ while mlen > width and l = left.shift
635
+ mlen = left.collect {|ss| ss.length}.max.to_i if l.length == mlen
636
+ if l.length < width and (r = right[0]) and !r.empty?
637
+ l = l.to_s.ljust(width) + ' ' + r
638
+ right.shift
639
+ end
640
+ yield(indent + l)
641
+ end
642
+
643
+ while begin l = left.shift; r = right.shift; l or r end
644
+ l = l.to_s.ljust(width) + ' ' + r if r and !r.empty?
645
+ yield(indent + l)
646
+ end
647
+
648
+ self
649
+ end
650
+
651
+ def add_banner(to) # :nodoc:
652
+ unless @short or @long
653
+ s = desc.join
654
+ to << " [" + s + "]..." unless s.empty?
655
+ end
656
+ to
657
+ end
658
+
659
+ def match_nonswitch?(str) # :nodoc:
660
+ @pattern =~ str unless @short or @long
661
+ end
662
+
663
+ #
664
+ # Main name of the switch.
665
+ #
666
+ def switch_name
667
+ (long.first || short.first).sub(/\A-+(?:\[no-\])?/, '')
668
+ end
669
+
670
+ def compsys(sdone, ldone) # :nodoc:
671
+ sopts, lopts = [], []
672
+ @short.each {|s| sdone.fetch(s) {sopts << s}; sdone[s] = true} if @short
673
+ @long.each {|s| ldone.fetch(s) {lopts << s}; ldone[s] = true} if @long
674
+ return if sopts.empty? and lopts.empty? # completely hidden
675
+
676
+ (sopts+lopts).each do |opt|
677
+ # "(-x -c -r)-l[left justify]"
678
+ if /\A--\[no-\](.+)$/ =~ opt
679
+ o = $1
680
+ yield("--#{o}", desc.join(""))
681
+ yield("--no-#{o}", desc.join(""))
682
+ else
683
+ yield("#{opt}", desc.join(""))
684
+ end
685
+ end
686
+ end
687
+
688
+ def pretty_print_contents(q) # :nodoc:
689
+ if @block
690
+ q.text ":" + @block.source_location.join(":") + ":"
691
+ first = false
692
+ else
693
+ first = true
694
+ end
695
+ [@short, @long].each do |list|
696
+ list.each do |opt|
697
+ if first
698
+ q.text ":"
699
+ first = false
700
+ end
701
+ q.breakable
702
+ q.text opt
703
+ end
704
+ end
705
+ end
706
+
707
+ def pretty_print(q) # :nodoc:
708
+ q.object_group(self) {pretty_print_contents(q)}
709
+ end
710
+
711
+ def omitted_argument(val) # :nodoc:
712
+ val.pop if val.size == 3 and val.last.nil?
713
+ val
714
+ end
715
+
716
+ #
717
+ # Switch that takes no arguments.
718
+ #
719
+ class NoArgument < self
720
+
721
+ #
722
+ # Raises an exception if any arguments given.
723
+ #
724
+ def parse(arg, argv)
725
+ yield(NeedlessArgument, arg) if arg
726
+ conv_arg(arg)
727
+ end
728
+
729
+ def self.incompatible_argument_styles(*) # :nodoc:
730
+ end
731
+
732
+ def self.pattern # :nodoc:
733
+ Object
734
+ end
735
+
736
+ def pretty_head # :nodoc:
737
+ "NoArgument"
738
+ end
739
+ end
740
+
741
+ #
742
+ # Switch that takes an argument.
743
+ #
744
+ class RequiredArgument < self
745
+
746
+ #
747
+ # Raises an exception if argument is not present.
748
+ #
749
+ def parse(arg, argv, &_)
750
+ unless arg
751
+ raise MissingArgument if argv.empty?
752
+ arg = argv.shift
753
+ end
754
+ conv_arg(*parse_arg(arg, &method(:raise)))
755
+ end
756
+
757
+ def pretty_head # :nodoc:
758
+ "Required"
759
+ end
760
+ end
761
+
762
+ #
763
+ # Switch that can omit argument.
764
+ #
765
+ class OptionalArgument < self
766
+
767
+ #
768
+ # Parses argument if given, or uses default value.
769
+ #
770
+ def parse(arg, argv, &error)
771
+ if arg
772
+ conv_arg(*parse_arg(arg, &error))
773
+ else
774
+ omitted_argument conv_arg(arg)
775
+ end
776
+ end
777
+
778
+ def pretty_head # :nodoc:
779
+ "Optional"
780
+ end
781
+ end
782
+
783
+ #
784
+ # Switch that takes an argument, which does not begin with '-' or is '-'.
785
+ #
786
+ class PlacedArgument < self
787
+
788
+ #
789
+ # Returns nil if argument is not present or begins with '-' and is not '-'.
790
+ #
791
+ def parse(arg, argv, &error)
792
+ if !(val = arg) and (argv.empty? or /\A-./ =~ (val = argv[0]))
793
+ return nil, block
794
+ end
795
+ opt = (val = parse_arg(val, &error))[1]
796
+ val = conv_arg(*val)
797
+ if opt and !arg
798
+ argv.shift
799
+ else
800
+ omitted_argument val
801
+ val[0] = nil
802
+ end
803
+ val
804
+ end
805
+
806
+ def pretty_head # :nodoc:
807
+ "Placed"
808
+ end
809
+ end
810
+ end
811
+
812
+ #
813
+ # Simple option list providing mapping from short and/or long option
814
+ # string to OptionParser::Switch and mapping from acceptable argument to
815
+ # matching pattern and converter pair. Also provides summary feature.
816
+ #
817
+ class List
818
+ # :nodoc:
819
+
820
+ # Map from acceptable argument types to pattern and converter pairs.
821
+ attr_reader :atype
822
+
823
+ # Map from short style option switches to actual switch objects.
824
+ attr_reader :short
825
+
826
+ # Map from long style option switches to actual switch objects.
827
+ attr_reader :long
828
+
829
+ # List of all switches and summary string.
830
+ attr_reader :list
831
+
832
+ #
833
+ # Just initializes all instance variables.
834
+ #
835
+ def initialize
836
+ @atype = {}
837
+ @short = OptionMap.new
838
+ @long = OptionMap.new
839
+ @list = []
840
+ end
841
+
842
+ def pretty_print(q) # :nodoc:
843
+ q.group(1, "(", ")") do
844
+ @list.each do |sw|
845
+ next unless Switch === sw
846
+ q.group(1, "(" + sw.pretty_head, ")") do
847
+ sw.pretty_print_contents(q)
848
+ end
849
+ end
850
+ end
851
+ end
852
+
853
+ #
854
+ # See OptionParser.accept.
855
+ #
856
+ def accept(t, pat = /.*/m, &block)
857
+ if pat
858
+ pat.respond_to?(:match) or
859
+ raise TypeError, "has no 'match'", ParseError.filter_backtrace(caller(2))
860
+ else
861
+ pat = t if t.respond_to?(:match)
862
+ end
863
+ unless block
864
+ block = pat.method(:convert).to_proc if pat.respond_to?(:convert)
865
+ end
866
+ @atype[t] = [pat, block]
867
+ end
868
+
869
+ #
870
+ # See OptionParser.reject.
871
+ #
872
+ def reject(t)
873
+ @atype.delete(t)
874
+ end
875
+
876
+ #
877
+ # Adds +sw+ according to +sopts+, +lopts+ and +nlopts+.
878
+ #
879
+ # +sw+:: OptionParser::Switch instance to be added.
880
+ # +sopts+:: Short style option list.
881
+ # +lopts+:: Long style option list.
882
+ # +nlopts+:: Negated long style options list.
883
+ #
884
+ def update(sw, sopts, lopts, nsw = nil, nlopts = nil) # :nodoc:
885
+ sopts.each {|o| @short[o] = sw} if sopts
886
+ lopts.each {|o| @long[o] = sw} if lopts
887
+ nlopts.each {|o| @long[o] = nsw} if nsw and nlopts
888
+ used = @short.invert.update(@long.invert)
889
+ @list.delete_if {|o| Switch === o and !used[o]}
890
+ end
891
+ private :update
892
+
893
+ #
894
+ # Inserts +switch+ at the head of the list, and associates short, long
895
+ # and negated long options. Arguments are:
896
+ #
897
+ # +switch+:: OptionParser::Switch instance to be inserted.
898
+ # +short_opts+:: List of short style options.
899
+ # +long_opts+:: List of long style options.
900
+ # +nolong_opts+:: List of long style options with "no-" prefix.
901
+ #
902
+ # prepend(switch, short_opts, long_opts, nolong_opts)
903
+ #
904
+ def prepend(*args)
905
+ update(*args)
906
+ @list.unshift(args[0])
907
+ end
908
+
909
+ #
910
+ # Appends +switch+ at the tail of the list, and associates short, long
911
+ # and negated long options. Arguments are:
912
+ #
913
+ # +switch+:: OptionParser::Switch instance to be inserted.
914
+ # +short_opts+:: List of short style options.
915
+ # +long_opts+:: List of long style options.
916
+ # +nolong_opts+:: List of long style options with "no-" prefix.
917
+ #
918
+ # append(switch, short_opts, long_opts, nolong_opts)
919
+ #
920
+ def append(*args)
921
+ update(*args)
922
+ @list.push(args[0])
923
+ end
924
+
925
+ #
926
+ # Searches +key+ in +id+ list. The result is returned or yielded if a
927
+ # block is given. If it isn't found, nil is returned.
928
+ #
929
+ def search(id, key)
930
+ if list = __send__(id)
931
+ val = list.fetch(key) {return nil}
932
+ block_given? ? yield(val) : val
933
+ end
934
+ end
935
+
936
+ #
937
+ # Searches list +id+ for +opt+ and the optional patterns for completion
938
+ # +pat+. If +icase+ is true, the search is case insensitive. The result
939
+ # is returned or yielded if a block is given. If it isn't found, nil is
940
+ # returned.
941
+ #
942
+ def complete(id, opt, icase = false, *pat, &block)
943
+ __send__(id).complete(opt, icase, *pat, &block)
944
+ end
945
+
946
+ def get_candidates(id)
947
+ yield __send__(id).keys
948
+ end
949
+
950
+ #
951
+ # Iterates over each option, passing the option to the +block+.
952
+ #
953
+ def each_option(&block)
954
+ list.each(&block)
955
+ end
956
+
957
+ #
958
+ # Creates the summary table, passing each line to the +block+ (without
959
+ # newline). The arguments +args+ are passed along to the summarize
960
+ # method which is called on every option.
961
+ #
962
+ def summarize(*args, &block)
963
+ sum = []
964
+ list.reverse_each do |opt|
965
+ if opt.respond_to?(:summarize) # perhaps OptionParser::Switch
966
+ s = []
967
+ opt.summarize(*args) {|l| s << l}
968
+ sum.concat(s.reverse)
969
+ elsif !opt or opt.empty?
970
+ sum << ""
971
+ elsif opt.respond_to?(:each_line)
972
+ sum.concat([*opt.each_line].reverse)
973
+ else
974
+ sum.concat([*opt.each].reverse)
975
+ end
976
+ end
977
+ sum.reverse_each(&block)
978
+ end
979
+
980
+ def add_banner(to) # :nodoc:
981
+ list.each do |opt|
982
+ if opt.respond_to?(:add_banner)
983
+ opt.add_banner(to)
984
+ end
985
+ end
986
+ to
987
+ end
988
+
989
+ def compsys(*args, &block) # :nodoc:
990
+ list.each do |opt|
991
+ if opt.respond_to?(:compsys)
992
+ opt.compsys(*args, &block)
993
+ end
994
+ end
995
+ end
996
+ end
997
+
998
+ #
999
+ # Hash with completion search feature. See OptionParser::Completion.
1000
+ #
1001
+ class CompletingHash < Hash
1002
+ include Completion
1003
+
1004
+ #
1005
+ # Completion for hash key.
1006
+ #
1007
+ def match(key)
1008
+ *values = fetch(key) {
1009
+ raise AmbiguousArgument, catch(:ambiguous) {return complete(key)}
1010
+ }
1011
+ return key, *values
1012
+ end
1013
+ end
1014
+
1015
+ # :stopdoc:
1016
+
1017
+ #
1018
+ # Enumeration of acceptable argument styles. Possible values are:
1019
+ #
1020
+ # NO_ARGUMENT:: The switch takes no arguments. (:NONE)
1021
+ # REQUIRED_ARGUMENT:: The switch requires an argument. (:REQUIRED)
1022
+ # OPTIONAL_ARGUMENT:: The switch requires an optional argument. (:OPTIONAL)
1023
+ #
1024
+ # Use like --switch=argument (long style) or -Xargument (short style). For
1025
+ # short style, only portion matched to argument pattern is treated as
1026
+ # argument.
1027
+ #
1028
+ ArgumentStyle = {}
1029
+ NoArgument.each {|el| ArgumentStyle[el] = Switch::NoArgument}
1030
+ RequiredArgument.each {|el| ArgumentStyle[el] = Switch::RequiredArgument}
1031
+ OptionalArgument.each {|el| ArgumentStyle[el] = Switch::OptionalArgument}
1032
+ ArgumentStyle.freeze
1033
+
1034
+ #
1035
+ # Switches common used such as '--', and also provides default
1036
+ # argument classes
1037
+ #
1038
+ DefaultList = List.new
1039
+ DefaultList.short['-'] = Switch::NoArgument.new {}
1040
+ DefaultList.long[''] = Switch::NoArgument.new {throw :terminate}
1041
+
1042
+ COMPSYS_HEADER = <<'XXX' # :nodoc:
1043
+
1044
+ typeset -A opt_args
1045
+ local context state line
1046
+
1047
+ _arguments -s -S \
1048
+ XXX
1049
+
1050
+ def compsys(to, name = File.basename($0)) # :nodoc:
1051
+ to << "#compdef #{name}\n"
1052
+ to << COMPSYS_HEADER
1053
+ visit(:compsys, {}, {}) {|o, d|
1054
+ to << %Q[ "#{o}[#{d.gsub(/[\\\"\[\]]/, '\\\\\&')}]" \\\n]
1055
+ }
1056
+ to << " '*:file:_files' && return 0\n"
1057
+ end
1058
+
1059
+ def help_exit
1060
+ if $stdout.tty? && (pager = ENV.values_at(*%w[RUBY_PAGER PAGER]).find {|e| e && !e.empty?})
1061
+ less = ENV["LESS"]
1062
+ args = [{"LESS" => "#{less} -Fe"}, pager, "w"]
1063
+ print = proc do |f|
1064
+ f.puts help
1065
+ rescue Errno::EPIPE
1066
+ # pager terminated
1067
+ end
1068
+ if Process.respond_to?(:fork) and false
1069
+ IO.popen("-") {|f| f ? Process.exec(*args, in: f) : print.call($stdout)}
1070
+ # unreachable
1071
+ end
1072
+ IO.popen(*args, &print)
1073
+ else
1074
+ puts help
1075
+ end
1076
+ exit
1077
+ end
1078
+
1079
+ #
1080
+ # Default options for ARGV, which never appear in option summary.
1081
+ #
1082
+ Officious = {}
1083
+
1084
+ #
1085
+ # --help
1086
+ # Shows option summary.
1087
+ #
1088
+ Officious['help'] = proc do |parser|
1089
+ Switch::NoArgument.new do |arg|
1090
+ parser.help_exit
1091
+ end
1092
+ end
1093
+
1094
+ #
1095
+ # --*-completion-bash=WORD
1096
+ # Shows candidates for command line completion.
1097
+ #
1098
+ Officious['*-completion-bash'] = proc do |parser|
1099
+ Switch::RequiredArgument.new do |arg|
1100
+ puts parser.candidate(arg)
1101
+ exit
1102
+ end
1103
+ end
1104
+
1105
+ #
1106
+ # --*-completion-zsh[=NAME:FILE]
1107
+ # Creates zsh completion file.
1108
+ #
1109
+ Officious['*-completion-zsh'] = proc do |parser|
1110
+ Switch::OptionalArgument.new do |arg|
1111
+ parser.compsys($stdout, arg)
1112
+ exit
1113
+ end
1114
+ end
1115
+
1116
+ #
1117
+ # --version
1118
+ # Shows version string if Version is defined.
1119
+ #
1120
+ Officious['version'] = proc do |parser|
1121
+ Switch::OptionalArgument.new do |pkg|
1122
+ if pkg
1123
+ begin
1124
+ require_relative 'optparse/version'
1125
+ rescue LoadError
1126
+ else
1127
+ show_version(*pkg.split(/,/)) or
1128
+ abort("#{parser.program_name}: no version found in package #{pkg}")
1129
+ exit
1130
+ end
1131
+ end
1132
+ v = parser.ver or abort("#{parser.program_name}: version unknown")
1133
+ puts v
1134
+ exit
1135
+ end
1136
+ end
1137
+
1138
+ # :startdoc:
1139
+
1140
+ #
1141
+ # Class methods
1142
+ #
1143
+
1144
+ #
1145
+ # Initializes a new instance and evaluates the optional block in context
1146
+ # of the instance. Arguments +args+ are passed to #new, see there for
1147
+ # description of parameters.
1148
+ #
1149
+ # This method is *deprecated*, its behavior corresponds to the older #new
1150
+ # method.
1151
+ #
1152
+ def self.with(*args, &block)
1153
+ opts = new(*args)
1154
+ opts.instance_eval(&block)
1155
+ opts
1156
+ end
1157
+
1158
+ #
1159
+ # Returns an incremented value of +default+ according to +arg+.
1160
+ #
1161
+ def self.inc(arg, default = nil)
1162
+ case arg
1163
+ when Integer
1164
+ arg.nonzero?
1165
+ when nil
1166
+ default.to_i + 1
1167
+ end
1168
+ end
1169
+
1170
+ #
1171
+ # See self.inc
1172
+ #
1173
+ def inc(*args)
1174
+ self.class.inc(*args)
1175
+ end
1176
+
1177
+ #
1178
+ # Initializes the instance and yields itself if called with a block.
1179
+ #
1180
+ # +banner+:: Banner message.
1181
+ # +width+:: Summary width.
1182
+ # +indent+:: Summary indent.
1183
+ #
1184
+ def initialize(banner = nil, width = 32, indent = ' ' * 4)
1185
+ @stack = [DefaultList, List.new, List.new]
1186
+ @program_name = nil
1187
+ @banner = banner
1188
+ @summary_width = width
1189
+ @summary_indent = indent
1190
+ @default_argv = ARGV
1191
+ @require_exact = false
1192
+ @raise_unknown = true
1193
+ add_officious
1194
+ yield self if block_given?
1195
+ end
1196
+
1197
+ def add_officious # :nodoc:
1198
+ list = base()
1199
+ Officious.each do |opt, block|
1200
+ list.long[opt] ||= block.call(self)
1201
+ end
1202
+ end
1203
+
1204
+ #
1205
+ # Terminates option parsing. Optional parameter +arg+ is a string pushed
1206
+ # back to be the first non-option argument.
1207
+ #
1208
+ def terminate(arg = nil)
1209
+ self.class.terminate(arg)
1210
+ end
1211
+ #
1212
+ # See #terminate.
1213
+ #
1214
+ def self.terminate(arg = nil)
1215
+ throw :terminate, arg
1216
+ end
1217
+
1218
+ @stack = [DefaultList]
1219
+ #
1220
+ # Returns the global top option list.
1221
+ #
1222
+ # Do not use directly.
1223
+ #
1224
+ def self.top() DefaultList end
1225
+
1226
+ #
1227
+ # Directs to accept specified class +t+. The argument string is passed to
1228
+ # the block in which it should be converted to the desired class.
1229
+ #
1230
+ # +t+:: Argument class specifier, any object including Class.
1231
+ # +pat+:: Pattern for argument, defaults to +t+ if it responds to match.
1232
+ #
1233
+ # accept(t, pat, &block)
1234
+ #
1235
+ def accept(*args, &blk) top.accept(*args, &blk) end
1236
+ #
1237
+ # See #accept.
1238
+ #
1239
+ def self.accept(*args, &blk) top.accept(*args, &blk) end
1240
+
1241
+ #
1242
+ # Directs to reject specified class argument.
1243
+ #
1244
+ # +type+:: Argument class specifier, any object including Class.
1245
+ #
1246
+ # reject(type)
1247
+ #
1248
+ def reject(*args, &blk) top.reject(*args, &blk) end
1249
+ #
1250
+ # See #reject.
1251
+ #
1252
+ def self.reject(*args, &blk) top.reject(*args, &blk) end
1253
+
1254
+ #
1255
+ # Instance methods
1256
+ #
1257
+
1258
+ # Heading banner preceding summary.
1259
+ attr_writer :banner
1260
+
1261
+ # Program name to be emitted in error message and default banner,
1262
+ # defaults to $0.
1263
+ attr_writer :program_name
1264
+
1265
+ # Width for option list portion of summary. Must be Numeric.
1266
+ attr_accessor :summary_width
1267
+
1268
+ # Indentation for summary. Must be String (or have + String method).
1269
+ attr_accessor :summary_indent
1270
+
1271
+ # Strings to be parsed in default.
1272
+ attr_accessor :default_argv
1273
+
1274
+ # Whether to require that options match exactly (disallows providing
1275
+ # abbreviated long option as short option).
1276
+ attr_accessor :require_exact
1277
+
1278
+ # Whether to raise at unknown option.
1279
+ attr_accessor :raise_unknown
1280
+
1281
+ #
1282
+ # Heading banner preceding summary.
1283
+ #
1284
+ def banner
1285
+ unless @banner
1286
+ @banner = +"Usage: #{program_name} [options]"
1287
+ visit(:add_banner, @banner)
1288
+ end
1289
+ @banner
1290
+ end
1291
+
1292
+ #
1293
+ # Program name to be emitted in error message and default banner, defaults
1294
+ # to $0.
1295
+ #
1296
+ def program_name
1297
+ @program_name || strip_ext(File.basename($0))
1298
+ end
1299
+
1300
+ private def strip_ext(name) # :nodoc:
1301
+ exts = /#{
1302
+ require "rbconfig"
1303
+ Regexp.union(*RbConfig::CONFIG["EXECUTABLE_EXTS"]&.split(" "))
1304
+ }\z/o
1305
+ name.sub(exts, "")
1306
+ end
1307
+
1308
+ # for experimental cascading :-)
1309
+ alias set_banner banner=
1310
+ alias set_program_name program_name=
1311
+ alias set_summary_width summary_width=
1312
+ alias set_summary_indent summary_indent=
1313
+
1314
+ # Version
1315
+ attr_writer :version
1316
+ # Release code
1317
+ attr_writer :release
1318
+
1319
+ #
1320
+ # Version
1321
+ #
1322
+ def version
1323
+ (defined?(@version) && @version) || (defined?(::Version) && ::Version)
1324
+ end
1325
+
1326
+ #
1327
+ # Release code
1328
+ #
1329
+ def release
1330
+ (defined?(@release) && @release) || (defined?(::Release) && ::Release) || (defined?(::RELEASE) && ::RELEASE)
1331
+ end
1332
+
1333
+ #
1334
+ # Returns version string from program_name, version and release.
1335
+ #
1336
+ def ver
1337
+ if v = version
1338
+ str = +"#{program_name} #{[v].join('.')}"
1339
+ str << " (#{v})" if v = release
1340
+ str
1341
+ end
1342
+ end
1343
+
1344
+ #
1345
+ # Shows warning message with the program name
1346
+ #
1347
+ # +mesg+:: Message, defaulted to +$!+.
1348
+ #
1349
+ # See Kernel#warn.
1350
+ #
1351
+ def warn(mesg = $!)
1352
+ super("#{program_name}: #{mesg}")
1353
+ end
1354
+
1355
+ #
1356
+ # Shows message with the program name then aborts.
1357
+ #
1358
+ # +mesg+:: Message, defaulted to +$!+.
1359
+ #
1360
+ # See Kernel#abort.
1361
+ #
1362
+ def abort(mesg = $!)
1363
+ super("#{program_name}: #{mesg}")
1364
+ end
1365
+
1366
+ #
1367
+ # Subject of #on / #on_head, #accept / #reject
1368
+ #
1369
+ def top
1370
+ @stack[-1]
1371
+ end
1372
+
1373
+ #
1374
+ # Subject of #on_tail.
1375
+ #
1376
+ def base
1377
+ @stack[1]
1378
+ end
1379
+
1380
+ #
1381
+ # Pushes a new List.
1382
+ #
1383
+ # If a block is given, yields +self+ and returns the result of the
1384
+ # block, otherwise returns +self+.
1385
+ #
1386
+ def new
1387
+ @stack.push(List.new)
1388
+ if block_given?
1389
+ yield self
1390
+ else
1391
+ self
1392
+ end
1393
+ end
1394
+
1395
+ #
1396
+ # Removes the last List.
1397
+ #
1398
+ def remove
1399
+ @stack.pop
1400
+ end
1401
+
1402
+ #
1403
+ # Puts option summary into +to+ and returns +to+. Yields each line if
1404
+ # a block is given.
1405
+ #
1406
+ # +to+:: Output destination, which must have method <<. Defaults to [].
1407
+ # +width+:: Width of left side, defaults to @summary_width.
1408
+ # +max+:: Maximum length allowed for left side, defaults to +width+ - 1.
1409
+ # +indent+:: Indentation, defaults to @summary_indent.
1410
+ #
1411
+ def summarize(to = [], width = @summary_width, max = width - 1, indent = @summary_indent, &blk)
1412
+ nl = "\n"
1413
+ blk ||= proc {|l| to << (l.index(nl, -1) ? l : l + nl)}
1414
+ visit(:summarize, {}, {}, width, max, indent, &blk)
1415
+ to
1416
+ end
1417
+
1418
+ #
1419
+ # Returns option summary string.
1420
+ #
1421
+ def help; summarize("#{banner}".sub(/\n?\z/, "\n")) end
1422
+ alias to_s help
1423
+
1424
+ def pretty_print(q) # :nodoc:
1425
+ q.object_group(self) do
1426
+ first = true
1427
+ if @stack.size > 2
1428
+ @stack.each_with_index do |s, i|
1429
+ next if i < 2
1430
+ next if s.list.empty?
1431
+ if first
1432
+ first = false
1433
+ q.text ":"
1434
+ end
1435
+ q.breakable
1436
+ s.pretty_print(q)
1437
+ end
1438
+ end
1439
+ end
1440
+ end
1441
+
1442
+ def inspect # :nodoc:
1443
+ require 'pp'
1444
+ pretty_print_inspect
1445
+ end
1446
+
1447
+ #
1448
+ # Returns option summary list.
1449
+ #
1450
+ def to_a; summarize("#{banner}".split(/^/)) end
1451
+
1452
+ #
1453
+ # Checks if an argument is given twice, in which case an ArgumentError is
1454
+ # raised. Called from OptionParser#switch only.
1455
+ #
1456
+ # +obj+:: New argument.
1457
+ # +prv+:: Previously specified argument.
1458
+ # +msg+:: Exception message.
1459
+ #
1460
+ def notwice(obj, prv, msg) # :nodoc:
1461
+ unless !prv or prv == obj
1462
+ raise(ArgumentError, "argument #{msg} given twice: #{obj}",
1463
+ ParseError.filter_backtrace(caller(2)))
1464
+ end
1465
+ obj
1466
+ end
1467
+ private :notwice
1468
+
1469
+ SPLAT_PROC = proc {|*a| a.length <= 1 ? a.first : a} # :nodoc:
1470
+
1471
+ # :call-seq:
1472
+ # make_switch(params, block = nil)
1473
+ #
1474
+ # :include: ../doc/optparse/creates_option.rdoc
1475
+ #
1476
+ def make_switch(opts, block = nil)
1477
+ short, long, nolong, style, pattern, conv, not_pattern, not_conv, not_style = [], [], []
1478
+ ldesc, sdesc, desc, arg = [], [], []
1479
+ default_style = Switch::NoArgument
1480
+ default_pattern = nil
1481
+ klass = nil
1482
+ q, a = nil
1483
+ has_arg = false
1484
+ values = nil
1485
+
1486
+ opts.each do |o|
1487
+ # argument class
1488
+ next if search(:atype, o) do |pat, c|
1489
+ klass = notwice(o, klass, 'type')
1490
+ if not_style and not_style != Switch::NoArgument
1491
+ not_pattern, not_conv = pat, c
1492
+ else
1493
+ default_pattern, conv = pat, c
1494
+ end
1495
+ end
1496
+
1497
+ # directly specified pattern(any object possible to match)
1498
+ if !Completion.completable?(o) and o.respond_to?(:match)
1499
+ pattern = notwice(o, pattern, 'pattern')
1500
+ if pattern.respond_to?(:convert)
1501
+ conv = pattern.method(:convert).to_proc
1502
+ else
1503
+ conv = SPLAT_PROC
1504
+ end
1505
+ next
1506
+ end
1507
+
1508
+ # anything others
1509
+ case o
1510
+ when Proc, Method
1511
+ block = notwice(o, block, 'block')
1512
+ when Array, Hash, Set
1513
+ if Array === o
1514
+ o, v = o.partition {|v,| Completion.completable?(v)}
1515
+ values = notwice(v, values, 'values') unless v.empty?
1516
+ next if o.empty?
1517
+ end
1518
+ case pattern
1519
+ when CompletingHash
1520
+ when nil
1521
+ pattern = CompletingHash.new
1522
+ conv = pattern.method(:convert).to_proc if pattern.respond_to?(:convert)
1523
+ else
1524
+ raise ArgumentError, "argument pattern given twice"
1525
+ end
1526
+ o.each {|pat, *v| pattern[pat] = v.fetch(0) {pat}}
1527
+ when Range
1528
+ values = notwice(o, values, 'values')
1529
+ when Module
1530
+ raise ArgumentError, "unsupported argument type: #{o}", ParseError.filter_backtrace(caller(4))
1531
+ when *ArgumentStyle.keys
1532
+ style = notwice(ArgumentStyle[o], style, 'style')
1533
+ when /\A--no-([^\[\]=\s]*)(.+)?/
1534
+ q, a = $1, $2
1535
+ o = notwice(a ? Object : TrueClass, klass, 'type')
1536
+ not_pattern, not_conv = search(:atype, o) unless not_style
1537
+ not_style = (not_style || default_style).guess(arg = a) if a
1538
+ default_style = Switch::NoArgument
1539
+ default_pattern, conv = search(:atype, FalseClass) unless default_pattern
1540
+ ldesc << "--no-#{q}"
1541
+ (q = q.downcase).tr!('_', '-')
1542
+ long << "no-#{q}"
1543
+ nolong << q
1544
+ when /\A--\[no-\]([^\[\]=\s]*)(.+)?/
1545
+ q, a = $1, $2
1546
+ o = notwice(a ? Object : TrueClass, klass, 'type')
1547
+ if a
1548
+ default_style = default_style.guess(arg = a)
1549
+ default_pattern, conv = search(:atype, o) unless default_pattern
1550
+ end
1551
+ ldesc << "--[no-]#{q}"
1552
+ (o = q.downcase).tr!('_', '-')
1553
+ long << o
1554
+ not_pattern, not_conv = search(:atype, FalseClass) unless not_style
1555
+ not_style = Switch::NoArgument
1556
+ nolong << "no-#{o}"
1557
+ when /\A--([^\[\]=\s]*)(.+)?/
1558
+ q, a = $1, $2
1559
+ if a
1560
+ o = notwice(NilClass, klass, 'type')
1561
+ default_style = default_style.guess(arg = a)
1562
+ default_pattern, conv = search(:atype, o) unless default_pattern
1563
+ end
1564
+ ldesc << "--#{q}"
1565
+ (o = q.downcase).tr!('_', '-')
1566
+ long << o
1567
+ when /\A-(\[\^?\]?(?:[^\\\]]|\\.)*\])(.+)?/
1568
+ q, a = $1, $2
1569
+ o = notwice(Object, klass, 'type')
1570
+ if a
1571
+ default_style = default_style.guess(arg = a)
1572
+ default_pattern, conv = search(:atype, o) unless default_pattern
1573
+ else
1574
+ has_arg = true
1575
+ end
1576
+ sdesc << "-#{q}"
1577
+ short << Regexp.new(q)
1578
+ when /\A-(.)(.+)?/
1579
+ q, a = $1, $2
1580
+ if a
1581
+ o = notwice(NilClass, klass, 'type')
1582
+ default_style = default_style.guess(arg = a)
1583
+ default_pattern, conv = search(:atype, o) unless default_pattern
1584
+ end
1585
+ sdesc << "-#{q}"
1586
+ short << q
1587
+ when /\A=/
1588
+ style = notwice(default_style.guess(arg = o), style, 'style')
1589
+ default_pattern, conv = search(:atype, Object) unless default_pattern
1590
+ else
1591
+ desc.push(o) if o && !o.empty?
1592
+ end
1593
+ end
1594
+
1595
+ default_pattern, conv = search(:atype, default_style.pattern) unless default_pattern
1596
+ if Range === values and klass
1597
+ unless (!values.begin or klass === values.begin) and
1598
+ (!values.end or klass === values.end)
1599
+ raise ArgumentError, "range does not match class"
1600
+ end
1601
+ end
1602
+ if !(short.empty? and long.empty?)
1603
+ if has_arg and default_style == Switch::NoArgument
1604
+ default_style = Switch::RequiredArgument
1605
+ end
1606
+ s = (style || default_style).new(pattern || default_pattern,
1607
+ conv, sdesc, ldesc, arg, desc, block, values)
1608
+ elsif !block
1609
+ if style or pattern
1610
+ raise ArgumentError, "no switch given", ParseError.filter_backtrace(caller)
1611
+ end
1612
+ s = desc
1613
+ else
1614
+ short << pattern
1615
+ s = (style || default_style).new(pattern,
1616
+ conv, nil, nil, arg, desc, block, values)
1617
+ end
1618
+ return s, short, long,
1619
+ (not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style),
1620
+ nolong
1621
+ end
1622
+
1623
+ # ----
1624
+ # Option definition phase methods
1625
+ #
1626
+ # These methods are used to define options, or to construct an
1627
+ # OptionParser instance in other words.
1628
+
1629
+ # :call-seq:
1630
+ # define(*params, &block)
1631
+ #
1632
+ # :include: ../doc/optparse/creates_option.rdoc
1633
+ #
1634
+ def define(*opts, &block)
1635
+ top.append(*(sw = make_switch(opts, block)))
1636
+ sw[0]
1637
+ end
1638
+
1639
+ # :call-seq:
1640
+ # on(*params, &block)
1641
+ #
1642
+ # :include: ../doc/optparse/creates_option.rdoc
1643
+ #
1644
+ def on(*opts, &block)
1645
+ define(*opts, &block)
1646
+ self
1647
+ end
1648
+ alias def_option define
1649
+
1650
+ # :call-seq:
1651
+ # define_head(*params, &block)
1652
+ #
1653
+ # :include: ../doc/optparse/creates_option.rdoc
1654
+ #
1655
+ def define_head(*opts, &block)
1656
+ top.prepend(*(sw = make_switch(opts, block)))
1657
+ sw[0]
1658
+ end
1659
+
1660
+ # :call-seq:
1661
+ # on_head(*params, &block)
1662
+ #
1663
+ # :include: ../doc/optparse/creates_option.rdoc
1664
+ #
1665
+ # The new option is added at the head of the summary.
1666
+ #
1667
+ def on_head(*opts, &block)
1668
+ define_head(*opts, &block)
1669
+ self
1670
+ end
1671
+ alias def_head_option define_head
1672
+
1673
+ # :call-seq:
1674
+ # define_tail(*params, &block)
1675
+ #
1676
+ # :include: ../doc/optparse/creates_option.rdoc
1677
+ #
1678
+ def define_tail(*opts, &block)
1679
+ base.append(*(sw = make_switch(opts, block)))
1680
+ sw[0]
1681
+ end
1682
+
1683
+ #
1684
+ # :call-seq:
1685
+ # on_tail(*params, &block)
1686
+ #
1687
+ # :include: ../doc/optparse/creates_option.rdoc
1688
+ #
1689
+ # The new option is added at the tail of the summary.
1690
+ #
1691
+ def on_tail(*opts, &block)
1692
+ define_tail(*opts, &block)
1693
+ self
1694
+ end
1695
+ alias def_tail_option define_tail
1696
+
1697
+ #
1698
+ # Add separator in summary.
1699
+ #
1700
+ def separator(string)
1701
+ top.append(string, nil, nil)
1702
+ end
1703
+
1704
+ # ----
1705
+ # Arguments parse phase methods
1706
+ #
1707
+ # These methods parse +argv+, convert, and store the results by
1708
+ # calling handlers. As these methods do not modify +self+, +self+
1709
+ # can be frozen.
1710
+
1711
+ #
1712
+ # Parses command line arguments +argv+ in order. When a block is given,
1713
+ # each non-option argument is yielded. When optional +into+ keyword
1714
+ # argument is provided, the parsed option values are stored there via
1715
+ # <code>[]=</code> method (so it can be Hash, or OpenStruct, or other
1716
+ # similar object).
1717
+ #
1718
+ # Returns the rest of +argv+ left unparsed.
1719
+ #
1720
+ def order(*argv, **keywords, &nonopt)
1721
+ argv = argv[0].dup if argv.size == 1 and Array === argv[0]
1722
+ order!(argv, **keywords, &nonopt)
1723
+ end
1724
+
1725
+ #
1726
+ # Same as #order, but removes switches destructively.
1727
+ # Non-option arguments remain in +argv+.
1728
+ #
1729
+ def order!(argv = default_argv, into: nil, **keywords, &nonopt)
1730
+ setter = ->(name, val) {into[name.to_sym] = val} if into
1731
+ parse_in_order(argv, setter, **keywords, &nonopt)
1732
+ end
1733
+
1734
+ def parse_in_order(argv = default_argv, setter = nil, exact: require_exact, **, &nonopt) # :nodoc:
1735
+ opt, arg, val, rest = nil
1736
+ nonopt ||= proc {|a| throw :terminate, a}
1737
+ argv.unshift(arg) if arg = catch(:terminate) {
1738
+ while arg = argv.shift
1739
+ case arg
1740
+ # long option
1741
+ when /\A--([^=]*)(?:=(.*))?/m
1742
+ opt, rest = $1, $2
1743
+ opt.tr!('_', '-')
1744
+ begin
1745
+ if exact
1746
+ sw, = search(:long, opt)
1747
+ else
1748
+ sw, = complete(:long, opt, true)
1749
+ end
1750
+ rescue ParseError
1751
+ throw :terminate, arg unless raise_unknown
1752
+ raise $!.set_option(arg, true)
1753
+ else
1754
+ unless sw
1755
+ throw :terminate, arg unless raise_unknown
1756
+ raise InvalidOption, arg
1757
+ end
1758
+ end
1759
+ begin
1760
+ opt, cb, val = sw.parse(rest, argv) {|*exc| raise(*exc)}
1761
+ val = callback!(cb, 1, val) if cb
1762
+ callback!(setter, 2, sw.switch_name, val) if setter
1763
+ rescue ParseError
1764
+ raise $!.set_option(arg, rest)
1765
+ end
1766
+
1767
+ # short option
1768
+ when /\A-(.)((=).*|.+)?/m
1769
+ eq, rest, opt = $3, $2, $1
1770
+ has_arg, val = eq, rest
1771
+ begin
1772
+ sw, = search(:short, opt)
1773
+ unless sw
1774
+ begin
1775
+ sw, = complete(:short, opt)
1776
+ # short option matched.
1777
+ val = arg.delete_prefix('-')
1778
+ has_arg = true
1779
+ rescue InvalidOption
1780
+ raise if exact
1781
+ # if no short options match, try completion with long
1782
+ # options.
1783
+ sw, = complete(:long, opt)
1784
+ eq ||= !rest
1785
+ end
1786
+ end
1787
+ rescue ParseError
1788
+ throw :terminate, arg unless raise_unknown
1789
+ raise $!.set_option(arg, true)
1790
+ end
1791
+ begin
1792
+ opt, cb, val = sw.parse(val, argv) {|*exc| raise(*exc) if eq}
1793
+ rescue ParseError
1794
+ raise $!.set_option(arg, arg.length > 2)
1795
+ else
1796
+ raise InvalidOption, arg if has_arg and !eq and arg == "-#{opt}"
1797
+ end
1798
+ begin
1799
+ argv.unshift(opt) if opt and (!rest or (opt = opt.sub(/\A-*/, '-')) != '-')
1800
+ val = callback!(cb, 1, val) if cb
1801
+ callback!(setter, 2, sw.switch_name, val) if setter
1802
+ rescue ParseError
1803
+ raise $!.set_option(arg, arg.length > 2)
1804
+ end
1805
+
1806
+ # non-option argument
1807
+ else
1808
+ catch(:prune) do
1809
+ visit(:each_option) do |sw0|
1810
+ sw = sw0
1811
+ sw.block.call(arg) if Switch === sw and sw.match_nonswitch?(arg)
1812
+ end
1813
+ nonopt.call(arg)
1814
+ end
1815
+ end
1816
+ end
1817
+
1818
+ nil
1819
+ }
1820
+
1821
+ visit(:search, :short, nil) {|sw| sw.block.call(*argv) if !sw.pattern}
1822
+
1823
+ argv
1824
+ end
1825
+ private :parse_in_order
1826
+
1827
+ # Calls callback with _val_.
1828
+ def callback!(cb, max_arity, *args) # :nodoc:
1829
+ args.compact!
1830
+
1831
+ if (size = args.size) < max_arity and cb.to_proc.lambda?
1832
+ (arity = cb.arity) < 0 and arity = (1-arity)
1833
+ arity = max_arity if arity > max_arity
1834
+ args[arity - 1] = nil if arity > size
1835
+ end
1836
+ cb.call(*args)
1837
+ end
1838
+ private :callback!
1839
+
1840
+ #
1841
+ # Parses command line arguments +argv+ in permutation mode and returns
1842
+ # list of non-option arguments. When optional +into+ keyword
1843
+ # argument is provided, the parsed option values are stored there via
1844
+ # <code>[]=</code> method (so it can be Hash, or OpenStruct, or other
1845
+ # similar object).
1846
+ #
1847
+ def permute(*argv, **keywords)
1848
+ argv = argv[0].dup if argv.size == 1 and Array === argv[0]
1849
+ permute!(argv, **keywords)
1850
+ end
1851
+
1852
+ #
1853
+ # Same as #permute, but removes switches destructively.
1854
+ # Non-option arguments remain in +argv+.
1855
+ #
1856
+ def permute!(argv = default_argv, **keywords)
1857
+ nonopts = []
1858
+ order!(argv, **keywords) {|nonopt| nonopts << nonopt}
1859
+ argv[0, 0] = nonopts
1860
+ argv
1861
+ end
1862
+
1863
+ #
1864
+ # Parses command line arguments +argv+ in order when environment variable
1865
+ # POSIXLY_CORRECT is set, and in permutation mode otherwise.
1866
+ # When optional +into+ keyword argument is provided, the parsed option
1867
+ # values are stored there via <code>[]=</code> method (so it can be Hash,
1868
+ # or OpenStruct, or other similar object).
1869
+ #
1870
+ def parse(*argv, **keywords)
1871
+ argv = argv[0].dup if argv.size == 1 and Array === argv[0]
1872
+ parse!(argv, **keywords)
1873
+ end
1874
+
1875
+ #
1876
+ # Same as #parse, but removes switches destructively.
1877
+ # Non-option arguments remain in +argv+.
1878
+ #
1879
+ def parse!(argv = default_argv, **keywords)
1880
+ if ENV.include?('POSIXLY_CORRECT')
1881
+ order!(argv, **keywords)
1882
+ else
1883
+ permute!(argv, **keywords)
1884
+ end
1885
+ end
1886
+
1887
+ #
1888
+ # Wrapper method for getopts.rb.
1889
+ #
1890
+ # params = ARGV.getopts("ab:", "foo", "bar:", "zot:Z;zot option")
1891
+ # # params["a"] = true # -a
1892
+ # # params["b"] = "1" # -b1
1893
+ # # params["foo"] = "1" # --foo
1894
+ # # params["bar"] = "x" # --bar x
1895
+ # # params["zot"] = "z" # --zot Z
1896
+ #
1897
+ # Option +symbolize_names+ (boolean) specifies whether returned Hash keys should be Symbols; defaults to +false+ (use Strings).
1898
+ #
1899
+ # params = ARGV.getopts("ab:", "foo", "bar:", "zot:Z;zot option", symbolize_names: true)
1900
+ # # params[:a] = true # -a
1901
+ # # params[:b] = "1" # -b1
1902
+ # # params[:foo] = "1" # --foo
1903
+ # # params[:bar] = "x" # --bar x
1904
+ # # params[:zot] = "z" # --zot Z
1905
+ #
1906
+ def getopts(*args, symbolize_names: false, **keywords)
1907
+ argv = Array === args.first ? args.shift : default_argv
1908
+ single_options, *long_options = *args
1909
+
1910
+ result = {}
1911
+ setter = (symbolize_names ?
1912
+ ->(name, val) {result[name.to_sym] = val}
1913
+ : ->(name, val) {result[name] = val})
1914
+
1915
+ single_options.scan(/(.)(:)?/) do |opt, val|
1916
+ if val
1917
+ setter[opt, nil]
1918
+ define("-#{opt} VAL")
1919
+ else
1920
+ setter[opt, false]
1921
+ define("-#{opt}")
1922
+ end
1923
+ end if single_options
1924
+
1925
+ long_options.each do |arg|
1926
+ arg, desc = arg.split(';', 2)
1927
+ opt, val = arg.split(':', 2)
1928
+ if val
1929
+ setter[opt, (val unless val.empty?)]
1930
+ define("--#{opt}=#{result[opt] || "VAL"}", *[desc].compact)
1931
+ else
1932
+ setter[opt, false]
1933
+ define("--#{opt}", *[desc].compact)
1934
+ end
1935
+ end
1936
+
1937
+ parse_in_order(argv, setter, **keywords)
1938
+ result
1939
+ end
1940
+
1941
+ #
1942
+ # See #getopts.
1943
+ #
1944
+ def self.getopts(*args, symbolize_names: false)
1945
+ new.getopts(*args, symbolize_names: symbolize_names)
1946
+ end
1947
+
1948
+ #
1949
+ # Traverses @stack, sending each element method +id+ with +args+ and
1950
+ # +block+.
1951
+ #
1952
+ def visit(id, *args, &block) # :nodoc:
1953
+ @stack.reverse_each do |el|
1954
+ el.__send__(id, *args, &block)
1955
+ end
1956
+ nil
1957
+ end
1958
+ private :visit
1959
+
1960
+ #
1961
+ # Searches +key+ in @stack for +id+ hash and returns or yields the result.
1962
+ #
1963
+ def search(id, key) # :nodoc:
1964
+ block_given = block_given?
1965
+ visit(:search, id, key) do |k|
1966
+ return block_given ? yield(k) : k
1967
+ end
1968
+ end
1969
+ private :search
1970
+
1971
+ #
1972
+ # Completes shortened long style option switch and returns pair of
1973
+ # canonical switch and switch descriptor OptionParser::Switch.
1974
+ #
1975
+ # +typ+:: Searching table.
1976
+ # +opt+:: Searching key.
1977
+ # +icase+:: Search case insensitive if true.
1978
+ # +pat+:: Optional pattern for completion.
1979
+ #
1980
+ def complete(typ, opt, icase = false, *pat) # :nodoc:
1981
+ if pat.empty?
1982
+ search(typ, opt) {|sw| return [sw, opt]} # exact match or...
1983
+ end
1984
+ ambiguous = catch(:ambiguous) {
1985
+ visit(:complete, typ, opt, icase, *pat) {|o, *sw| return sw}
1986
+ }
1987
+ exc = ambiguous ? AmbiguousOption : InvalidOption
1988
+ raise exc.new(opt, additional: proc {|o| additional_message(typ, o)})
1989
+ end
1990
+ private :complete
1991
+
1992
+ #
1993
+ # Returns additional info.
1994
+ #
1995
+ def additional_message(typ, opt)
1996
+ return unless typ and opt and defined?(DidYouMean::SpellChecker)
1997
+ all_candidates = []
1998
+ visit(:get_candidates, typ) do |candidates|
1999
+ all_candidates.concat(candidates)
2000
+ end
2001
+ all_candidates.select! {|cand| cand.is_a?(String) }
2002
+ checker = DidYouMean::SpellChecker.new(dictionary: all_candidates)
2003
+ DidYouMean.formatter.message_for(all_candidates & checker.correct(opt))
2004
+ end
2005
+
2006
+ #
2007
+ # Return candidates for +word+.
2008
+ #
2009
+ def candidate(word)
2010
+ list = []
2011
+ case word
2012
+ when '-'
2013
+ long = short = true
2014
+ when /\A--/
2015
+ word, arg = word.split(/=/, 2)
2016
+ argpat = Completion.regexp(arg, false) if arg and !arg.empty?
2017
+ long = true
2018
+ when /\A-/
2019
+ short = true
2020
+ end
2021
+ pat = Completion.regexp(word, long)
2022
+ visit(:each_option) do |opt|
2023
+ next unless Switch === opt
2024
+ opts = (long ? opt.long : []) + (short ? opt.short : [])
2025
+ opts = Completion.candidate(word, true, pat, &opts.method(:each)).map(&:first) if pat
2026
+ if /\A=/ =~ opt.arg
2027
+ opts.map! {|sw| sw + "="}
2028
+ if arg and CompletingHash === opt.pattern
2029
+ if opts = opt.pattern.candidate(arg, false, argpat)
2030
+ opts.map!(&:last)
2031
+ end
2032
+ end
2033
+ end
2034
+ list.concat(opts)
2035
+ end
2036
+ list
2037
+ end
2038
+
2039
+ #
2040
+ # Loads options from file names as +filename+. Does nothing when the file
2041
+ # is not present. Returns whether successfully loaded.
2042
+ #
2043
+ # +filename+ defaults to basename of the program without suffix in a
2044
+ # directory ~/.options, then the basename with '.options' suffix
2045
+ # under XDG and Haiku standard places.
2046
+ #
2047
+ # The optional +into+ keyword argument works exactly like that accepted in
2048
+ # method #parse.
2049
+ #
2050
+ def load(filename = nil, **keywords)
2051
+ unless filename
2052
+ basename = File.basename($0, '.*')
2053
+ return true if load(File.expand_path("~/.options/#{basename}"), **keywords) rescue nil
2054
+ basename << ".options"
2055
+ if !(xdg = ENV['XDG_CONFIG_HOME']) or xdg.empty?
2056
+ # https://specifications.freedesktop.org/basedir-spec/latest/#variables
2057
+ #
2058
+ # If $XDG_CONFIG_HOME is either not set or empty, a default
2059
+ # equal to $HOME/.config should be used.
2060
+ xdg = ['~/.config', true]
2061
+ end
2062
+ return [
2063
+ xdg,
2064
+
2065
+ *ENV['XDG_CONFIG_DIRS']&.split(File::PATH_SEPARATOR),
2066
+
2067
+ # Haiku
2068
+ ['~/config/settings', true],
2069
+ ].any? {|dir, expand|
2070
+ next if !dir or dir.empty?
2071
+ filename = File.join(dir, basename)
2072
+ filename = File.expand_path(filename) if expand
2073
+ load(filename, **keywords) rescue nil
2074
+ }
2075
+ end
2076
+ begin
2077
+ parse(*File.readlines(filename, chomp: true), **keywords)
2078
+ true
2079
+ rescue Errno::ENOENT, Errno::ENOTDIR
2080
+ false
2081
+ end
2082
+ end
2083
+
2084
+ #
2085
+ # Parses environment variable +env+ or its uppercase with splitting like a
2086
+ # shell.
2087
+ #
2088
+ # +env+ defaults to the basename of the program.
2089
+ #
2090
+ def environment(env = File.basename($0, '.*'), **keywords)
2091
+ env = ENV[env] || ENV[env.upcase] or return
2092
+ require 'shellwords'
2093
+ parse(*Shellwords.shellwords(env), **keywords)
2094
+ end
2095
+
2096
+ #
2097
+ # Acceptable argument classes
2098
+ #
2099
+
2100
+ #
2101
+ # Any string and no conversion. This is fall-back.
2102
+ #
2103
+ accept(Object) {|s,|s or s.nil?}
2104
+
2105
+ accept(NilClass) {|s,|s}
2106
+
2107
+ #
2108
+ # Any non-empty string, and no conversion.
2109
+ #
2110
+ accept(String, /.+/m) {|s,*|s}
2111
+
2112
+ #
2113
+ # Ruby/C-like integer, octal for 0-7 sequence, binary for 0b, hexadecimal
2114
+ # for 0x, and decimal for others; with optional sign prefix. Converts to
2115
+ # Integer.
2116
+ #
2117
+ decimal = '\d+(?:_\d+)*'
2118
+ binary = 'b[01]+(?:_[01]+)*'
2119
+ hex = 'x[\da-f]+(?:_[\da-f]+)*'
2120
+ octal = "0(?:[0-7]+(?:_[0-7]+)*|#{binary}|#{hex})?"
2121
+ integer = "#{octal}|#{decimal}"
2122
+
2123
+ accept(Integer, %r"\A[-+]?(?:#{integer})\z"io) {|s,|
2124
+ begin
2125
+ Integer(s)
2126
+ rescue ArgumentError
2127
+ raise OptionParser::InvalidArgument, s
2128
+ end if s
2129
+ }
2130
+
2131
+ #
2132
+ # Float number format, and converts to Float.
2133
+ #
2134
+ float = "(?:#{decimal}(?=(.)?)(?:\\.(?:#{decimal})?)?|\\.#{decimal})(?:E[-+]?#{decimal})?"
2135
+ floatpat = %r"\A[-+]?#{float}\z"io
2136
+ accept(Float, floatpat) {|s,| s.to_f if s}
2137
+
2138
+ #
2139
+ # Generic numeric format, converts to Integer for integer format, Float
2140
+ # for float format, and Rational for rational format.
2141
+ #
2142
+ real = "[-+]?(?:#{octal}|#{float})"
2143
+ accept(Numeric, /\A(#{real})(?:\/(#{real}))?\z/io) {|s, d, f, n,|
2144
+ if n
2145
+ Rational(d, n)
2146
+ elsif f
2147
+ Float(s)
2148
+ else
2149
+ Integer(s)
2150
+ end
2151
+ }
2152
+
2153
+ #
2154
+ # Decimal integer format, to be converted to Integer.
2155
+ #
2156
+ DecimalInteger = /\A[-+]?#{decimal}\z/io
2157
+ accept(DecimalInteger, DecimalInteger) {|s,|
2158
+ begin
2159
+ Integer(s, 10)
2160
+ rescue ArgumentError
2161
+ raise OptionParser::InvalidArgument, s
2162
+ end if s
2163
+ }
2164
+
2165
+ #
2166
+ # Ruby/C like octal/hexadecimal/binary integer format, to be converted to
2167
+ # Integer.
2168
+ #
2169
+ OctalInteger = /\A[-+]?(?:[0-7]+(?:_[0-7]+)*|0(?:#{binary}|#{hex}))\z/io
2170
+ accept(OctalInteger, OctalInteger) {|s,|
2171
+ begin
2172
+ Integer(s, 8)
2173
+ rescue ArgumentError
2174
+ raise OptionParser::InvalidArgument, s
2175
+ end if s
2176
+ }
2177
+
2178
+ #
2179
+ # Decimal integer/float number format, to be converted to Integer for
2180
+ # integer format, Float for float format.
2181
+ #
2182
+ DecimalNumeric = floatpat # decimal integer is allowed as float also.
2183
+ accept(DecimalNumeric, floatpat) {|s, f|
2184
+ begin
2185
+ if f
2186
+ Float(s)
2187
+ else
2188
+ Integer(s)
2189
+ end
2190
+ rescue ArgumentError
2191
+ raise OptionParser::InvalidArgument, s
2192
+ end if s
2193
+ }
2194
+
2195
+ #
2196
+ # Boolean switch, which means whether it is present or not, whether it is
2197
+ # absent or not with prefix no-, or it takes an argument
2198
+ # yes/no/true/false/+/-.
2199
+ #
2200
+ yesno = CompletingHash.new
2201
+ %w[- no false].each {|el| yesno[el] = false}
2202
+ %w[+ yes true].each {|el| yesno[el] = true}
2203
+ yesno['nil'] = false # should be nil?
2204
+ accept(TrueClass, yesno) {|arg, val| val == nil or val}
2205
+ #
2206
+ # Similar to TrueClass, but defaults to false.
2207
+ #
2208
+ accept(FalseClass, yesno) {|arg, val| val != nil and val}
2209
+
2210
+ #
2211
+ # List of strings separated by ",".
2212
+ #
2213
+ accept(Array) do |s, |
2214
+ if s
2215
+ s = s.split(',').collect {|ss| ss unless ss.empty?}
2216
+ end
2217
+ s
2218
+ end
2219
+
2220
+ #
2221
+ # Regular expression with options.
2222
+ #
2223
+ accept(Regexp, %r"\A/((?:\\.|[^\\])*)/([[:alpha:]]+)?\z|.*") do |all, s, o|
2224
+ f = 0
2225
+ if o
2226
+ f |= Regexp::IGNORECASE if /i/ =~ o
2227
+ f |= Regexp::MULTILINE if /m/ =~ o
2228
+ f |= Regexp::EXTENDED if /x/ =~ o
2229
+ case o = o.delete("imx")
2230
+ when ""
2231
+ when "u"
2232
+ s = s.encode(Encoding::UTF_8)
2233
+ when "e"
2234
+ s = s.encode(Encoding::EUC_JP)
2235
+ when "s"
2236
+ s = s.encode(Encoding::SJIS)
2237
+ when "n"
2238
+ f |= Regexp::NOENCODING
2239
+ else
2240
+ raise OptionParser::InvalidArgument, "unknown regexp option - #{o}"
2241
+ end
2242
+ else
2243
+ s ||= all
2244
+ end
2245
+ Regexp.new(s, f)
2246
+ end
2247
+
2248
+ #
2249
+ # Exceptions
2250
+ #
2251
+
2252
+ #
2253
+ # Base class of exceptions from OptionParser.
2254
+ #
2255
+ class ParseError < RuntimeError
2256
+ # Reason which caused the error.
2257
+ Reason = 'parse error'
2258
+
2259
+ # :nodoc:
2260
+ def initialize(*args, additional: nil)
2261
+ @additional = additional
2262
+ @arg0, = args
2263
+ @args = args
2264
+ @reason = nil
2265
+ end
2266
+
2267
+ attr_reader :args
2268
+ attr_writer :reason
2269
+ attr_accessor :additional
2270
+
2271
+ #
2272
+ # Pushes back erred argument(s) to +argv+.
2273
+ #
2274
+ def recover(argv)
2275
+ argv[0, 0] = @args
2276
+ argv
2277
+ end
2278
+
2279
+ DIR = File.join(__dir__, '')
2280
+ def self.filter_backtrace(array)
2281
+ unless $DEBUG
2282
+ array.delete_if {|bt| bt.start_with?(DIR)}
2283
+ end
2284
+ array
2285
+ end
2286
+
2287
+ def set_backtrace(array)
2288
+ super(self.class.filter_backtrace(array))
2289
+ end
2290
+
2291
+ def set_option(opt, eq)
2292
+ if eq
2293
+ @args[0] = opt
2294
+ else
2295
+ @args.unshift(opt)
2296
+ end
2297
+ self
2298
+ end
2299
+
2300
+ #
2301
+ # Returns error reason. Override this for I18N.
2302
+ #
2303
+ def reason
2304
+ @reason || self.class::Reason
2305
+ end
2306
+
2307
+ def inspect
2308
+ "#<#{self.class}: #{args.join(' ')}>"
2309
+ end
2310
+
2311
+ #
2312
+ # Default stringizing method to emit standard error message.
2313
+ #
2314
+ def message
2315
+ "#{reason}: #{args.join(' ')}#{additional[@arg0] if additional}"
2316
+ end
2317
+
2318
+ alias to_s message
2319
+ end
2320
+
2321
+ #
2322
+ # Raises when ambiguously completable string is encountered.
2323
+ #
2324
+ class AmbiguousOption < ParseError
2325
+ const_set(:Reason, 'ambiguous option')
2326
+ end
2327
+
2328
+ #
2329
+ # Raises when there is an argument for a switch which takes no argument.
2330
+ #
2331
+ class NeedlessArgument < ParseError
2332
+ const_set(:Reason, 'needless argument')
2333
+ end
2334
+
2335
+ #
2336
+ # Raises when a switch with mandatory argument has no argument.
2337
+ #
2338
+ class MissingArgument < ParseError
2339
+ const_set(:Reason, 'missing argument')
2340
+ end
2341
+
2342
+ #
2343
+ # Raises when switch is undefined.
2344
+ #
2345
+ class InvalidOption < ParseError
2346
+ const_set(:Reason, 'invalid option')
2347
+ end
2348
+
2349
+ #
2350
+ # Raises when the given argument does not match required format.
2351
+ #
2352
+ class InvalidArgument < ParseError
2353
+ const_set(:Reason, 'invalid argument')
2354
+ end
2355
+
2356
+ #
2357
+ # Raises when the given argument word can't be completed uniquely.
2358
+ #
2359
+ class AmbiguousArgument < InvalidArgument
2360
+ const_set(:Reason, 'ambiguous argument')
2361
+ end
2362
+
2363
+ #
2364
+ # Miscellaneous
2365
+ #
2366
+
2367
+ #
2368
+ # Extends command line arguments array (ARGV) to parse itself.
2369
+ #
2370
+ module Arguable
2371
+
2372
+ #
2373
+ # Sets OptionParser object, when +opt+ is +false+ or +nil+, methods
2374
+ # OptionParser::Arguable#options and OptionParser::Arguable#options= are
2375
+ # undefined. Thus, there is no ways to access the OptionParser object
2376
+ # via the receiver object.
2377
+ #
2378
+ def options=(opt)
2379
+ unless @optparse = opt
2380
+ class << self
2381
+ undef_method(:options)
2382
+ undef_method(:options=)
2383
+ end
2384
+ end
2385
+ end
2386
+
2387
+ #
2388
+ # Actual OptionParser object, automatically created if nonexistent.
2389
+ #
2390
+ # If called with a block, yields the OptionParser object and returns the
2391
+ # result of the block. If an OptionParser::ParseError exception occurs
2392
+ # in the block, it is rescued, a error message printed to STDERR and
2393
+ # +nil+ returned.
2394
+ #
2395
+ def options
2396
+ @optparse ||= OptionParser.new
2397
+ @optparse.default_argv = self
2398
+ block_given? or return @optparse
2399
+ begin
2400
+ yield @optparse
2401
+ rescue ParseError
2402
+ @optparse.warn $!
2403
+ nil
2404
+ end
2405
+ end
2406
+
2407
+ #
2408
+ # Parses +self+ destructively in order and returns +self+ containing the
2409
+ # rest arguments left unparsed.
2410
+ #
2411
+ def order!(**keywords, &blk) options.order!(self, **keywords, &blk) end
2412
+
2413
+ #
2414
+ # Parses +self+ destructively in permutation mode and returns +self+
2415
+ # containing the rest arguments left unparsed.
2416
+ #
2417
+ def permute!(**keywords) options.permute!(self, **keywords) end
2418
+
2419
+ #
2420
+ # Parses +self+ destructively and returns +self+ containing the
2421
+ # rest arguments left unparsed.
2422
+ #
2423
+ def parse!(**keywords) options.parse!(self, **keywords) end
2424
+
2425
+ #
2426
+ # Substitution of getopts is possible as follows. Also see
2427
+ # OptionParser#getopts.
2428
+ #
2429
+ # def getopts(*args)
2430
+ # ($OPT = ARGV.getopts(*args)).each do |opt, val|
2431
+ # eval "$OPT_#{opt.gsub(/[^A-Za-z0-9_]/, '_')} = val"
2432
+ # end
2433
+ # rescue OptionParser::ParseError
2434
+ # end
2435
+ #
2436
+ def getopts(*args, symbolize_names: false, **keywords)
2437
+ options.getopts(self, *args, symbolize_names: symbolize_names, **keywords)
2438
+ end
2439
+
2440
+ #
2441
+ # Initializes instance variable.
2442
+ #
2443
+ def self.extend_object(obj)
2444
+ super
2445
+ obj.instance_eval {@optparse = nil}
2446
+ end
2447
+
2448
+ def initialize(*args) # :nodoc:
2449
+ super
2450
+ @optparse = nil
2451
+ end
2452
+ end
2453
+
2454
+ #
2455
+ # Acceptable argument classes. Now contains DecimalInteger, OctalInteger
2456
+ # and DecimalNumeric. See Acceptable argument classes (in source code).
2457
+ #
2458
+ module Acceptables
2459
+ const_set(:DecimalInteger, OptionParser::DecimalInteger)
2460
+ const_set(:OctalInteger, OptionParser::OctalInteger)
2461
+ const_set(:DecimalNumeric, OptionParser::DecimalNumeric)
2462
+ end
2463
+ end
2464
+
2465
+ # ARGV is arguable by OptionParser
2466
+ ARGV.extend(OptionParser::Arguable)
2467
+
2468
+ # An alias for OptionParser.
2469
+ OptParse = OptionParser # :nodoc: