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,236 @@
1
+ module CodeRay
2
+ module Scanners
3
+
4
+ # Scanner for JavaScript.
5
+ #
6
+ # Aliases: +ecmascript+, +ecma_script+, +javascript+
7
+ class JavaScript < Scanner
8
+
9
+ register_for :java_script
10
+ file_extension 'js'
11
+
12
+ # The actual JavaScript keywords.
13
+ KEYWORDS = %w[
14
+ break case catch continue default delete do else
15
+ finally for function if in instanceof new
16
+ return switch throw try typeof var void while with
17
+ ] # :nodoc:
18
+ PREDEFINED_CONSTANTS = %w[
19
+ false null true undefined NaN Infinity
20
+ ] # :nodoc:
21
+
22
+ MAGIC_VARIABLES = %w[ this arguments ] # :nodoc: arguments was introduced in JavaScript 1.4
23
+
24
+ KEYWORDS_EXPECTING_VALUE = WordList.new.add %w[
25
+ case delete in instanceof new return throw typeof with
26
+ ] # :nodoc:
27
+
28
+ # Reserved for future use.
29
+ RESERVED_WORDS = %w[
30
+ abstract boolean byte char class debugger double enum export extends
31
+ final float goto implements import int interface long native package
32
+ private protected public short static super synchronized throws transient
33
+ volatile
34
+ ] # :nodoc:
35
+
36
+ IDENT_KIND = WordList.new(:ident).
37
+ add(RESERVED_WORDS, :reserved).
38
+ add(PREDEFINED_CONSTANTS, :predefined_constant).
39
+ add(MAGIC_VARIABLES, :local_variable).
40
+ add(KEYWORDS, :keyword) # :nodoc:
41
+
42
+ ESCAPE = / [bfnrtv\n\\'"] | x[a-fA-F0-9]{1,2} | [0-7]{1,3} /x # :nodoc:
43
+ UNICODE_ESCAPE = / u[a-fA-F0-9]{4} | U[a-fA-F0-9]{8} /x # :nodoc:
44
+ REGEXP_ESCAPE = / [bBdDsSwW] /x # :nodoc:
45
+ STRING_CONTENT_PATTERN = {
46
+ "'" => /[^\\']+/,
47
+ '"' => /[^\\"]+/,
48
+ '/' => /[^\\\/]+/,
49
+ } # :nodoc:
50
+ KEY_CHECK_PATTERN = {
51
+ "'" => / (?> [^\\']* (?: \\. [^\\']* )* ) ' \s* : /mx,
52
+ '"' => / (?> [^\\"]* (?: \\. [^\\"]* )* ) " \s* : /mx,
53
+ } # :nodoc:
54
+
55
+ protected
56
+
57
+ def setup
58
+ @state = :initial
59
+ end
60
+
61
+ def scan_tokens encoder, options
62
+
63
+ state, string_delimiter = options[:state] || @state
64
+ if string_delimiter
65
+ encoder.begin_group state
66
+ end
67
+
68
+ value_expected = true
69
+ key_expected = false
70
+ function_expected = false
71
+
72
+ until eos?
73
+
74
+ case state
75
+
76
+ when :initial
77
+
78
+ if match = scan(/ \s+ | \\\n /x)
79
+ value_expected = true if !value_expected && match.index(?\n)
80
+ encoder.text_token match, :space
81
+
82
+ elsif match = scan(%r! // [^\n\\]* (?: \\. [^\n\\]* )* | /\* (?: .*? \*/ | .*() ) !mx)
83
+ value_expected = true
84
+ encoder.text_token match, :comment
85
+ state = :open_multi_line_comment if self[1]
86
+
87
+ elsif check(/\.?\d/)
88
+ key_expected = value_expected = false
89
+ if match = scan(/0[xX][0-9A-Fa-f]+/)
90
+ encoder.text_token match, :hex
91
+ elsif match = scan(/(?>0[0-7]+)(?![89.eEfF])/)
92
+ encoder.text_token match, :octal
93
+ elsif match = scan(/\d+[fF]|\d*\.\d+(?:[eE][+-]?\d+)?[fF]?|\d+[eE][+-]?\d+[fF]?/)
94
+ encoder.text_token match, :float
95
+ elsif match = scan(/\d+/)
96
+ encoder.text_token match, :integer
97
+ end
98
+
99
+ elsif value_expected && match = scan(/<([[:alpha:]]\w*) (?: [^\/>]*\/> | .*?<\/\1>)/xim)
100
+ # TODO: scan over nested tags
101
+ xml_scanner.tokenize match, :tokens => encoder
102
+ value_expected = false
103
+
104
+ elsif match = scan(/ [-+*=<>?:;,!&^|(\[{~%]+ | \.(?!\d) /x)
105
+ value_expected = true
106
+ last_operator = match[-1]
107
+ key_expected = (last_operator == ?{) || (last_operator == ?,)
108
+ function_expected = false
109
+ encoder.text_token match, :operator
110
+
111
+ elsif match = scan(/ [)\]}]+ /x)
112
+ function_expected = key_expected = value_expected = false
113
+ encoder.text_token match, :operator
114
+
115
+ elsif match = scan(/ [$a-zA-Z_][A-Za-z_0-9$]* /x)
116
+ kind = IDENT_KIND[match]
117
+ value_expected = (kind == :keyword) && KEYWORDS_EXPECTING_VALUE[match]
118
+ # TODO: labels
119
+ if kind == :ident
120
+ if match.index(?$) # $ allowed inside an identifier
121
+ kind = :predefined
122
+ elsif function_expected
123
+ kind = :function
124
+ elsif check(/\s*[=:]\s*function\b/)
125
+ kind = :function
126
+ elsif key_expected && check(/\s*:/)
127
+ kind = :key
128
+ end
129
+ end
130
+ function_expected = (kind == :keyword) && (match == 'function')
131
+ key_expected = false
132
+ encoder.text_token match, kind
133
+
134
+ elsif match = scan(/["']/)
135
+ if key_expected && check(KEY_CHECK_PATTERN[match])
136
+ state = :key
137
+ else
138
+ state = :string
139
+ end
140
+ encoder.begin_group state
141
+ string_delimiter = match
142
+ encoder.text_token match, :delimiter
143
+
144
+ elsif value_expected && (match = scan(/\//))
145
+ encoder.begin_group :regexp
146
+ state = :regexp
147
+ string_delimiter = '/'
148
+ encoder.text_token match, :delimiter
149
+
150
+ elsif match = scan(/ \/ /x)
151
+ value_expected = true
152
+ key_expected = false
153
+ encoder.text_token match, :operator
154
+
155
+ else
156
+ encoder.text_token getch, :error
157
+
158
+ end
159
+
160
+ when :string, :regexp, :key
161
+ if match = scan(STRING_CONTENT_PATTERN[string_delimiter])
162
+ encoder.text_token match, :content
163
+ elsif match = scan(/["'\/]/)
164
+ encoder.text_token match, :delimiter
165
+ if state == :regexp
166
+ modifiers = scan(/[gim]+/)
167
+ encoder.text_token modifiers, :modifier if modifiers && !modifiers.empty?
168
+ end
169
+ encoder.end_group state
170
+ string_delimiter = nil
171
+ key_expected = value_expected = false
172
+ state = :initial
173
+ elsif state != :regexp && (match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox))
174
+ if string_delimiter == "'" && !(match == "\\\\" || match == "\\'")
175
+ encoder.text_token match, :content
176
+ else
177
+ encoder.text_token match, :char
178
+ end
179
+ elsif state == :regexp && match = scan(/ \\ (?: #{ESCAPE} | #{REGEXP_ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
180
+ encoder.text_token match, :char
181
+ elsif match = scan(/\\./m)
182
+ encoder.text_token match, :content
183
+ elsif match = scan(/ \\ | $ /x)
184
+ encoder.end_group state
185
+ encoder.text_token match, :error unless match.empty?
186
+ string_delimiter = nil
187
+ key_expected = value_expected = false
188
+ state = :initial
189
+ else
190
+ raise_inspect "else case #{string_delimiter} reached; %p not handled." % peek(1), encoder
191
+ end
192
+
193
+ when :open_multi_line_comment
194
+ if match = scan(%r! .*? \*/ !mx)
195
+ state = :initial
196
+ else
197
+ match = scan(%r! .+ !mx)
198
+ end
199
+ value_expected = true
200
+ encoder.text_token match, :comment if match
201
+
202
+ else
203
+ #:nocov:
204
+ raise_inspect 'Unknown state: %p' % [state], encoder
205
+ #:nocov:
206
+
207
+ end
208
+
209
+ end
210
+
211
+ if options[:keep_state]
212
+ @state = state, string_delimiter
213
+ end
214
+
215
+ if [:string, :regexp].include? state
216
+ encoder.end_group state
217
+ end
218
+
219
+ encoder
220
+ end
221
+
222
+ protected
223
+
224
+ def reset_instance
225
+ super
226
+ @xml_scanner.reset if defined? @xml_scanner
227
+ end
228
+
229
+ def xml_scanner
230
+ @xml_scanner ||= CodeRay.scanner :xml, :tokens => @tokens, :keep_tokens => true, :keep_state => false
231
+ end
232
+
233
+ end
234
+
235
+ end
236
+ end
@@ -0,0 +1,98 @@
1
+ module CodeRay
2
+ module Scanners
3
+
4
+ # Scanner for JSON (JavaScript Object Notation).
5
+ class JSON < Scanner
6
+
7
+ register_for :json
8
+ file_extension 'json'
9
+
10
+ KINDS_NOT_LOC = [
11
+ :float, :char, :content, :delimiter,
12
+ :error, :integer, :operator, :value,
13
+ ] # :nodoc:
14
+
15
+ ESCAPE = / [bfnrt\\"\/] /x # :nodoc:
16
+ UNICODE_ESCAPE = / u[a-fA-F0-9]{4} /x # :nodoc:
17
+ KEY = / (?> (?: [^\\"]+ | \\. )* ) " \s* : /x
18
+
19
+ protected
20
+
21
+ def setup
22
+ @state = :initial
23
+ end
24
+
25
+ # See http://json.org/ for a definition of the JSON lexic/grammar.
26
+ def scan_tokens encoder, options
27
+ state = options[:state] || @state
28
+
29
+ if [:string, :key].include? state
30
+ encoder.begin_group state
31
+ end
32
+
33
+ until eos?
34
+
35
+ case state
36
+
37
+ when :initial
38
+ if match = scan(/ \s+ /x)
39
+ encoder.text_token match, :space
40
+ elsif match = scan(/"/)
41
+ state = check(/#{KEY}/o) ? :key : :string
42
+ encoder.begin_group state
43
+ encoder.text_token match, :delimiter
44
+ elsif match = scan(/ [:,\[{\]}] /x)
45
+ encoder.text_token match, :operator
46
+ elsif match = scan(/ true | false | null /x)
47
+ encoder.text_token match, :value
48
+ elsif match = scan(/ -? (?: 0 | [1-9]\d* ) /x)
49
+ if scan(/ \.\d+ (?:[eE][-+]?\d+)? | [eE][-+]? \d+ /x)
50
+ match << matched
51
+ encoder.text_token match, :float
52
+ else
53
+ encoder.text_token match, :integer
54
+ end
55
+ else
56
+ encoder.text_token getch, :error
57
+ end
58
+
59
+ when :string, :key
60
+ if match = scan(/[^\\"]+/)
61
+ encoder.text_token match, :content
62
+ elsif match = scan(/"/)
63
+ encoder.text_token match, :delimiter
64
+ encoder.end_group state
65
+ state = :initial
66
+ elsif match = scan(/ \\ (?: #{ESCAPE} | #{UNICODE_ESCAPE} ) /mox)
67
+ encoder.text_token match, :char
68
+ elsif match = scan(/\\./m)
69
+ encoder.text_token match, :content
70
+ elsif match = scan(/ \\ | $ /x)
71
+ encoder.end_group state
72
+ encoder.text_token match, :error unless match.empty?
73
+ state = :initial
74
+ else
75
+ raise_inspect "else case \" reached; %p not handled." % peek(1), encoder
76
+ end
77
+
78
+ else
79
+ raise_inspect 'Unknown state: %p' % [state], encoder
80
+
81
+ end
82
+ end
83
+
84
+ if options[:keep_state]
85
+ @state = state
86
+ end
87
+
88
+ if [:string, :key].include? state
89
+ encoder.end_group state
90
+ end
91
+
92
+ encoder
93
+ end
94
+
95
+ end
96
+
97
+ end
98
+ end
@@ -0,0 +1,280 @@
1
+ # encoding: utf-8
2
+
3
+ module CodeRay
4
+ module Scanners
5
+
6
+ # Scanner for the Lua[http://lua.org] programming lanuage.
7
+ #
8
+ # The language’s complete syntax is defined in
9
+ # {the Lua manual}[http://www.lua.org/manual/5.2/manual.html],
10
+ # which is what this scanner tries to conform to.
11
+ class Lua < Scanner
12
+
13
+ register_for :lua
14
+ file_extension 'lua'
15
+ title 'Lua'
16
+
17
+ # Keywords used in Lua.
18
+ KEYWORDS = %w[and break do else elseif end
19
+ for function goto if in
20
+ local not or repeat return
21
+ then until while
22
+ ]
23
+
24
+ # Constants set by the Lua core.
25
+ PREDEFINED_CONSTANTS = %w[false true nil]
26
+
27
+ # The expressions contained in this array are parts of Lua’s `basic'
28
+ # library. Although it’s not entirely necessary to load that library,
29
+ # it is highly recommended and one would have to provide own implementations
30
+ # of some of these expressions if one does not do so. They however aren’t
31
+ # keywords, neither are they constants, but nearly predefined, so they
32
+ # get tagged as `predefined' rather than anything else.
33
+ #
34
+ # This list excludes values of form `_UPPERCASE' because the Lua manual
35
+ # requires such identifiers to be reserved by Lua anyway and they are
36
+ # highlighted directly accordingly, without the need for specific
37
+ # identifiers to be listed here.
38
+ PREDEFINED_EXPRESSIONS = %w[
39
+ assert collectgarbage dofile error getmetatable
40
+ ipairs load loadfile next pairs pcall print
41
+ rawequal rawget rawlen rawset select setmetatable
42
+ tonumber tostring type xpcall
43
+ ]
44
+
45
+ # Automatic token kind selection for normal words.
46
+ IDENT_KIND = CodeRay::WordList.new(:ident).
47
+ add(KEYWORDS, :keyword).
48
+ add(PREDEFINED_CONSTANTS, :predefined_constant).
49
+ add(PREDEFINED_EXPRESSIONS, :predefined)
50
+
51
+ protected
52
+
53
+ # Scanner initialization.
54
+ def setup
55
+ @state = :initial
56
+ @brace_depth = 0
57
+ end
58
+
59
+ # CodeRay entry hook. Starts parsing.
60
+ def scan_tokens(encoder, options)
61
+ state = options[:state] || @state
62
+ brace_depth = @brace_depth
63
+ num_equals = nil
64
+
65
+ until eos?
66
+ case state
67
+
68
+ when :initial
69
+ if match = scan(/\-\-\[\=*\[/) #--[[ long (possibly multiline) comment ]]
70
+ num_equals = match.count("=") # Number must match for comment end
71
+ encoder.begin_group(:comment)
72
+ encoder.text_token(match, :delimiter)
73
+ state = :long_comment
74
+
75
+ elsif match = scan(/--.*$/) # --Lua comment
76
+ encoder.text_token(match, :comment)
77
+
78
+ elsif match = scan(/\[=*\[/) # [[ long (possibly multiline) string ]]
79
+ num_equals = match.count("=") # Number must match for string end
80
+ encoder.begin_group(:string)
81
+ encoder.text_token(match, :delimiter)
82
+ state = :long_string
83
+
84
+ elsif match = scan(/::\s*[a-zA-Z_][a-zA-Z0-9_]+\s*::/) # ::goto_label::
85
+ encoder.text_token(match, :label)
86
+
87
+ elsif match = scan(/_[A-Z]+/) # _UPPERCASE are names reserved for Lua
88
+ encoder.text_token(match, :predefined)
89
+
90
+ elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/) # Normal letters (or letters followed by digits)
91
+ kind = IDENT_KIND[match]
92
+
93
+ # Extra highlighting for entities following certain keywords
94
+ if kind == :keyword and match == "function"
95
+ state = :function_expected
96
+ elsif kind == :keyword and match == "goto"
97
+ state = :goto_label_expected
98
+ elsif kind == :keyword and match == "local"
99
+ state = :local_var_expected
100
+ end
101
+
102
+ encoder.text_token(match, kind)
103
+
104
+ elsif match = scan(/\{/) # Opening table brace {
105
+ encoder.begin_group(:map)
106
+ encoder.text_token(match, brace_depth >= 1 ? :inline_delimiter : :delimiter)
107
+ brace_depth += 1
108
+ state = :map
109
+
110
+ elsif match = scan(/\}/) # Closing table brace }
111
+ if brace_depth == 1
112
+ brace_depth = 0
113
+ encoder.text_token(match, :delimiter)
114
+ encoder.end_group(:map)
115
+ elsif brace_depth == 0 # Mismatched brace
116
+ encoder.text_token(match, :error)
117
+ else
118
+ brace_depth -= 1
119
+ encoder.text_token(match, :inline_delimiter)
120
+ encoder.end_group(:map)
121
+ state = :map
122
+ end
123
+
124
+ elsif match = scan(/["']/) # String delimiters " and '
125
+ encoder.begin_group(:string)
126
+ encoder.text_token(match, :delimiter)
127
+ start_delim = match
128
+ state = :string
129
+
130
+ # ↓Prefix hex number ←|→ decimal number
131
+ elsif match = scan(/-? (?:0x\h* \. \h+ (?:p[+\-]?\d+)? | \d*\.\d+ (?:e[+\-]?\d+)?)/ix) # hexadecimal constants have no E power, decimal ones no P power
132
+ encoder.text_token(match, :float)
133
+
134
+ # ↓Prefix hex number ←|→ decimal number
135
+ elsif match = scan(/-? (?:0x\h+ (?:p[+\-]?\d+)? | \d+ (?:e[+\-]?\d+)?)/ix) # hexadecimal constants have no E power, decimal ones no P power
136
+ encoder.text_token(match, :integer)
137
+
138
+ elsif match = scan(/[\+\-\*\/%^\#=~<>\(\)\[\]:;,] | \.(?!\d)/x) # Operators
139
+ encoder.text_token(match, :operator)
140
+
141
+ elsif match = scan(/\s+/) # Space
142
+ encoder.text_token(match, :space)
143
+
144
+ else # Invalid stuff. Note that Lua doesn’t accept multibyte chars outside of strings, hence these are also errors.
145
+ encoder.text_token(getch, :error)
146
+ end
147
+
148
+ # It may be that we’re scanning a full-blown subexpression of a table
149
+ # (tables can contain full expressions in parts).
150
+ # If this is the case, return to :map scanning state.
151
+ state = :map if state == :initial && brace_depth >= 1
152
+
153
+ when :function_expected
154
+ if match = scan(/\(.*?\)/m) # x = function() # "Anonymous" function without explicit name
155
+ encoder.text_token(match, :operator)
156
+ state = :initial
157
+ elsif match = scan(/[a-zA-Z_] (?:[a-zA-Z0-9_\.] (?!\.\d))* [\.\:]/x) # function tbl.subtbl.foo() | function tbl:foo() # Colon only allowed as last separator
158
+ encoder.text_token(match, :ident)
159
+ elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/) # function foo()
160
+ encoder.text_token(match, :function)
161
+ state = :initial
162
+ elsif match = scan(/\s+/) # Between the `function' keyword and the ident may be any amount of whitespace
163
+ encoder.text_token(match, :space)
164
+ else
165
+ encoder.text_token(getch, :error)
166
+ state = :initial
167
+ end
168
+
169
+ when :goto_label_expected
170
+ if match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/)
171
+ encoder.text_token(match, :label)
172
+ state = :initial
173
+ elsif match = scan(/\s+/) # Between the `goto' keyword and the label may be any amount of whitespace
174
+ encoder.text_token(match, :space)
175
+ else
176
+ encoder.text_token(getch, :error)
177
+ end
178
+
179
+ when :local_var_expected
180
+ if match = scan(/function/) # local function ...
181
+ encoder.text_token(match, :keyword)
182
+ state = :function_expected
183
+ elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]*/)
184
+ encoder.text_token(match, :local_variable)
185
+ elsif match = scan(/,/)
186
+ encoder.text_token(match, :operator)
187
+ elsif match = scan(/\=/)
188
+ encoder.text_token(match, :operator)
189
+ # After encountering the equal sign, arbitrary expressions are
190
+ # allowed again, so just return to the main state for further
191
+ # parsing.
192
+ state = :initial
193
+ elsif match = scan(/\n/)
194
+ encoder.text_token(match, :space)
195
+ state = :initial
196
+ elsif match = scan(/\s+/)
197
+ encoder.text_token(match, :space)
198
+ else
199
+ encoder.text_token(getch, :error)
200
+ end
201
+
202
+ when :long_comment
203
+ if match = scan(/.*?(?=\]={#{num_equals}}\])/m)
204
+ encoder.text_token(match, :content)
205
+
206
+ delim = scan(/\]={#{num_equals}}\]/)
207
+ encoder.text_token(delim, :delimiter)
208
+ else # No terminator found till EOF
209
+ encoder.text_token(rest, :error)
210
+ terminate
211
+ end
212
+ encoder.end_group(:comment)
213
+ state = :initial
214
+
215
+ when :long_string
216
+ if match = scan(/.*?(?=\]={#{num_equals}}\])/m) # Long strings do not interpret any escape sequences
217
+ encoder.text_token(match, :content)
218
+
219
+ delim = scan(/\]={#{num_equals}}\]/)
220
+ encoder.text_token(delim, :delimiter)
221
+ else # No terminator found till EOF
222
+ encoder.text_token(rest, :error)
223
+ terminate
224
+ end
225
+ encoder.end_group(:string)
226
+ state = :initial
227
+
228
+ when :string
229
+ if match = scan(/[^\\#{start_delim}\n]+/) # Everything except \ and the start delimiter character is string content (newlines are only allowed if preceeded by \ or \z)
230
+ encoder.text_token(match, :content)
231
+ elsif match = scan(/\\(?:['"abfnrtv\\]|z\s*|x\h\h|\d{1,3}|\n)/m)
232
+ encoder.text_token(match, :char)
233
+ elsif match = scan(Regexp.compile(start_delim))
234
+ encoder.text_token(match, :delimiter)
235
+ encoder.end_group(:string)
236
+ state = :initial
237
+ elsif match = scan(/\n/) # Lua forbids unescaped newlines in normal non-long strings
238
+ encoder.text_token("\\n\n", :error) # Visually appealing error indicator--otherwise users may wonder whether the highlighter cannot highlight multine strings
239
+ encoder.end_group(:string)
240
+ state = :initial
241
+ else
242
+ encoder.text_token(getch, :error)
243
+ end
244
+
245
+ when :map
246
+ if match = scan(/[,;]/)
247
+ encoder.text_token(match, :operator)
248
+ elsif match = scan(/[a-zA-Z_][a-zA-Z0-9_]* (?=\s*=)/x)
249
+ encoder.text_token(match, :key)
250
+ encoder.text_token(scan(/\s+/), :space) if check(/\s+/)
251
+ encoder.text_token(scan(/\=/), :operator)
252
+ state = :initial
253
+ elsif match = scan(/\s+/m)
254
+ encoder.text_token(match, :space)
255
+ else
256
+ # Note this clause doesn’t advance the scan pointer, it’s a kind of
257
+ # "retry with other options" (the :initial state then of course
258
+ # advances the pointer).
259
+ state = :initial
260
+ end
261
+ else
262
+ raise
263
+ end
264
+
265
+ end
266
+
267
+ if options[:keep_state]
268
+ @state = state
269
+ end
270
+
271
+ encoder.end_group :string if [:string].include? state
272
+ brace_depth.times { encoder.end_group :map }
273
+
274
+ encoder
275
+ end
276
+
277
+ end
278
+
279
+ end
280
+ end