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.
- checksums.yaml +4 -4
- data/README.md +61 -66
- data/lib/llm/shell/command.rb +40 -40
- data/lib/llm/shell/commands/clear_screen.rb +4 -18
- data/lib/llm/shell/commands/debug_mode.rb +12 -0
- data/lib/llm/shell/commands/dir_import.rb +4 -20
- data/lib/llm/shell/commands/disable_tool.rb +33 -0
- data/lib/llm/shell/commands/enable_tool.rb +33 -0
- data/lib/llm/shell/commands/file_import.rb +4 -20
- data/lib/llm/shell/commands/help.rb +23 -36
- data/lib/llm/shell/commands/show_chat.rb +4 -19
- data/lib/llm/shell/commands/show_version.rb +4 -20
- data/lib/llm/shell/commands/system_prompt.rb +4 -18
- data/lib/llm/shell/completion.rb +5 -5
- data/lib/llm/shell/config.rb +4 -5
- data/lib/llm/shell/formatter.rb +1 -2
- data/lib/llm/shell/internal/coderay/lib/coderay/duo.rb +81 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/_map.rb +17 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/comment_filter.rb +25 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/count.rb +39 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/debug.rb +49 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/debug_lint.rb +63 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/div.rb +23 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/encoder.rb +190 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/filter.rb +58 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/html/css.rb +65 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/html/numbering.rb +108 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/html/output.rb +164 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/html.rb +333 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/json.rb +83 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/lines_of_code.rb +45 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/lint.rb +59 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/null.rb +18 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/page.rb +24 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/span.rb +23 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/statistic.rb +95 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/terminal.rb +195 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/text.rb +46 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/token_kind_filter.rb +111 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/xml.rb +72 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders/yaml.rb +50 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/encoders.rb +18 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/for_redcloth.rb +95 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/helpers/file_type.rb +151 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/helpers/plugin.rb +55 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/helpers/plugin_host.rb +221 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/helpers/word_list.rb +72 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/_map.rb +24 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/c.rb +189 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/clojure.rb +217 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/cpp.rb +217 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/css.rb +196 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/debug.rb +75 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/delphi.rb +144 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/diff.rb +221 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/erb.rb +81 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/go.rb +208 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/groovy.rb +268 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/haml.rb +168 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/html.rb +275 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/java/builtin_types.rb +421 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/java.rb +174 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/java_script.rb +236 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/json.rb +98 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/lua.rb +280 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/php.rb +527 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/python.rb +287 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/raydebug.rb +75 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/ruby/patterns.rb +178 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/ruby/string_state.rb +79 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/ruby.rb +477 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/sass.rb +232 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/scanner.rb +337 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/sql.rb +169 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/taskpaper.rb +36 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/text.rb +26 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/xml.rb +17 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners/yaml.rb +140 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/scanners.rb +27 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/styles/_map.rb +7 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/styles/alpha.rb +153 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/styles/style.rb +18 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/styles.rb +15 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/token_kinds.rb +85 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/tokens.rb +164 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/tokens_proxy.rb +55 -0
- data/lib/llm/shell/internal/coderay/lib/coderay/version.rb +3 -0
- data/lib/llm/shell/internal/coderay/lib/coderay.rb +284 -0
- data/lib/llm/shell/internal/io-line/lib/io/line/multiple.rb +19 -0
- data/lib/{io → llm/shell/internal/io-line/lib/io}/line.rb +2 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/bot/builder.rb +31 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/bot/conversable.rb +37 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/bot/prompt/completion.rb +49 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/bot/prompt/respond.rb +49 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/bot.rb +150 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/buffer.rb +162 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/client.rb +36 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/error.rb +49 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/eventhandler.rb +44 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/eventstream/event.rb +69 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/eventstream/parser.rb +88 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/eventstream.rb +8 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/file.rb +91 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/function.rb +177 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/message.rb +178 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/mime.rb +140 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/multipart.rb +101 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/object/builder.rb +38 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/object/kernel.rb +53 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/object.rb +89 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/provider.rb +352 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/error_handler.rb +36 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/files.rb +155 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/format/completion_format.rb +88 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/format.rb +29 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/models.rb +54 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/response/completion.rb +39 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/response/enumerable.rb +11 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/response/file.rb +23 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/response/web_search.rb +21 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic/stream_parser.rb +66 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/anthropic.rb +138 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/deepseek/format/completion_format.rb +68 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/deepseek/format.rb +27 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/deepseek.rb +75 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/audio.rb +73 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/error_handler.rb +47 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/files.rb +146 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/format/completion_format.rb +69 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/format.rb +39 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/images.rb +133 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/models.rb +60 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/response/completion.rb +35 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/response/embedding.rb +8 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/response/file.rb +11 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/response/files.rb +15 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/response/image.rb +31 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/response/models.rb +15 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/response/web_search.rb +22 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini/stream_parser.rb +86 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/gemini.rb +173 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/llamacpp.rb +74 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/ollama/error_handler.rb +36 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/ollama/format/completion_format.rb +77 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/ollama/format.rb +29 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/ollama/models.rb +56 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/ollama/response/completion.rb +28 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/ollama/response/embedding.rb +9 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/ollama/stream_parser.rb +44 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/ollama.rb +116 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/audio.rb +91 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/error_handler.rb +46 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/files.rb +134 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/format/completion_format.rb +90 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/format/moderation_format.rb +35 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/format/respond_format.rb +72 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/format.rb +54 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/images.rb +109 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/models.rb +55 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/moderations.rb +65 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/response/audio.rb +7 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/response/completion.rb +40 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/response/embedding.rb +9 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/response/enumerable.rb +23 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/response/file.rb +7 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/response/image.rb +16 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/response/moderations.rb +34 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/response/responds.rb +48 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/response/web_search.rb +21 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/responses/stream_parser.rb +76 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/responses.rb +99 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/stream_parser.rb +86 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai/vector_stores.rb +228 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/openai.rb +206 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/xai/images.rb +58 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/xai.rb +72 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/providers/zai.rb +74 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/response.rb +67 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/schema/array.rb +26 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/schema/boolean.rb +13 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/schema/integer.rb +43 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/schema/leaf.rb +78 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/schema/null.rb +13 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/schema/number.rb +43 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/schema/object.rb +41 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/schema/string.rb +34 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/schema/version.rb +8 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/schema.rb +81 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/server_tool.rb +32 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/tool/param.rb +75 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/tool.rb +78 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/utils.rb +19 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm/version.rb +5 -0
- data/lib/llm/shell/internal/llm.rb/lib/llm.rb +121 -0
- data/lib/llm/shell/internal/optparse/lib/optionparser.rb +2 -0
- data/lib/llm/shell/internal/optparse/lib/optparse/ac.rb +70 -0
- data/lib/llm/shell/internal/optparse/lib/optparse/date.rb +18 -0
- data/lib/llm/shell/internal/optparse/lib/optparse/kwargs.rb +27 -0
- data/lib/llm/shell/internal/optparse/lib/optparse/shellwords.rb +7 -0
- data/lib/llm/shell/internal/optparse/lib/optparse/time.rb +11 -0
- data/lib/llm/shell/internal/optparse/lib/optparse/uri.rb +7 -0
- data/lib/llm/shell/internal/optparse/lib/optparse/version.rb +80 -0
- data/lib/llm/shell/internal/optparse/lib/optparse.rb +2469 -0
- data/lib/llm/shell/internal/paint/lib/paint/constants.rb +104 -0
- data/lib/llm/shell/internal/paint/lib/paint/pa.rb +13 -0
- data/lib/llm/shell/internal/paint/lib/paint/rgb_colors.rb +14 -0
- data/lib/llm/shell/internal/paint/lib/paint/shortcuts.rb +100 -0
- data/lib/llm/shell/internal/paint/lib/paint/shortcuts_version.rb +5 -0
- data/lib/llm/shell/internal/paint/lib/paint/util.rb +16 -0
- data/lib/llm/shell/internal/paint/lib/paint/version.rb +5 -0
- data/lib/llm/shell/internal/paint/lib/paint.rb +261 -0
- data/lib/llm/shell/internal/reline/lib/reline/config.rb +378 -0
- data/lib/llm/shell/internal/reline/lib/reline/face.rb +199 -0
- data/lib/llm/shell/internal/reline/lib/reline/history.rb +76 -0
- data/lib/llm/shell/internal/reline/lib/reline/io/ansi.rb +322 -0
- data/lib/llm/shell/internal/reline/lib/reline/io/dumb.rb +120 -0
- data/lib/llm/shell/internal/reline/lib/reline/io/windows.rb +530 -0
- data/lib/llm/shell/internal/reline/lib/reline/io.rb +55 -0
- data/lib/llm/shell/internal/reline/lib/reline/key_actor/base.rb +37 -0
- data/lib/llm/shell/internal/reline/lib/reline/key_actor/composite.rb +17 -0
- data/lib/llm/shell/internal/reline/lib/reline/key_actor/emacs.rb +517 -0
- data/lib/llm/shell/internal/reline/lib/reline/key_actor/vi_command.rb +518 -0
- data/lib/llm/shell/internal/reline/lib/reline/key_actor/vi_insert.rb +517 -0
- data/lib/llm/shell/internal/reline/lib/reline/key_actor.rb +8 -0
- data/lib/llm/shell/internal/reline/lib/reline/key_stroke.rb +119 -0
- data/lib/llm/shell/internal/reline/lib/reline/kill_ring.rb +125 -0
- data/lib/llm/shell/internal/reline/lib/reline/line_editor.rb +2356 -0
- data/lib/llm/shell/internal/reline/lib/reline/unicode/east_asian_width.rb +1292 -0
- data/lib/llm/shell/internal/reline/lib/reline/unicode.rb +421 -0
- data/lib/llm/shell/internal/reline/lib/reline/version.rb +3 -0
- data/lib/llm/shell/internal/reline/lib/reline.rb +527 -0
- data/lib/llm/shell/internal/tomlrb/lib/tomlrb/generated_parser.rb +712 -0
- data/lib/llm/shell/internal/tomlrb/lib/tomlrb/handler.rb +268 -0
- data/lib/llm/shell/internal/tomlrb/lib/tomlrb/local_date.rb +35 -0
- data/lib/llm/shell/internal/tomlrb/lib/tomlrb/local_date_time.rb +42 -0
- data/lib/llm/shell/internal/tomlrb/lib/tomlrb/local_time.rb +40 -0
- data/lib/llm/shell/internal/tomlrb/lib/tomlrb/parser.rb +21 -0
- data/lib/llm/shell/internal/tomlrb/lib/tomlrb/scanner.rb +92 -0
- data/lib/llm/shell/internal/tomlrb/lib/tomlrb/string_utils.rb +40 -0
- data/lib/llm/shell/internal/tomlrb/lib/tomlrb/version.rb +5 -0
- data/lib/llm/shell/internal/tomlrb/lib/tomlrb.rb +49 -0
- data/lib/llm/shell/options.rb +1 -1
- data/lib/llm/shell/renderer.rb +2 -3
- data/lib/llm/shell/repl.rb +21 -16
- data/lib/llm/shell/tool.rb +42 -0
- data/lib/llm/shell/tools/read_file.rb +15 -0
- data/lib/llm/shell/tools/system.rb +17 -0
- data/lib/llm/shell/tools/write_file.rb +16 -0
- data/lib/llm/shell/version.rb +1 -1
- data/lib/llm/shell.rb +83 -39
- data/libexec/llm-shell/shell +4 -6
- data/llm-shell.gemspec +0 -4
- metadata +233 -63
- data/lib/llm/function.rb +0 -17
- data/lib/llm/shell/command/extension.rb +0 -42
- data/lib/llm/shell/commands/utils.rb +0 -21
- data/lib/llm/shell/functions/read_file.rb +0 -22
- data/lib/llm/shell/functions/write_file.rb +0 -22
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
module CodeRay
|
|
2
|
+
module Scanners
|
|
3
|
+
|
|
4
|
+
# This scanner is really complex, since Ruby _is_ a complex language!
|
|
5
|
+
#
|
|
6
|
+
# It tries to highlight 100% of all common code,
|
|
7
|
+
# and 90% of strange codes.
|
|
8
|
+
#
|
|
9
|
+
# It is optimized for HTML highlighting, and is not very useful for
|
|
10
|
+
# parsing or pretty printing.
|
|
11
|
+
class Ruby < Scanner
|
|
12
|
+
|
|
13
|
+
register_for :ruby
|
|
14
|
+
file_extension 'rb'
|
|
15
|
+
|
|
16
|
+
autoload :Patterns, CodeRay.coderay_path('scanners', 'ruby', 'patterns')
|
|
17
|
+
autoload :StringState, CodeRay.coderay_path('scanners', 'ruby', 'string_state')
|
|
18
|
+
|
|
19
|
+
def interpreted_string_state
|
|
20
|
+
StringState.new :string, true, '"'
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
protected
|
|
24
|
+
|
|
25
|
+
def setup
|
|
26
|
+
@state = :initial
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def scan_tokens encoder, options
|
|
30
|
+
state, heredocs = options[:state] || @state
|
|
31
|
+
heredocs = heredocs.dup if heredocs.is_a?(Array)
|
|
32
|
+
|
|
33
|
+
if state && state.instance_of?(StringState)
|
|
34
|
+
encoder.begin_group state.type
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
last_state = nil
|
|
38
|
+
|
|
39
|
+
method_call_expected = false
|
|
40
|
+
value_expected = true
|
|
41
|
+
|
|
42
|
+
inline_block_stack = nil
|
|
43
|
+
inline_block_curly_depth = 0
|
|
44
|
+
|
|
45
|
+
if heredocs
|
|
46
|
+
state = heredocs.shift
|
|
47
|
+
encoder.begin_group state.type
|
|
48
|
+
heredocs = nil if heredocs.empty?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# def_object_stack = nil
|
|
52
|
+
# def_object_paren_depth = 0
|
|
53
|
+
|
|
54
|
+
patterns = Patterns # avoid constant lookup
|
|
55
|
+
|
|
56
|
+
unicode = string.respond_to?(:encoding) && string.encoding.name == 'UTF-8'
|
|
57
|
+
|
|
58
|
+
until eos?
|
|
59
|
+
|
|
60
|
+
if state.instance_of? ::Symbol
|
|
61
|
+
|
|
62
|
+
if match = scan(/[ \t\f\v]+/)
|
|
63
|
+
encoder.text_token match, :space
|
|
64
|
+
|
|
65
|
+
elsif match = scan(/\n/)
|
|
66
|
+
if heredocs
|
|
67
|
+
unscan # heredoc scanning needs \n at start
|
|
68
|
+
state = heredocs.shift
|
|
69
|
+
encoder.begin_group state.type
|
|
70
|
+
heredocs = nil if heredocs.empty?
|
|
71
|
+
else
|
|
72
|
+
state = :initial if state == :undef_comma_expected
|
|
73
|
+
encoder.text_token match, :space
|
|
74
|
+
value_expected = true
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
elsif match = scan(bol? ? / \#(!)?.* | #{patterns::RUBYDOC_OR_DATA} /ox : /\#.*/)
|
|
78
|
+
encoder.text_token match, self[1] ? :doctype : :comment
|
|
79
|
+
|
|
80
|
+
elsif match = scan(/\\\n/)
|
|
81
|
+
if heredocs
|
|
82
|
+
unscan # heredoc scanning needs \n at start
|
|
83
|
+
encoder.text_token scan(/\\/), :space
|
|
84
|
+
state = heredocs.shift
|
|
85
|
+
encoder.begin_group state.type
|
|
86
|
+
heredocs = nil if heredocs.empty?
|
|
87
|
+
else
|
|
88
|
+
encoder.text_token match, :space
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
elsif state == :initial
|
|
92
|
+
|
|
93
|
+
# IDENTS #
|
|
94
|
+
if !method_call_expected &&
|
|
95
|
+
match = scan(unicode ? /#{patterns::METHOD_NAME}/uo :
|
|
96
|
+
/#{patterns::METHOD_NAME}/o)
|
|
97
|
+
|
|
98
|
+
kind = patterns::IDENT_KIND[match]
|
|
99
|
+
if value_expected != :colon_expected && scan(/:(?!:)/)
|
|
100
|
+
value_expected = true
|
|
101
|
+
encoder.text_token match, :key
|
|
102
|
+
encoder.text_token ':', :operator
|
|
103
|
+
else
|
|
104
|
+
value_expected = false
|
|
105
|
+
if kind == :ident
|
|
106
|
+
if match[/\A[A-Z]/] && !(match[/[!?]$/] || match?(/\(/))
|
|
107
|
+
kind = :constant
|
|
108
|
+
end
|
|
109
|
+
elsif kind == :keyword
|
|
110
|
+
state = patterns::KEYWORD_NEW_STATE[match]
|
|
111
|
+
if patterns::KEYWORDS_EXPECTING_VALUE[match]
|
|
112
|
+
value_expected = match == 'when' ? :colon_expected : true
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
value_expected = true if !value_expected && check(/#{patterns::VALUE_FOLLOWS}/o)
|
|
116
|
+
encoder.text_token match, kind
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
elsif method_call_expected &&
|
|
120
|
+
match = scan(unicode ? /#{patterns::METHOD_AFTER_DOT}/uo :
|
|
121
|
+
/#{patterns::METHOD_AFTER_DOT}/o)
|
|
122
|
+
if method_call_expected == '::' && match[/\A[A-Z]/] && !match?(/\(/)
|
|
123
|
+
encoder.text_token match, :constant
|
|
124
|
+
else
|
|
125
|
+
encoder.text_token match, :ident
|
|
126
|
+
end
|
|
127
|
+
method_call_expected = false
|
|
128
|
+
value_expected = check(/#{patterns::VALUE_FOLLOWS}/o)
|
|
129
|
+
|
|
130
|
+
# OPERATORS #
|
|
131
|
+
elsif !method_call_expected && match = scan(/ (\.(?!\.)|::) | ( \.\.\.? | ==?=? | [,\(\[\{] ) | [\)\]\}] /x)
|
|
132
|
+
method_call_expected = self[1]
|
|
133
|
+
value_expected = !method_call_expected && !!self[2]
|
|
134
|
+
if inline_block_stack
|
|
135
|
+
case match
|
|
136
|
+
when '{'
|
|
137
|
+
inline_block_curly_depth += 1
|
|
138
|
+
when '}'
|
|
139
|
+
inline_block_curly_depth -= 1
|
|
140
|
+
if inline_block_curly_depth == 0 # closing brace of inline block reached
|
|
141
|
+
state, inline_block_curly_depth, heredocs = inline_block_stack.pop
|
|
142
|
+
inline_block_stack = nil if inline_block_stack.empty?
|
|
143
|
+
heredocs = nil if heredocs && heredocs.empty?
|
|
144
|
+
encoder.text_token match, :inline_delimiter
|
|
145
|
+
encoder.end_group :inline
|
|
146
|
+
next
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
encoder.text_token match, :operator
|
|
151
|
+
|
|
152
|
+
elsif match = scan(unicode ? /#{patterns::SYMBOL}/uo :
|
|
153
|
+
/#{patterns::SYMBOL}/o)
|
|
154
|
+
case delim = match[1]
|
|
155
|
+
when ?', ?"
|
|
156
|
+
encoder.begin_group :symbol
|
|
157
|
+
encoder.text_token ':', :symbol
|
|
158
|
+
match = delim.chr
|
|
159
|
+
encoder.text_token match, :delimiter
|
|
160
|
+
state = self.class::StringState.new :symbol, delim == ?", match
|
|
161
|
+
else
|
|
162
|
+
encoder.text_token match, :symbol
|
|
163
|
+
value_expected = false
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
elsif match = scan(/ ' (?:(?>[^'\\]*) ')? | " (?:(?>[^"\\\#]*) ")? /mx)
|
|
167
|
+
if match.size == 1
|
|
168
|
+
kind = check(self.class::StringState.simple_key_pattern(match)) ? :key : :string
|
|
169
|
+
encoder.begin_group kind
|
|
170
|
+
encoder.text_token match, :delimiter
|
|
171
|
+
state = self.class::StringState.new kind, match == '"', match # important for streaming
|
|
172
|
+
else
|
|
173
|
+
kind = value_expected == true && scan(/:/) ? :key : :string
|
|
174
|
+
encoder.begin_group kind
|
|
175
|
+
encoder.text_token match[0,1], :delimiter
|
|
176
|
+
encoder.text_token match[1..-2], :content if match.size > 2
|
|
177
|
+
encoder.text_token match[-1,1], :delimiter
|
|
178
|
+
encoder.end_group kind
|
|
179
|
+
encoder.text_token ':', :operator if kind == :key
|
|
180
|
+
value_expected = false
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
elsif match = scan(unicode ? /#{patterns::INSTANCE_VARIABLE}/uo :
|
|
184
|
+
/#{patterns::INSTANCE_VARIABLE}/o)
|
|
185
|
+
value_expected = false
|
|
186
|
+
encoder.text_token match, :instance_variable
|
|
187
|
+
|
|
188
|
+
elsif value_expected && match = scan(/\//)
|
|
189
|
+
encoder.begin_group :regexp
|
|
190
|
+
encoder.text_token match, :delimiter
|
|
191
|
+
state = self.class::StringState.new :regexp, true, '/'
|
|
192
|
+
|
|
193
|
+
elsif match = scan(value_expected ? /[-+]?#{patterns::NUMERIC}/o : /#{patterns::NUMERIC}/o)
|
|
194
|
+
if method_call_expected
|
|
195
|
+
encoder.text_token match, :error
|
|
196
|
+
method_call_expected = false
|
|
197
|
+
else
|
|
198
|
+
kind = self[1] ? :float : :integer # TODO: send :hex/:octal/:binary
|
|
199
|
+
match << 'r' if match !~ /e/i && scan(/r/)
|
|
200
|
+
match << 'i' if scan(/i/)
|
|
201
|
+
encoder.text_token match, kind
|
|
202
|
+
end
|
|
203
|
+
value_expected = false
|
|
204
|
+
|
|
205
|
+
elsif match = scan(/ [-+!~^\/]=? | [:;] | &\. | [*|&]{1,2}=? | >>? /x)
|
|
206
|
+
value_expected = true
|
|
207
|
+
encoder.text_token match, :operator
|
|
208
|
+
|
|
209
|
+
elsif value_expected && match = scan(/#{patterns::HEREDOC_OPEN}/o)
|
|
210
|
+
quote = self[3]
|
|
211
|
+
delim = self[quote ? 4 : 2]
|
|
212
|
+
kind = patterns::QUOTE_TO_TYPE[quote]
|
|
213
|
+
encoder.begin_group kind
|
|
214
|
+
encoder.text_token match, :delimiter
|
|
215
|
+
encoder.end_group kind
|
|
216
|
+
heredocs ||= [] # create heredocs if empty
|
|
217
|
+
heredocs << self.class::StringState.new(kind, quote != "'", delim,
|
|
218
|
+
self[1] ? :indented : :linestart)
|
|
219
|
+
value_expected = false
|
|
220
|
+
|
|
221
|
+
elsif value_expected && match = scan(/#{patterns::FANCY_STRING_START}/o)
|
|
222
|
+
kind = patterns::FANCY_STRING_KIND[self[1]]
|
|
223
|
+
encoder.begin_group kind
|
|
224
|
+
state = self.class::StringState.new kind, patterns::FANCY_STRING_INTERPRETED[self[1]], self[2]
|
|
225
|
+
encoder.text_token match, :delimiter
|
|
226
|
+
|
|
227
|
+
elsif value_expected && match = scan(/#{patterns::CHARACTER}/o)
|
|
228
|
+
value_expected = false
|
|
229
|
+
encoder.text_token match, :integer
|
|
230
|
+
|
|
231
|
+
elsif match = scan(/ %=? | <(?:<|=>?)? | \? /x)
|
|
232
|
+
value_expected = match == '?' ? :colon_expected : true
|
|
233
|
+
encoder.text_token match, :operator
|
|
234
|
+
|
|
235
|
+
elsif match = scan(/`/)
|
|
236
|
+
encoder.begin_group :shell
|
|
237
|
+
encoder.text_token match, :delimiter
|
|
238
|
+
state = self.class::StringState.new :shell, true, match
|
|
239
|
+
|
|
240
|
+
elsif match = scan(unicode ? /#{patterns::GLOBAL_VARIABLE}/uo :
|
|
241
|
+
/#{patterns::GLOBAL_VARIABLE}/o)
|
|
242
|
+
encoder.text_token match, :global_variable
|
|
243
|
+
value_expected = false
|
|
244
|
+
|
|
245
|
+
elsif match = scan(unicode ? /#{patterns::CLASS_VARIABLE}/uo :
|
|
246
|
+
/#{patterns::CLASS_VARIABLE}/o)
|
|
247
|
+
encoder.text_token match, :class_variable
|
|
248
|
+
value_expected = false
|
|
249
|
+
|
|
250
|
+
elsif match = scan(/\\\z/)
|
|
251
|
+
encoder.text_token match, :space
|
|
252
|
+
|
|
253
|
+
else
|
|
254
|
+
if method_call_expected
|
|
255
|
+
method_call_expected = false
|
|
256
|
+
next
|
|
257
|
+
end
|
|
258
|
+
unless unicode
|
|
259
|
+
# check for unicode
|
|
260
|
+
$DEBUG_BEFORE, $DEBUG = $DEBUG, false
|
|
261
|
+
begin
|
|
262
|
+
if check(/./mu).size > 1
|
|
263
|
+
# seems like we should try again with unicode
|
|
264
|
+
unicode = true
|
|
265
|
+
end
|
|
266
|
+
rescue
|
|
267
|
+
# bad unicode char; use getch
|
|
268
|
+
ensure
|
|
269
|
+
$DEBUG = $DEBUG_BEFORE
|
|
270
|
+
end
|
|
271
|
+
next if unicode
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
encoder.text_token getch, :error
|
|
275
|
+
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
if last_state
|
|
279
|
+
state = last_state unless state.is_a?(StringState) # otherwise, a simple 'def"' results in unclosed tokens
|
|
280
|
+
last_state = nil
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
elsif state == :def_expected
|
|
284
|
+
if match = scan(unicode ? /(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/uo :
|
|
285
|
+
/(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/o)
|
|
286
|
+
encoder.text_token match, :method
|
|
287
|
+
state = :initial
|
|
288
|
+
else
|
|
289
|
+
last_state = :dot_expected
|
|
290
|
+
state = :initial
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
elsif state == :dot_expected
|
|
294
|
+
if match = scan(/\.|::/)
|
|
295
|
+
# invalid definition
|
|
296
|
+
state = :def_expected
|
|
297
|
+
encoder.text_token match, :operator
|
|
298
|
+
else
|
|
299
|
+
state = :initial
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
elsif state == :module_expected
|
|
303
|
+
if match = scan(/<</)
|
|
304
|
+
encoder.text_token match, :operator
|
|
305
|
+
else
|
|
306
|
+
state = :initial
|
|
307
|
+
if match = scan(unicode ? / (?:#{patterns::IDENT}::)* #{patterns::IDENT} /oux :
|
|
308
|
+
/ (?:#{patterns::IDENT}::)* #{patterns::IDENT} /ox)
|
|
309
|
+
encoder.text_token match, :class
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
elsif state == :undef_expected
|
|
314
|
+
state = :undef_comma_expected
|
|
315
|
+
if match = scan(unicode ? /(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/uo :
|
|
316
|
+
/(?>#{patterns::METHOD_NAME_EX})(?!\.|::)/o)
|
|
317
|
+
encoder.text_token match, :method
|
|
318
|
+
elsif match = scan(/#{patterns::SYMBOL}/o)
|
|
319
|
+
case delim = match[1]
|
|
320
|
+
when ?', ?"
|
|
321
|
+
encoder.begin_group :symbol
|
|
322
|
+
encoder.text_token ':', :symbol
|
|
323
|
+
match = delim.chr
|
|
324
|
+
encoder.text_token match, :delimiter
|
|
325
|
+
state = self.class::StringState.new :symbol, delim == ?", match
|
|
326
|
+
state.next_state = :undef_comma_expected
|
|
327
|
+
else
|
|
328
|
+
encoder.text_token match, :symbol
|
|
329
|
+
end
|
|
330
|
+
else
|
|
331
|
+
state = :initial
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
elsif state == :undef_comma_expected
|
|
335
|
+
if match = scan(/,/)
|
|
336
|
+
encoder.text_token match, :operator
|
|
337
|
+
state = :undef_expected
|
|
338
|
+
else
|
|
339
|
+
state = :initial
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
elsif state == :alias_expected
|
|
343
|
+
match = scan(unicode ? /(#{patterns::METHOD_NAME_OR_SYMBOL})([ \t]+)(#{patterns::METHOD_NAME_OR_SYMBOL})/uo :
|
|
344
|
+
/(#{patterns::METHOD_NAME_OR_SYMBOL})([ \t]+)(#{patterns::METHOD_NAME_OR_SYMBOL})/o)
|
|
345
|
+
|
|
346
|
+
if match
|
|
347
|
+
encoder.text_token self[1], (self[1][0] == ?: ? :symbol : :method)
|
|
348
|
+
encoder.text_token self[2], :space
|
|
349
|
+
encoder.text_token self[3], (self[3][0] == ?: ? :symbol : :method)
|
|
350
|
+
end
|
|
351
|
+
state = :initial
|
|
352
|
+
|
|
353
|
+
else
|
|
354
|
+
#:nocov:
|
|
355
|
+
raise_inspect 'Unknown state: %p' % [state], encoder
|
|
356
|
+
#:nocov:
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
else # StringState
|
|
360
|
+
|
|
361
|
+
match = scan_until(state.pattern) || scan_rest
|
|
362
|
+
unless match.empty?
|
|
363
|
+
encoder.text_token match, :content
|
|
364
|
+
break if eos?
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
if state.heredoc && self[1] # end of heredoc
|
|
368
|
+
match = getch
|
|
369
|
+
match << scan_until(/$/) unless eos?
|
|
370
|
+
encoder.text_token match, :delimiter unless match.empty?
|
|
371
|
+
encoder.end_group state.type
|
|
372
|
+
state = state.next_state
|
|
373
|
+
next
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
case match = getch
|
|
377
|
+
|
|
378
|
+
when state.delim
|
|
379
|
+
if state.paren_depth
|
|
380
|
+
state.paren_depth -= 1
|
|
381
|
+
if state.paren_depth > 0
|
|
382
|
+
encoder.text_token match, :content
|
|
383
|
+
next
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
encoder.text_token match, :delimiter
|
|
387
|
+
if state.type == :regexp && !eos?
|
|
388
|
+
match = scan(/#{patterns::REGEXP_MODIFIERS}/o)
|
|
389
|
+
encoder.text_token match, :modifier unless match.empty?
|
|
390
|
+
end
|
|
391
|
+
encoder.end_group state.type
|
|
392
|
+
value_expected = false
|
|
393
|
+
state = state.next_state
|
|
394
|
+
|
|
395
|
+
when '\\'
|
|
396
|
+
if state.interpreted
|
|
397
|
+
if esc = scan(/#{patterns::ESCAPE}/o)
|
|
398
|
+
encoder.text_token match + esc, :char
|
|
399
|
+
else
|
|
400
|
+
encoder.text_token match, :error
|
|
401
|
+
end
|
|
402
|
+
else
|
|
403
|
+
case esc = getch
|
|
404
|
+
when nil
|
|
405
|
+
encoder.text_token match, :content
|
|
406
|
+
when state.delim, '\\'
|
|
407
|
+
encoder.text_token match + esc, :char
|
|
408
|
+
else
|
|
409
|
+
encoder.text_token match + esc, :content
|
|
410
|
+
end
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
when '#'
|
|
414
|
+
case peek(1)
|
|
415
|
+
when '{'
|
|
416
|
+
inline_block_stack ||= []
|
|
417
|
+
inline_block_stack << [state, inline_block_curly_depth, heredocs]
|
|
418
|
+
value_expected = true
|
|
419
|
+
state = :initial
|
|
420
|
+
inline_block_curly_depth = 1
|
|
421
|
+
encoder.begin_group :inline
|
|
422
|
+
encoder.text_token match + getch, :inline_delimiter
|
|
423
|
+
when '$', '@'
|
|
424
|
+
encoder.text_token match, :escape
|
|
425
|
+
last_state = state
|
|
426
|
+
state = :initial
|
|
427
|
+
else
|
|
428
|
+
#:nocov:
|
|
429
|
+
raise_inspect 'else-case # reached; #%p not handled' % [peek(1)], encoder
|
|
430
|
+
#:nocov:
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
when state.opening_paren
|
|
434
|
+
state.paren_depth += 1
|
|
435
|
+
encoder.text_token match, :content
|
|
436
|
+
|
|
437
|
+
else
|
|
438
|
+
#:nocov
|
|
439
|
+
raise_inspect 'else-case " reached; %p not handled, state = %p' % [match, state], encoder
|
|
440
|
+
#:nocov:
|
|
441
|
+
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
end
|
|
445
|
+
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
# cleaning up
|
|
449
|
+
if state.is_a? StringState
|
|
450
|
+
encoder.end_group state.type
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
if options[:keep_state]
|
|
454
|
+
if state.is_a?(StringState) && state.heredoc
|
|
455
|
+
(heredocs ||= []).unshift state
|
|
456
|
+
state = :initial
|
|
457
|
+
elsif heredocs && heredocs.empty?
|
|
458
|
+
heredocs = nil
|
|
459
|
+
end
|
|
460
|
+
@state = state, heredocs
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
if inline_block_stack
|
|
464
|
+
until inline_block_stack.empty?
|
|
465
|
+
state, = *inline_block_stack.pop
|
|
466
|
+
encoder.end_group :inline
|
|
467
|
+
encoder.end_group state.type
|
|
468
|
+
end
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
encoder
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
end
|
|
477
|
+
end
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
module CodeRay
|
|
2
|
+
module Scanners
|
|
3
|
+
|
|
4
|
+
# A scanner for Sass.
|
|
5
|
+
class Sass < CSS
|
|
6
|
+
|
|
7
|
+
register_for :sass
|
|
8
|
+
file_extension 'sass'
|
|
9
|
+
|
|
10
|
+
protected
|
|
11
|
+
|
|
12
|
+
def setup
|
|
13
|
+
@state = :initial
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def scan_tokens encoder, options
|
|
17
|
+
states = Array(options[:state] || @state).dup
|
|
18
|
+
|
|
19
|
+
encoder.begin_group :string if states.last == :sqstring || states.last == :dqstring
|
|
20
|
+
|
|
21
|
+
until eos?
|
|
22
|
+
|
|
23
|
+
if bol? && (match = scan(/(?>( +)?(\/[\*\/])(.+)?)(?=\n)/))
|
|
24
|
+
encoder.text_token self[1], :space if self[1]
|
|
25
|
+
encoder.begin_group :comment
|
|
26
|
+
encoder.text_token self[2], :delimiter
|
|
27
|
+
encoder.text_token self[3], :content if self[3]
|
|
28
|
+
if match = scan(/(?:\n+#{self[1]} .*)+/)
|
|
29
|
+
encoder.text_token match, :content
|
|
30
|
+
end
|
|
31
|
+
encoder.end_group :comment
|
|
32
|
+
elsif match = scan(/\n|[^\n\S]+\n?/)
|
|
33
|
+
encoder.text_token match, :space
|
|
34
|
+
if match.index(/\n/)
|
|
35
|
+
value_expected = false
|
|
36
|
+
states.pop if states.last == :include
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
elsif states.last == :sass_inline && (match = scan(/\}/))
|
|
40
|
+
encoder.text_token match, :inline_delimiter
|
|
41
|
+
encoder.end_group :inline
|
|
42
|
+
states.pop
|
|
43
|
+
|
|
44
|
+
elsif case states.last
|
|
45
|
+
when :initial, :media, :sass_inline
|
|
46
|
+
if match = scan(/(?>#{RE::Ident})(?!\()/ox)
|
|
47
|
+
encoder.text_token match, value_expected ? :value : (check(/.*:(?![a-z])/) ? :key : :tag)
|
|
48
|
+
next
|
|
49
|
+
elsif !value_expected && (match = scan(/\*/))
|
|
50
|
+
encoder.text_token match, :tag
|
|
51
|
+
next
|
|
52
|
+
elsif match = scan(RE::Class)
|
|
53
|
+
encoder.text_token match, :class
|
|
54
|
+
next
|
|
55
|
+
elsif match = scan(RE::Id)
|
|
56
|
+
encoder.text_token match, :id
|
|
57
|
+
next
|
|
58
|
+
elsif match = scan(RE::PseudoClass)
|
|
59
|
+
encoder.text_token match, :pseudo_class
|
|
60
|
+
next
|
|
61
|
+
elsif match = scan(RE::AttributeSelector)
|
|
62
|
+
# TODO: Improve highlighting inside of attribute selectors.
|
|
63
|
+
encoder.text_token match[0,1], :operator
|
|
64
|
+
encoder.text_token match[1..-2], :attribute_name if match.size > 2
|
|
65
|
+
encoder.text_token match[-1,1], :operator if match[-1] == ?]
|
|
66
|
+
next
|
|
67
|
+
elsif match = scan(/(\=|@mixin +)#{RE::Ident}/o)
|
|
68
|
+
encoder.text_token match, :function
|
|
69
|
+
next
|
|
70
|
+
elsif match = scan(/@import\b/)
|
|
71
|
+
encoder.text_token match, :directive
|
|
72
|
+
states << :include
|
|
73
|
+
next
|
|
74
|
+
elsif match = scan(/@media\b/)
|
|
75
|
+
encoder.text_token match, :directive
|
|
76
|
+
# states.push :media_before_name
|
|
77
|
+
next
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
when :block
|
|
81
|
+
if match = scan(/(?>#{RE::Ident})(?!\()/ox)
|
|
82
|
+
if value_expected
|
|
83
|
+
encoder.text_token match, :value
|
|
84
|
+
else
|
|
85
|
+
encoder.text_token match, :key
|
|
86
|
+
end
|
|
87
|
+
next
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
when :sqstring, :dqstring
|
|
91
|
+
if match = scan(states.last == :sqstring ? /(?:[^\n\'\#]+|\\\n|#{RE::Escape}|#(?!\{))+/o : /(?:[^\n\"\#]+|\\\n|#{RE::Escape}|#(?!\{))+/o)
|
|
92
|
+
encoder.text_token match, :content
|
|
93
|
+
elsif match = scan(/['"]/)
|
|
94
|
+
encoder.text_token match, :delimiter
|
|
95
|
+
encoder.end_group :string
|
|
96
|
+
states.pop
|
|
97
|
+
elsif match = scan(/#\{/)
|
|
98
|
+
encoder.begin_group :inline
|
|
99
|
+
encoder.text_token match, :inline_delimiter
|
|
100
|
+
states.push :sass_inline
|
|
101
|
+
elsif match = scan(/ \\ | $ /x)
|
|
102
|
+
encoder.end_group states.last
|
|
103
|
+
encoder.text_token match, :error unless match.empty?
|
|
104
|
+
states.pop
|
|
105
|
+
else
|
|
106
|
+
raise_inspect "else case #{states.last} reached; %p not handled." % peek(1), encoder
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
when :include
|
|
110
|
+
if match = scan(/[^\s'",]+/)
|
|
111
|
+
encoder.text_token match, :include
|
|
112
|
+
next
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
else
|
|
116
|
+
#:nocov:
|
|
117
|
+
raise_inspect 'Unknown state: %p' % [states.last], encoder
|
|
118
|
+
#:nocov:
|
|
119
|
+
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
elsif match = scan(/\$#{RE::Ident}/o)
|
|
123
|
+
encoder.text_token match, :variable
|
|
124
|
+
next
|
|
125
|
+
|
|
126
|
+
elsif match = scan(/&/)
|
|
127
|
+
encoder.text_token match, :local_variable
|
|
128
|
+
|
|
129
|
+
elsif match = scan(/\+#{RE::Ident}/o)
|
|
130
|
+
encoder.text_token match, :include
|
|
131
|
+
value_expected = true
|
|
132
|
+
|
|
133
|
+
elsif match = scan(/\/\*(?:.*?\*\/|.*)|\/\/.*/)
|
|
134
|
+
encoder.text_token match, :comment
|
|
135
|
+
|
|
136
|
+
elsif match = scan(/#\{/)
|
|
137
|
+
encoder.begin_group :inline
|
|
138
|
+
encoder.text_token match, :inline_delimiter
|
|
139
|
+
states.push :sass_inline
|
|
140
|
+
|
|
141
|
+
elsif match = scan(/\{/)
|
|
142
|
+
value_expected = false
|
|
143
|
+
encoder.text_token match, :operator
|
|
144
|
+
states.push :block
|
|
145
|
+
|
|
146
|
+
elsif match = scan(/\}/)
|
|
147
|
+
value_expected = false
|
|
148
|
+
encoder.text_token match, :operator
|
|
149
|
+
if states.last == :block || states.last == :media
|
|
150
|
+
states.pop
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
elsif match = scan(/['"]/)
|
|
154
|
+
encoder.begin_group :string
|
|
155
|
+
encoder.text_token match, :delimiter
|
|
156
|
+
if states.include? :sass_inline
|
|
157
|
+
# no nesting, just scan the string until delimiter
|
|
158
|
+
content = scan_until(/(?=#{match}|\}|\z)/)
|
|
159
|
+
encoder.text_token content, :content unless content.empty?
|
|
160
|
+
encoder.text_token match, :delimiter if scan(/#{match}/)
|
|
161
|
+
encoder.end_group :string
|
|
162
|
+
else
|
|
163
|
+
states.push match == "'" ? :sqstring : :dqstring
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
elsif match = scan(/#{RE::Function}/o)
|
|
167
|
+
encoder.begin_group :function
|
|
168
|
+
start = match[/^[-\w]+\(/]
|
|
169
|
+
encoder.text_token start, :delimiter
|
|
170
|
+
if match[-1] == ?)
|
|
171
|
+
encoder.text_token match[start.size..-2], :content
|
|
172
|
+
encoder.text_token ')', :delimiter
|
|
173
|
+
else
|
|
174
|
+
encoder.text_token match[start.size..-1], :content if start.size < match.size
|
|
175
|
+
end
|
|
176
|
+
encoder.end_group :function
|
|
177
|
+
|
|
178
|
+
elsif match = scan(/[a-z][-a-z_]*(?=\()/o)
|
|
179
|
+
encoder.text_token match, :predefined
|
|
180
|
+
|
|
181
|
+
elsif match = scan(/(?: #{RE::Dimension} | #{RE::Percentage} | #{RE::Num} )/ox)
|
|
182
|
+
encoder.text_token match, :float
|
|
183
|
+
|
|
184
|
+
elsif match = scan(/#{RE::HexColor}/o)
|
|
185
|
+
encoder.text_token match, :color
|
|
186
|
+
|
|
187
|
+
elsif match = scan(/! *(?:important|optional)/)
|
|
188
|
+
encoder.text_token match, :important
|
|
189
|
+
|
|
190
|
+
elsif match = scan(/(?:rgb|hsl)a?\([^()\n]*\)?/)
|
|
191
|
+
encoder.text_token match, :color
|
|
192
|
+
|
|
193
|
+
elsif match = scan(/@else if\b|#{RE::AtKeyword}/o)
|
|
194
|
+
encoder.text_token match, :directive
|
|
195
|
+
value_expected = true
|
|
196
|
+
|
|
197
|
+
elsif match = scan(/ == | != | [-+*\/>~:;,.=()] /x)
|
|
198
|
+
if match == ':'
|
|
199
|
+
value_expected = true
|
|
200
|
+
elsif match == ';'
|
|
201
|
+
value_expected = false
|
|
202
|
+
end
|
|
203
|
+
encoder.text_token match, :operator
|
|
204
|
+
|
|
205
|
+
else
|
|
206
|
+
encoder.text_token getch, :error
|
|
207
|
+
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
states.pop if states.last == :include
|
|
213
|
+
|
|
214
|
+
if options[:keep_state]
|
|
215
|
+
@state = states.dup
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
while state = states.pop
|
|
219
|
+
if state == :sass_inline
|
|
220
|
+
encoder.end_group :inline
|
|
221
|
+
elsif state == :sqstring || state == :dqstring
|
|
222
|
+
encoder.end_group :string
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
encoder
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
end
|
|
232
|
+
end
|