From c3efdad2c2dd4d65890f7cbd0e67a4324d1ce143 Mon Sep 17 00:00:00 2001 From: Iristyle Date: Thu, 4 Apr 2013 08:55:15 -0400 Subject: [PATCH] feat(SublimeText2.EditorPackages): cache packages --- .../tools/PackageCache/Abacus/.gitignore | 1 + .../tools/PackageCache/Abacus/Abacus.py | 212 ++ .../Abacus/Abacus.sublime-settings | 56 + .../PackageCache/Abacus/CSS.sublime-settings | 10 + .../Abacus/Default (Linux).sublime-keymap | 6 + .../Abacus/Default (OSX).sublime-keymap | 6 + .../Abacus/Default (Windows).sublime-keymap | 6 + .../Abacus/Default.sublime-commands | 6 + .../PackageCache/Abacus/Main.sublime-menu | 97 + .../tools/PackageCache/Abacus/README.md | 59 + .../PackageCache/Abacus/Ruby.sublime-settings | 10 + .../PackageCache/Abacus/package-metadata.json | 1 + .../Block Cursor Everywhere/.gitignore | 5 + .../Block Cursor Everywhere/README.md | 48 + .../SublimeBlockCursor.py | 38 + .../package-metadata.json | 1 + .../BracketHighlighter/.gitignore | 3 + .../BracketHighlighter/CHANGELOG.md | 68 + .../Default.sublime-commands | 152 ++ .../BracketHighlighter/Default.sublime-keymap | 16 + .../BracketHighlighter/Example.sublime-keymap | 153 ++ .../BracketHighlighter/Main.sublime-menu | 260 ++ .../BracketHighlighter/bh_core.py | 1405 ++++++++++ .../bh_core.sublime-settings | 372 +++ .../BracketHighlighter/bh_modules/__init__.py | 1 + .../bh_modules/bracketremove.py | 41 + .../bh_modules/bracketselect.py | 33 + .../bh_modules/foldbracket.py | 16 + .../bh_modules/phpkeywords.py | 2 + .../bh_modules/rubykeywords.py | 12 + .../bh_modules/swapbrackets.py | 14 + .../bh_modules/swapquotes.py | 46 + .../bh_modules/tagattrselect.py | 54 + .../bh_modules/tagnameselect.py | 14 + .../BracketHighlighter/bh_modules/tags.py | 243 ++ .../BracketHighlighter/bh_plugin.py | 140 + .../BracketHighlighter/bh_remove.py | 51 + .../BracketHighlighter/bh_swapping.py | 44 + .../bh_swapping.sublime-settings | 46 + .../BracketHighlighter/bh_wrapping.py | 368 +++ .../bh_wrapping.sublime-settings | 71 + .../icons/angle_bracket.png | Bin 0 -> 217 bytes .../icons/angle_bracket_close.png | Bin 0 -> 155 bytes .../icons/angle_bracket_close_small.png | Bin 0 -> 143 bytes .../icons/angle_bracket_open.png | Bin 0 -> 155 bytes .../icons/angle_bracket_open_small.png | Bin 0 -> 138 bytes .../icons/angle_bracket_small.png | Bin 0 -> 191 bytes .../BracketHighlighter/icons/bookmark.png | Bin 0 -> 108 bytes .../icons/bookmark_small.png | Bin 0 -> 118 bytes .../BracketHighlighter/icons/circle.png | Bin 0 -> 220 bytes .../BracketHighlighter/icons/circle_small.png | Bin 0 -> 215 bytes .../icons/curly_bracket.png | Bin 0 -> 242 bytes .../icons/curly_bracket_close.png | Bin 0 -> 173 bytes .../icons/curly_bracket_close_small.png | Bin 0 -> 156 bytes .../icons/curly_bracket_open.png | Bin 0 -> 173 bytes .../icons/curly_bracket_open_small.png | Bin 0 -> 145 bytes .../icons/curly_bracket_small.png | Bin 0 -> 211 bytes .../BracketHighlighter/icons/dot.png | Bin 0 -> 167 bytes .../BracketHighlighter/icons/dot_small.png | Bin 0 -> 148 bytes .../BracketHighlighter/icons/double_quote.png | Bin 0 -> 158 bytes .../icons/double_quote_close.png | Bin 0 -> 125 bytes .../icons/double_quote_close_small.png | Bin 0 -> 131 bytes .../icons/double_quote_offset.png | Bin 0 -> 163 bytes .../icons/double_quote_offset_open.png | Bin 0 -> 133 bytes .../icons/double_quote_offset_open_small.png | Bin 0 -> 134 bytes .../icons/double_quote_offset_small.png | Bin 0 -> 169 bytes .../icons/double_quote_open.png | Bin 0 -> 133 bytes .../icons/double_quote_open_small.png | Bin 0 -> 134 bytes .../icons/double_quote_small.png | Bin 0 -> 167 bytes .../BracketHighlighter/icons/question.png | Bin 0 -> 176 bytes .../icons/question_small.png | Bin 0 -> 158 bytes .../BracketHighlighter/icons/quote.png | Bin 0 -> 125 bytes .../BracketHighlighter/icons/quote_small.png | Bin 0 -> 131 bytes .../icons/round_bracket.png | Bin 0 -> 270 bytes .../icons/round_bracket_close.png | Bin 0 -> 188 bytes .../icons/round_bracket_close_small.png | Bin 0 -> 148 bytes .../icons/round_bracket_open.png | Bin 0 -> 190 bytes .../icons/round_bracket_open_small.png | Bin 0 -> 147 bytes .../icons/round_bracket_small.png | Bin 0 -> 206 bytes .../BracketHighlighter/icons/single_quote.png | Bin 0 -> 137 bytes .../icons/single_quote_close.png | Bin 0 -> 111 bytes .../icons/single_quote_close_small.png | Bin 0 -> 104 bytes .../icons/single_quote_offset.png | Bin 0 -> 141 bytes .../icons/single_quote_offset_open.png | Bin 0 -> 114 bytes .../icons/single_quote_offset_open_small.png | Bin 0 -> 103 bytes .../icons/single_quote_offset_small.png | Bin 0 -> 124 bytes .../icons/single_quote_open.png | Bin 0 -> 114 bytes .../icons/single_quote_open_small.png | Bin 0 -> 103 bytes .../icons/single_quote_small.png | Bin 0 -> 124 bytes .../icons/square_bracket.png | Bin 0 -> 101 bytes .../icons/square_bracket_close.png | Bin 0 -> 97 bytes .../icons/square_bracket_close_small.png | Bin 0 -> 111 bytes .../icons/square_bracket_open.png | Bin 0 -> 98 bytes .../icons/square_bracket_open_small.png | Bin 0 -> 112 bytes .../icons/square_bracket_small.png | Bin 0 -> 127 bytes .../BracketHighlighter/icons/star.png | Bin 0 -> 244 bytes .../BracketHighlighter/icons/star_small.png | Bin 0 -> 166 bytes .../BracketHighlighter/icons/tag.png | Bin 0 -> 306 bytes .../BracketHighlighter/icons/tag_small.png | Bin 0 -> 215 bytes .../BracketHighlighter/package-metadata.json | 1 + .../PackageCache/BracketHighlighter/readme.md | 470 ++++ .../Bracketeer/Default.sublime-commands | 25 + .../Bracketeer/Example.sublime-keymap | 276 ++ .../tools/PackageCache/Bracketeer/README.md | 133 + .../PackageCache/Bracketeer/bracketeer.py | 393 +++ .../Bracketeer/bracketeer.sublime-settings | 3 + .../Bracketeer/package-metadata.json | 1 + .../PackageCache/Bracketeer/package.json | 7 + .../Default.sublime-commands | 42 + .../Clipboard Manager/Example.sublime-keymap | 96 + .../PackageCache/Clipboard Manager/README.md | 123 + .../Clipboard Manager/clipboard_manager.py | 230 ++ .../Clipboard Manager/package-metadata.json | 1 + .../Clipboard Manager/package.json | 7 + .../tools/PackageCache/EasyMotion/.gitignore | 1 + .../EasyMotion/Default (Linux).sublime-keymap | 22 + .../EasyMotion/Default (OSX).sublime-keymap | 22 + .../Default (Windows).sublime-keymap | 22 + .../EasyMotion/Default.sublime-keymap | 23 + .../EasyMotion/EasyMotion.sublime-settings | 11 + .../tools/PackageCache/EasyMotion/LICENSE | 203 ++ .../PackageCache/EasyMotion/Main.sublime-menu | 90 + .../tools/PackageCache/EasyMotion/README.md | 134 + .../PackageCache/EasyMotion/easy_motion.py | 293 +++ .../EasyMotion/package-metadata.json | 1 + .../tools/PackageCache/ExportHtml/.gitignore | 2 + .../ColorSchemes/Print-Color.tmTheme | 362 +++ .../ColorSchemes/Print-Grayscale.tmTheme | 133 + .../ExportHtml/Context.sublime-menu | 26 + .../ExportHtml/Default.sublime-commands | 35 + .../PackageCache/ExportHtml/ExportBbcode.py | 352 +++ .../PackageCache/ExportHtml/ExportHtml.py | 907 +++++++ .../ExportHtml/ExportHtml.sublime-settings | 180 ++ .../ExportHtml/ExportHtmlLib/__init__.py | 0 .../ExportHtmlLib/desktop/__init__.py | 285 ++ .../ExportHtmlLib/desktop/dialog.py | 549 ++++ .../ExportHtmlLib/desktop/windows.py | 273 ++ .../ExportHtml/ExportHtmlLib/rgba/__init__.py | 0 .../ExportHtml/ExportHtmlLib/rgba/rgba.py | 173 ++ .../ExportHtml/HtmlAnnotations.py | 262 ++ .../PackageCache/ExportHtml/Main.sublime-menu | 40 + .../PackageCache/ExportHtml/css/export.css | 153 ++ .../PackageCache/ExportHtml/icons/bubble.png | Bin 0 -> 320 bytes .../PackageCache/ExportHtml/icons/down.png | Bin 0 -> 294 bytes .../PackageCache/ExportHtml/icons/gutter.png | Bin 0 -> 345 bytes .../PackageCache/ExportHtml/icons/print.png | Bin 0 -> 401 bytes .../PackageCache/ExportHtml/icons/text.png | Bin 0 -> 351 bytes .../PackageCache/ExportHtml/icons/toolbar.xcf | Bin 0 -> 4284 bytes .../PackageCache/ExportHtml/icons/wrap.png | Bin 0 -> 392 bytes .../PackageCache/ExportHtml/js/annotation.js | 206 ++ .../tools/PackageCache/ExportHtml/js/lines.js | 87 + .../PackageCache/ExportHtml/js/plaintext.js | 39 + .../tools/PackageCache/ExportHtml/js/plist.js | 129 + .../tools/PackageCache/ExportHtml/js/print.js | 15 + .../ExportHtml/package-metadata.json | 1 + .../tools/PackageCache/ExportHtml/readme.md | 220 ++ .../FileDiffs/Context.sublime-menu | 8 + .../FileDiffs/Default.sublime-commands | 6 + .../FileDiffs/Example.sublime-keymap | 3 + .../FileDiffs/FileDiffs.sublime-settings | 31 + .../PackageCache/FileDiffs/Main.sublime-menu | 27 + .../tools/PackageCache/FileDiffs/README.md | 55 + .../FileDiffs/Side Bar.sublime-menu | 6 + .../FileDiffs/Tab Context.sublime-menu | 6 + .../PackageCache/FileDiffs/file_diffs.py | 292 +++ .../FileDiffs/package-metadata.json | 1 + .../tools/PackageCache/FileDiffs/package.json | 7 + .../LiveReload/LiveReload.sublime-settings | 8 + .../PackageCache/LiveReload/Main.sublime-menu | 39 + .../tools/PackageCache/LiveReload/README.md | 62 + .../PackageCache/LiveReload/assets/config.rb | 8 + .../PackageCache/LiveReload/livereload.js | 942 +++++++ .../PackageCache/LiveReload/livereload_st2.py | 436 ++++ .../PackageCache/LiveReload/messages.json | 4 + .../LiveReload/messages/install_upgrade.txt | 9 + .../LiveReload/package-metadata.json | 1 + .../Markdown Preview/.gitattributes | 2 + .../PackageCache/Markdown Preview/.gitignore | 2 + .../Markdown Preview/Default.sublime-commands | 28 + .../Markdown Preview/Main.sublime-menu | 35 + .../Markdown Preview/MarkdownPreview.py | 217 ++ .../MarkdownPreview.sublime-settings | 60 + .../PackageCache/Markdown Preview/README.md | 52 + .../Markdown Preview/desktop/__init__.py | 290 ++ .../Markdown Preview/desktop/dialog.py | 551 ++++ .../Markdown Preview/desktop/windows.py | 273 ++ .../PackageCache/Markdown Preview/github.css | 364 +++ .../Markdown Preview/markdown.css | 122 + .../Markdown Preview/markdown2.py | 2322 +++++++++++++++++ .../Markdown Preview/package-metadata.json | 1 + .../PackageCache/Markdown Preview/sample.html | 141 + .../PackageCache/Markdown Preview/sample.md | 132 + .../Missing.sublime-commands | 115 + .../Missing Palette Commands/README.md | 8 + .../package-metadata.json | 1 + .../PackageCache/Related Files/.gitignore | 1 + .../Related Files/Default.sublime-commands | 3 + .../Related Files/Default.sublime-keymap | 3 + .../Related Files/Main.sublime-menu | 27 + .../PackageCache/Related Files/README.md | 36 + .../RelatedFiles.sublime-settings | 84 + .../app/controllers/examples_controller.rb | 3 + .../example1/app/helpers/examples_helper.rb | 0 .../example1/app/views/examples/index.html | 0 .../example1/app/views/examples/show.html | 0 .../controllers/examples_controller_test.rb | 0 .../app/controllers/examples_controller.rb | 0 .../example2/app/views/examples/index.html | 0 .../example2/app/views/examples/show.html | 0 .../controllers/examples_controller_test.rb | 0 .../Related Files/package-metadata.json | 1 + .../PackageCache/Related Files/related.py | 86 + .../Related Files/related_files.py | 39 + .../Related Files/related_test.py | 52 + .../Related Files/screenshots/list.png | Bin 0 -> 22696 bytes .../PackageCache/SmartMarkdown/.gitignore | 6 + .../Default (Linux).sublime-keymap | 26 + .../Default (OSX).sublime-keymap | 26 + .../Default (Windows).sublime-keymap | 26 + .../SmartMarkdown/Default.sublime-commands | 27 + .../SmartMarkdown/Default.sublime-keymap | 72 + .../SmartMarkdown/Main.sublime-menu | 70 + .../SmartMarkdown.sublime-settings | 11 + .../PackageCache/SmartMarkdown/headline.py | 263 ++ .../SmartMarkdown/headline_level.py | 21 + .../SmartMarkdown/headline_move.py | 61 + .../PackageCache/SmartMarkdown/license.txt | 7 + .../SmartMarkdown/package-metadata.json | 1 + .../SmartMarkdown/pandoc_render.py | 124 + .../PackageCache/SmartMarkdown/readme.md | 59 + .../SmartMarkdown/smart_folding.py | 195 ++ .../PackageCache/SmartMarkdown/smart_list.py | 57 + .../PackageCache/SmartMarkdown/smart_table.py | 87 + .../tools/PackageCache/SmartMarkdown/table.py | 207 ++ .../PackageCache/SmartMarkdown/utilities.py | 23 + .../Solarized Color Scheme/README.mkd | 80 + .../Solarized (dark).tmTheme | 2142 +++++++++++++++ .../Solarized (light).tmTheme | 2146 +++++++++++++++ .../package-metadata.json | 1 + .../StringEncode/Default.sublime-commands | 66 + .../StringEncode/Example.sublime-keymap | 14 + .../tools/PackageCache/StringEncode/README.md | 52 + .../StringEncode/package-metadata.json | 1 + .../PackageCache/StringEncode/package.json | 7 + .../StringEncode/string_encode.py | 165 ++ .../Default (Linux).sublime-keymap | 80 + .../Default (Linux).sublime-mousemap | 6 + .../SublimeTODO/Default (OSX).sublime-keymap | 80 + .../Default (OSX).sublime-mousemap | 6 + .../Default (Windows).sublime-keymap | 80 + .../Default (Windows).sublime-mousemap | 6 + .../SublimeTODO/Default.sublime-commands | 10 + .../PackageCache/SublimeTODO/README.markdown | 117 + .../PackageCache/SublimeTODO/messages.json | 3 + .../SublimeTODO/messages/1.1.0.txt | 6 + .../SublimeTODO/package-metadata.json | 1 + .../PackageCache/SublimeTODO/packages.json | 20 + .../tools/PackageCache/SublimeTODO/todo.py | 441 ++++ .../todo_results.hidden-tmLanguage | 49 + .../PackageCache/TrailingSpaces/.gitignore | 1 + .../TrailingSpaces/Default.sublime-commands | 10 + .../PackageCache/TrailingSpaces/MIT-LICENSE | 20 + .../TrailingSpaces/Main.sublime-menu | 123 + .../PackageCache/TrailingSpaces/README.md | 313 +++ .../PackageCache/TrailingSpaces/messages.json | 4 + .../TrailingSpaces/messages/install.txt | 40 + .../TrailingSpaces/messages/v1.0.0.txt | 65 + .../TrailingSpaces/package-metadata.json | 1 + .../PackageCache/TrailingSpaces/packages.json | 20 + .../TrailingSpaces/trailing_spaces.py | 453 ++++ .../trailing_spaces.sublime-settings | 56 + .../Default (Windows).sublime-keymap | 522 ++++ .../README.md | 38 + .../package-metadata.json | 1 + 274 files changed, 26863 insertions(+) create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/.gitignore create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Abacus.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Abacus.sublime-settings create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/CSS.sublime-settings create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Default (Linux).sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Default (OSX).sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Default (Windows).sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Default.sublime-commands create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Main.sublime-menu create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/README.md create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Ruby.sublime-settings create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/package-metadata.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Block Cursor Everywhere/.gitignore create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Block Cursor Everywhere/README.md create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Block Cursor Everywhere/SublimeBlockCursor.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Block Cursor Everywhere/package-metadata.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/.gitignore create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/CHANGELOG.md create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/Default.sublime-commands create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/Default.sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/Example.sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/Main.sublime-menu create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_core.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_core.sublime-settings create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/__init__.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/bracketremove.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/bracketselect.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/foldbracket.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/phpkeywords.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/rubykeywords.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/swapbrackets.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/swapquotes.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/tagattrselect.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/tagnameselect.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/tags.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_plugin.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_remove.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_swapping.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_swapping.sublime-settings create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_wrapping.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_wrapping.sublime-settings create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/angle_bracket.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/angle_bracket_close.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/angle_bracket_close_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/angle_bracket_open.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/angle_bracket_open_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/angle_bracket_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/bookmark.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/bookmark_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/circle.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/circle_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/curly_bracket.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/curly_bracket_close.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/curly_bracket_close_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/curly_bracket_open.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/curly_bracket_open_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/curly_bracket_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/dot.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/dot_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_close.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_close_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_offset.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_offset_open.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_offset_open_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_offset_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_open.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_open_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/question.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/question_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/quote.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/quote_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/round_bracket.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/round_bracket_close.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/round_bracket_close_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/round_bracket_open.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/round_bracket_open_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/round_bracket_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote_close.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote_close_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote_offset.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote_offset_open.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote_offset_open_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote_offset_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote_open.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote_open_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/square_bracket.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/square_bracket_close.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/square_bracket_close_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/square_bracket_open.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/square_bracket_open_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/square_bracket_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/star.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/star_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/tag.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/tag_small.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/package-metadata.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/readme.md create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/Default.sublime-commands create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/Example.sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/README.md create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/bracketeer.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/bracketeer.sublime-settings create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/package-metadata.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/package.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/Default.sublime-commands create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/Example.sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/README.md create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/clipboard_manager.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/package-metadata.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/package.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/.gitignore create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Default (Linux).sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Default (OSX).sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Default (Windows).sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Default.sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/EasyMotion.sublime-settings create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/LICENSE create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Main.sublime-menu create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/README.md create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/easy_motion.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/package-metadata.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/.gitignore create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ColorSchemes/Print-Color.tmTheme create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ColorSchemes/Print-Grayscale.tmTheme create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/Context.sublime-menu create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/Default.sublime-commands create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportBbcode.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtml.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtml.sublime-settings create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/__init__.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/desktop/__init__.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/desktop/dialog.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/desktop/windows.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/rgba/__init__.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/rgba/rgba.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/HtmlAnnotations.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/Main.sublime-menu create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/css/export.css create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/icons/bubble.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/icons/down.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/icons/gutter.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/icons/print.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/icons/text.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/icons/toolbar.xcf create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/icons/wrap.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/js/annotation.js create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/js/lines.js create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/js/plaintext.js create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/js/plist.js create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/js/print.js create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/package-metadata.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/readme.md create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Context.sublime-menu create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Default.sublime-commands create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Example.sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/FileDiffs.sublime-settings create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Main.sublime-menu create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/README.md create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Side Bar.sublime-menu create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Tab Context.sublime-menu create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/file_diffs.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/package-metadata.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/package.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/LiveReload.sublime-settings create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/Main.sublime-menu create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/README.md create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/assets/config.rb create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/livereload.js create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/livereload_st2.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/messages.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/messages/install_upgrade.txt create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/package-metadata.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/.gitattributes create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/.gitignore create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/Default.sublime-commands create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/Main.sublime-menu create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/MarkdownPreview.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/MarkdownPreview.sublime-settings create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/README.md create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/desktop/__init__.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/desktop/dialog.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/desktop/windows.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/github.css create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/markdown.css create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/markdown2.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/package-metadata.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/sample.html create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/sample.md create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Missing Palette Commands/Missing.sublime-commands create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Missing Palette Commands/README.md create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Missing Palette Commands/package-metadata.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/.gitignore create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/Default.sublime-commands create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/Default.sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/Main.sublime-menu create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/README.md create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/RelatedFiles.sublime-settings create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example1/app/controllers/examples_controller.rb create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example1/app/helpers/examples_helper.rb create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example1/app/views/examples/index.html create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example1/app/views/examples/show.html create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example1/test/controllers/examples_controller_test.rb create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example2/app/controllers/examples_controller.rb create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example2/app/views/examples/index.html create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example2/app/views/examples/show.html create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example2/test/controllers/examples_controller_test.rb create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/package-metadata.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/related.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/related_files.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/related_test.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/screenshots/list.png create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/.gitignore create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default (Linux).sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default (OSX).sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default (Windows).sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default.sublime-commands create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default.sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Main.sublime-menu create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/SmartMarkdown.sublime-settings create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/headline.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/headline_level.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/headline_move.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/license.txt create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/package-metadata.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/pandoc_render.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/readme.md create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/smart_folding.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/smart_list.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/smart_table.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/table.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/utilities.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Solarized Color Scheme/README.mkd create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Solarized Color Scheme/Solarized (dark).tmTheme create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Solarized Color Scheme/Solarized (light).tmTheme create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Solarized Color Scheme/package-metadata.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/Default.sublime-commands create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/Example.sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/README.md create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/package-metadata.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/package.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/string_encode.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (Linux).sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (Linux).sublime-mousemap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (OSX).sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (OSX).sublime-mousemap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (Windows).sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (Windows).sublime-mousemap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default.sublime-commands create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/README.markdown create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/messages.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/messages/1.1.0.txt create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/package-metadata.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/packages.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/todo.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/todo_results.hidden-tmLanguage create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/.gitignore create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/Default.sublime-commands create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/MIT-LICENSE create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/Main.sublime-menu create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/README.md create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/messages.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/messages/install.txt create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/messages/v1.0.0.txt create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/package-metadata.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/packages.json create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/trailing_spaces.py create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/trailing_spaces.sublime-settings create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Editor/Default (Windows).sublime-keymap create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Editor/README.md create mode 100644 EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Editor/package-metadata.json diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/.gitignore b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Abacus.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Abacus.py new file mode 100644 index 0000000..1a994cf --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Abacus.py @@ -0,0 +1,212 @@ +import sublime, sublime_plugin, re, sys +from string import Template + +class AbacusCommand(sublime_plugin.TextCommand): + """ + Main entry point. Find candidates for alignment, + calculate appropriate column widths, and then + perform a series of replacements. + """ + def run(self, edit): + candidates = [] + separators = sublime.load_settings("Abacus.sublime-settings").get("com.khiltd.abacus.separators") + indentor = Template("$indentation$left_col") + lg_aligner = Template("$left_col$separator") + rg_aligner = Template("$left_col$gutter$separator_padding$separator") + + #Run through the separators accumulating alignment candidates + #starting with the longest ones i.e. '==' before '='. + longest_first = self.sort_separators(separators) + + #Favor those that lean right so assignments with slice notation in them + #get handled sanely + for separator in [righty for righty in longest_first if righty["gravity"] == "right"]: + self.find_candidates_for_separator(separator, candidates) + + for separator in [lefty for lefty in longest_first if lefty["gravity"] == "left"]: + self.find_candidates_for_separator(separator, candidates) + + #After accumulation is done, figure out what the minimum required + #indentation and column width is going to have to be to make every + #candidate happy. + max_indent, max_left_col_width = self.calc_left_col_width(candidates) + + #Perform actual alignments based on gravitational affinity of separators + for candidate in candidates: + indent = 0 + if not candidate["preserve_indent"]: + indent = max_indent + else: + indent = candidate["adjusted_indent"] + + sep_width = len(candidate["separator"]) + right_col = candidate["right_col"].strip() + left_col = indentor.substitute( indentation = " " * indent, + left_col = candidate["left_col"] ) + #Marry the separator to the proper column + if candidate["gravity"] == "left": + #Separator sits flush left + left_col = lg_aligner.substitute(left_col = left_col, + separator = candidate["separator"] ) + elif candidate["gravity"] == "right": + gutter_width = max_left_col_width + max_indent - len(left_col) - len(candidate["separator"]) + #Push the separator ONE separator's width over the tab boundary + left_col = rg_aligner.substitute( left_col = left_col, + gutter = " " * gutter_width, + separator_padding = " " * sep_width, + separator = candidate["separator"] ) + #Most sane people will want a space between the operator and the value. + right_col = " %s" % right_col + #Snap the left side together + left_col = left_col.ljust(max_indent + max_left_col_width) + candidate["replacement"] = "%s%s\n" % (left_col, right_col) + + #Replace each line in its entirety + full_line = self.region_from_line_number(candidate["line"]) + #sys.stdout.write(candidate["replacement"]) + self.view.replace(edit, full_line, candidate["replacement"]) + + #Scroll and muck with the selection + if candidates: + self.view.sel().clear() + for region in [self.region_from_line_number(changed["line"]) for changed in candidates]: + start_of_right_col = region.begin() + max_indent + max_left_col_width + insertion_point = sublime.Region(start_of_right_col, start_of_right_col) + self.view.sel().add(insertion_point) + #self.view.show_at_center(insertion_point) + else: + sublime.status_message('Abacus - no alignment token found on selected line(s)') + + def sort_separators(self, separators): + return sorted(separators, key=lambda sep: -len(sep["token"])) + + def find_candidates_for_separator(self, separator, candidates): + """ + Given a particular separator, loop through every + line in the current selection looking for it and + add unique matches to a list. + """ + debug = sublime.load_settings("Abacus.sublime-settings").get("com.khiltd.abacus.debug") + token = separator["token"] + selection = self.view.sel() + new_candidates = [] + for region in selection: + for line in self.view.lines(region): + line_no = self.view.rowcol(line.begin())[0] + + #Never match a line more than once + if len([match for match in candidates if match["line"] == line_no]): + continue + + #Collapse any string literals that might + #also contain our separator token so that + #we can reliably find the location of the + #real McCoy. + line_content = self.view.substr(line) + collapsed = line_content + + for match in re.finditer(r"(\"[^\"]*(?' + #And remember that quoted strings were collapsed + #up above! + token_pos = None + safe_token = re.escape(token) + token_matcher = re.compile(r"(?= self.tab_width / 2: + initial_indent = self.snap_to_next_boundary(initial_indent, self.tab_width) + else: + initial_indent -= initial_indent % self.tab_width + candidate = { "line": line_no, + "original": line_content, + "separator": sep, + "gravity": separator["gravity"], + "adjusted_indent": initial_indent, + "preserve_indent": separator.get("preserve_indentation", False), + "left_col": left_col.lstrip(), + "right_col": right_col.rstrip() } + new_candidates.append(candidate) + #Poke more stuff in the accumulator + candidates.extend(new_candidates) + + def calc_left_col_width(self, candidates): + """ + Given a list of lines we've already matched against + one or more separators, loop through them all to + normalize their indentation and determine the minimum + possible column width that will accomodate them all + when aligned to a tab stop boundary. + """ + max_width = 0 + max_indent = 0 + max_sep_width = 0 + + for candidate in candidates: + max_indent = max([candidate["adjusted_indent"], max_indent]) + max_sep_width = max([len(candidate["separator"]), max_sep_width]) + max_width = max([len(candidate["left_col"].rstrip()), max_width]) + + max_width += max_sep_width + + #Bump up to the next multiple of tab_width + max_width = self.snap_to_next_boundary(max_width, self.tab_width) + + return max_indent, max_width + + @property + def tab_width(self): + """ + Exceptionally inefficient + """ + return int(self.view.settings().get('tab_size', 4)) + + def detab(self, input): + """ + Goodbye tabs! + """ + return input.expandtabs(self.tab_width) + + def region_from_line_number(self, line_number): + """ + Given a zero-based line number, return a region + encompassing it (including the newline). + """ + return self.view.full_line(self.view.text_point(line_number, 0)) + + def snap_to_next_boundary(self, value, interval): + """ + Alignment voodoo + """ + return value + (interval - value % interval) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Abacus.sublime-settings b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Abacus.sublime-settings new file mode 100644 index 0000000..9c4843a --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Abacus.sublime-settings @@ -0,0 +1,56 @@ +{ + "com.khiltd.abacus.debug": true, + "com.khiltd.abacus.separators": + [ + { + "token": ":", + "gravity": "left", + "preserve_indentation": true + }, + { + "token": "=", + "gravity": "right", + "preserve_indentation": true + }, + { + "token": "+=", + "gravity": "right", + "preserve_indentation": true + }, + { + "token": "-=", + "gravity": "right", + "preserve_indentation": true + }, + { + "token": "*=", + "gravity": "right", + "preserve_indentation": true + }, + { + "token": "/=", + "gravity": "right", + "preserve_indentation": true + }, + { + "token": "?=", + "gravity": "right", + "preserve_indentation": true + }, + { + "token": "||=", + "gravity": "right", + "preserve_indentation": true + }, + { + "token": "%=", + "gravity": "right", + "preserve_indentation": true + }, + { + "token": "==", + "gravity": "right", + "preserve_indentation": true + } + ] +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/CSS.sublime-settings b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/CSS.sublime-settings new file mode 100644 index 0000000..dbb14e7 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/CSS.sublime-settings @@ -0,0 +1,10 @@ +{ + "com.khiltd.abacus.separators": + [ + { + "token": ":", + "gravity": "left", + "preserve_indentation": false + } + ] +} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Default (Linux).sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Default (Linux).sublime-keymap new file mode 100644 index 0000000..34bd3cb --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Default (Linux).sublime-keymap @@ -0,0 +1,6 @@ +[ + { + "keys": ["ctrl+alt+]"], "command": "abacus" + } +] + diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Default (OSX).sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Default (OSX).sublime-keymap new file mode 100644 index 0000000..5a36030 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Default (OSX).sublime-keymap @@ -0,0 +1,6 @@ +[ + { + "keys": ["super+ctrl+alt+]"], "command": "abacus" + } +] + diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Default (Windows).sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Default (Windows).sublime-keymap new file mode 100644 index 0000000..34bd3cb --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Default (Windows).sublime-keymap @@ -0,0 +1,6 @@ +[ + { + "keys": ["ctrl+alt+]"], "command": "abacus" + } +] + diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Default.sublime-commands b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Default.sublime-commands new file mode 100644 index 0000000..2737802 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Default.sublime-commands @@ -0,0 +1,6 @@ +[ + { + "caption": "Abacus: align selection", + "command": "abacus" + } +] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Main.sublime-menu b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Main.sublime-menu new file mode 100644 index 0000000..98f7058 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Main.sublime-menu @@ -0,0 +1,97 @@ +[ + { + "id": "selection", + "children": + [ + { "id": "abacus" }, + { + "command": "abacus", + "caption": "Abacus Align" + } + ] + }, + { + "caption": "Preferences", + "mnemonic": "n", + "id": "preferences", + "children": + [ + { + "caption": "Package Settings", + "mnemonic": "P", + "id": "package-settings", + "children": + [ + { + "caption": "Abacus", + "children": + [ + { + "command": "open_file", + "args": { "file": "${packages}/Abacus/Abacus.sublime-settings" }, + "caption": "Settings - Default" + }, + { + "command": "open_file", + "args": { "file": "${packages}/User/Abacus.sublime-settings" }, + "caption": "Settings - User" + }, + { + "command": "open_file_settings", + "caption": "Settings - Syntax Specific - User" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/Abacus/Default (Windows).sublime-keymap", + "platform": "Windows" + }, + "caption": "Key Bindings – Default" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/Abacus/Default (OSX).sublime-keymap", + "platform": "OSX" + }, + "caption": "Key Bindings – Default" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/Abacus/Default (Linux).sublime-keymap", + "platform": "Linux" + }, + "caption": "Key Bindings – Default" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/User/Default (Windows).sublime-keymap", + "platform": "Windows" + }, + "caption": "Key Bindings – User" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/User/Default (OSX).sublime-keymap", + "platform": "OSX" + }, + "caption": "Key Bindings – User" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/User/Default (Linux).sublime-keymap", + "platform": "Linux" + }, + "caption": "Key Bindings – User" + } + ] + } + ] + } + ] + } +] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/README.md b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/README.md new file mode 100644 index 0000000..a1f9bfc --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/README.md @@ -0,0 +1,59 @@ +Abacus Alignment Plugin for Sublime Text 2 +================ + +![This work?](http://dl.dropbox.com/u/5514249/Abacus.gif) + +I'm pretty anal about aligning things in my code, but the alignment plugins I tried were more-or-less one-trick-ponies, and I didn't like any of their tricks, so I made my own. + +My one anal pony trick involves allowing you to slide the operator like an abacus bead, toward either the left or the right hand side, by giving each possible token a `gravity` property like so: + +``` json +{ + "com.khiltd.abacus.separators": + [ + { + "token": ":", + "gravity": "left", + "preserve_indentation": true + }, + { + "token": "=", + "gravity": "right", + "preserve_indentation": true + } + ] +} +``` + +Abacus focuses on aligning assignments in as language-agnostic a manner as possible and strives to address most of the open issues in that other, more popular plugin (it won't even jack up your Backbone routes!). It is, however, an *alignment* tool and *not* a full-blown beautifier. It works best when there's one assignment per line; if you like shoving dozens of CSS or JSON declarations on a single line then you are an enemy of readability and this plugin will make every effort to hinder and harm your creature on Earth as far as it is able. + +`preserve_indentation` is a tip that you might be working in a language where whitespace is significant, thereby suggesting that Abacus should make no effort to normalize indentation across lines. It's not foolproof, especially if you set your tab width really, really low, but it tries harder than Cory Doctorow ever has. OK, you're right... It would be impossible for anyone to try harder than that. + +Usage +============ + +Make a selection, then `command + option + control + ]`. + +Think the plugin's crazy? Add the following to your config: + +``` +"com.khiltd.abacus.debug": true +``` + +and Abacus will dump its thoughts out to Sublime Text's console like so: + +``` + margin:0; + ^ + padding:0; + ^ + border-style:none; + ^ +``` + +Caveats +============ + +I've used nothing but Macs since 1984 and do absolutely **no** testing in Windows or Ububian's window manager of the minute. If something's broken in some OS I don't own, you'll need to have a suggestion as to how it can be fixed as I'm unlikely to have any idea what you're talking about. + +I don't care if you like real tabs or Windows line endings and don't bother with handling them. Seriously, what year is this? diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Ruby.sublime-settings b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Ruby.sublime-settings new file mode 100644 index 0000000..60f1a00 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/Ruby.sublime-settings @@ -0,0 +1,10 @@ +{ + "com.khiltd.abacus.separators": + [ + { + "token": "=>", + "gravity": "right", + "preserve_indentation": true + } + ] +} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/package-metadata.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/package-metadata.json new file mode 100644 index 0000000..e7f9187 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Abacus/package-metadata.json @@ -0,0 +1 @@ +{"url": "https://github.com/khiltd/Abacus", "version": "2013.01.11.00.24.46", "description": "An Alignment Plugin for Sublime Text 2 that actually works `\u2318\u2325^ ]`"} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Block Cursor Everywhere/.gitignore b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Block Cursor Everywhere/.gitignore new file mode 100644 index 0000000..47748ac --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Block Cursor Everywhere/.gitignore @@ -0,0 +1,5 @@ +*.pyc +*.cache +*.sublime-project +.DS_store +.c9revisions \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Block Cursor Everywhere/README.md b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Block Cursor Everywhere/README.md new file mode 100644 index 0000000..2575d03 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Block Cursor Everywhere/README.md @@ -0,0 +1,48 @@ +# BlockCursorEverywhere # + +![Screenshot](http://f.cl.ly/items/42131K2X1h0j0P2m1O2B/Screen%20Shot%202011-12-02%20at%202.36.54%20AM.png) + +It can become very difficult to keep track of your cursor location. This is solved by having a "block" cursor, which is very easy to spot no matter where it is on screen. Unfortunately, Sublime Text 2 does not (yet) support this feature natively. This Plugin mimics this functionality by highlighting the area behind the cursor whenever it moves (similar to how you might highlight syntax errors, or color a comment). + +## Installation ## + +### With Package Control ### + +If you have the [Package Control][package_control] installed, you can install BlockCursorEverywhere from inside Sublime Text itself. Open the Command Palette and select "Package Control: Install Package", then search for BlockCursorEverywhere and you’re done! + +### Without Package Control ### + +Go to your Sublime Text 2 Packages directory: + + Windows: %USERPROFILE%\AppData\Roaming\Sublime Text 2\Packages\ + Mac: ~/Library/Application Support/Sublime Text 2/Packages/ + +and clone the repository there + + git clone git://github.com/ingshtrom/BlockCursorEverywhere + + +## Configuration ## + +These are the settings that I prefer. You can change the style of the block cursor by adding a section to your theme file like so: + +```xml + + name + Block Cursor + scope + block_cursor + settings + + foreground + #000000 + background + #FF1111 + + +``` + +--------- + +[sublime]: http://www.sublimetext.com/2 +[package_control]: http://wbond.net/sublime_packages/package_control diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Block Cursor Everywhere/SublimeBlockCursor.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Block Cursor Everywhere/SublimeBlockCursor.py new file mode 100644 index 0000000..0336494 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Block Cursor Everywhere/SublimeBlockCursor.py @@ -0,0 +1,38 @@ +import sublime +import sublime_plugin + + +class BlockCursorEverywhere(sublime_plugin.EventListener): + def view_is_widget(self, view): + settings = view.settings() + return bool(settings.get('is_widget')) + + def show_block_cursor(self, view): + validRegions = [] + for s in view.sel(): + if s.a != s.b: + continue + validRegions.append(sublime.Region(s.a, s.a + 1)) + if validRegions.__len__: + view.add_regions('BlockCursorListener', validRegions, 'block_cursor') + else: + view.erase_regions('BlockCursorListener') + + def on_selection_modified(self, view): + if view.settings().get('is_widget') or not("Vintage" in view.settings().get('ignored_packages') or view.settings().get('command_mode')): + view.erase_regions('BlockCursorListener') + return + self.show_block_cursor(view) + + def on_deactivated(self, view): + view.erase_regions('BlockCursorListener') + view.settings().clear_on_change('command_mode') + self.current_view = None + + def on_activated(self, view): + self.on_selection_modified(view) + view.settings().add_on_change('command_mode', self.on_command_mode_change) + self.current_view = view + + def on_command_mode_change(self): + self.on_selection_modified(self.current_view) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Block Cursor Everywhere/package-metadata.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Block Cursor Everywhere/package-metadata.json new file mode 100644 index 0000000..d22c40a --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Block Cursor Everywhere/package-metadata.json @@ -0,0 +1 @@ +{"url": "https://github.com/ingshtrom/BlockCursorEverywhere", "version": "2013.01.20.10.51.30", "description": "Sublime Text 2 plugin to mimic a block cursor in Vintage command mode."} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/.gitignore b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/.gitignore new file mode 100644 index 0000000..043d1e9 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/.gitignore @@ -0,0 +1,3 @@ +*.pyc + +.DS_Store diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/CHANGELOG.md b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/CHANGELOG.md new file mode 100644 index 0000000..94f2f51 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/CHANGELOG.md @@ -0,0 +1,68 @@ +# Version 1.9.0 +- Add experimental CFML support (defaulted off) +- Add auto-detection of self-closing tags (defaulted on) + +# Version 1.8.0 +- Add new commands: "Show Bracket String Escape Mode" and "Toggle Bracket String Escape Mode". Default is "regex" + +# Version 1.7.2 +- Feed general bracket type to bracket plugins +- Adjust bracket select plugin to better handle HTML tags + +# Version 1.7.1 +- Reorganize some settings +- Limit auto-highlight selections by configurable threshold setting + +# Version 1.7.0 +- Hide parent quote highlighting when child quotes are highlighted +- Allow the searching for brackets in non-quoted code scoped as strings (like regex) +- Add setting "highlight_string_brackets_only" which allows never highlighting quotes but leaves internal string bracket highlighting on +- deprecate "enable_forward_slash_regex_strings" in favor of "find_brackets_in any_strings" + +# Version 1.6.2 +- Fix adjacent_only with multi_select + +# Version 1.6.1 +- Suppress string highlighting when adjacent_only is set, but allow internal string brackets to still get highlighted with adjacent_only settings if match_string_brackets is true + +# Version 1.6.0 +- Add setting to match only when cursor is between brackets + +# Version 1.5.3 +- Allow turning off gutter icons for multi-select via settings +- Fix multi-select detection +- Default the internal settings if setting is not found + +# Version 1.5.2 +- Use tiny icons when line height is less than 16 +- Use no icon if icon cannot be found +- Optimize png icons + +# Version 1.5.1 +- Ignore selection/edit events inside the main routine + +# Version 1.5.0 +- More responsive highlighting (thanks tito); delay setting no longer needed +- Organize bracket plugins +- Included more configurable custom gutter icons + +# Version 1.4.1 +- Make adjusment to regex modifier code to correctly count back modifiers in perl + +# Version 1.4.0 +- Account for perl regex, substitutions, and translations surrounded by "/" for string bracket matching +- Account for regex modifiers when matching regex surrounded by "/" in javascript and perl + +# Version 1.3.0 +- Fixed escaped brackets in string handling. Also a bit more efficient. + +# Version 1.2.0 +- Fix angle bracket avoidance when finding brackets inside strings, and make it cleaner + +# Version 1.1.0 +- Add python raw string support for quote highlighting +- Add highlighting of brackets in strings; will work in all strings, but mainly meant for regex. True by default +- Add support for targetting regex strings like in javascript that are scoped as strings, but are not quoted, but use '/'s. True by default + +# Version 1.0.0 +- All previous work and releases diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/Default.sublime-commands b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/Default.sublime-commands new file mode 100644 index 0000000..b9bdfc7 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/Default.sublime-commands @@ -0,0 +1,152 @@ +[ + // Toggle Global Enable + { + "caption": "BracketHighlighter: Toggle Global Enable", + "command": "bh_toggle_enable" + }, + // Search to end of file for bracket + { + "caption": "BracketHighlighter: Match Brackets (ignore threshold)", + "command": "bh_key", + "args": {"lines" : true} + }, + // Remove brackets + { + "caption": "BracketHighlighter: Remove Brackets", + "command": "bh_remove_brackets" + }, + // Go to left bracket + { + "caption": "BracketHighlighter: Jump to Left Bracket", + "command": "bh_key", + "args": + { + "lines" : true, + "plugin": + { + "type": ["__all__"], + "command": "bh_modules.bracketselect", + "args": {"select": "left"} + } + } + }, + // Go to right bracket + { + "caption": "BracketHighlighter: Jump to Right Bracket", + "command": "bh_key", + "args": + { + "lines" : true, + "plugin": + { + "type": ["__all__"], + "command": "bh_modules.bracketselect", + "args": {"select": "right"} + } + } + }, + // Select text between brackets + { + "caption": "BracketHighlighter: Select Bracket Content", + "command": "bh_key", + "args": + { + "lines" : true, + "plugin": + { + "type": ["__all__"], + "command": "bh_modules.bracketselect" + } + } + }, + // Fold contents between brackets + { + "caption": "BracketHighlighter: Fold Bracket Content", + "command": "bh_key", + "args": + { + "plugin": { + "type": ["__all__"], + "command" : "bh_modules.foldbracket" + } + } + }, + { "caption": "-" }, + // Toggle between string and regex escape mode for string brackets + { + "caption": "BracketHighlighter: Toggle String Bracket Escape Mode", + "command": "bh_toggle_string_escape_mode" + }, + // Toggle high visibility mode + { + "caption": "BracketHighlighter: Toggle High Visibility Mode", + "command": "bh_toggle_high_visibility" + }, + { "caption": "-" }, + // Select tag name of HTML/XML tag (both opening name and closing) + { + "caption": "BracketHighlighter: Select Tag Name (closing and opening)", + "command": "bh_key", + "args": + { + "plugin": + { + "type": ["cfml", "html", "angle"], + "command": "bh_modules.tagnameselect" + } + } + }, + // Select the attribute to the right of the cursor (will wrap inside the tag) + { + "caption": "BracketHighlighter: Select Next Attribute (right)", + "command": "bh_key", + "args": + { + "plugin": + { + "type": ["cfml", "html", "angle"], + "command": "bh_modules.tagattrselect", + "args": {"direction": "right"} + } + } + }, + // Select the attribute to the left of the cursor (will wrap inside the tag) + { + "caption": "BracketHighlighter: Select Next Attribute (left)", + "command": "bh_key", + "args": + { + "plugin": + { + "type": ["cfml", "html", "angle"], + "command": "bh_modules.tagattrselect", + "args": {"direction": "left"} + } + } + }, + // Convert single quote string to double quoted string and vice versa + // Will handle escaping or unescaping quotes within the string + { + "caption": "BracketHighlighter: Swap Quotes", + "command": "bh_key", + "args": + { + "lines" : true, + "plugin": + { + "type": ["single_quote", "double_quote", "py_single_quote", "py_double_quote"], + "command": "bh_modules.swapquotes" + } + } + }, + // Swap Brackets + { + "caption": "BracketHighlighter: Swap Brackets", + "command": "swap_brackets" + }, + // Surround selection with brackets from quick panel + { + "caption": "BracketHighlighter: Wrap Selections with Brackets", + "command": "wrap_brackets" + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/Default.sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/Default.sublime-keymap new file mode 100644 index 0000000..5421945 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/Default.sublime-keymap @@ -0,0 +1,16 @@ +[ + // Navigate tabstops in wrapped selection + { + "keys": ["tab"], + "command": "bh_next_wrap_sel", + "context": + [ + { + "operand": true, + "operator": "equal", + "match_all": true, + "key": "bh_wrapping" + } + ] + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/Example.sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/Example.sublime-keymap new file mode 100644 index 0000000..ef99e15 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/Example.sublime-keymap @@ -0,0 +1,153 @@ +[ + // Toggle Global Enable + { + "keys": ["ctrl+alt+super+e"], + "command": "bh_toggle_enable" + }, + // Search to end of file for bracket + { + "keys": ["ctrl+alt+super+b"], + "command": "bh_key", + "args": + { + "lines" : true + } + }, + // Go to left bracket + { + "keys": ["ctrl+alt+super+up"], + "command": "bh_key", + "args": + { + "lines" : true, + "plugin": + { + "type": ["__all__"], + "command": "bh_modules.bracketselect", + "args": {"select": "left"} + } + } + }, + // Go to right bracket + { + "keys": ["ctrl+alt+super+down"], + "command": "bh_key", + "args": + { + "lines" : true, + "plugin": + { + "type": ["__all__"], + "command": "bh_modules.bracketselect", + "args": {"select": "right"} + } + } + }, + // Remove brackets + { + "keys": ["ctrl+alt+super+r"], + "command": "bh_remove_brackets" + }, + // Toggle string escape mode for sub bracket search in strings + { + "keys": ["ctrl+alt+super+x"], + "command": "bh_toggle_string_escape_mode" + }, + // Select text between brackets + { + "keys": ["ctrl+alt+super+s"], + "command": "bh_key", + "args": + { + "lines" : true, + "plugin": + { + "type": ["__all__"], + "command": "bh_modules.bracketselect" + } + } + }, + // Select tag name of HTML/XML tag (both opening name and closing) + { + "keys": ["ctrl+alt+super+t"], + "command": "bh_key", + "args": + { + "plugin": + { + "type": ["cfml", "html", "angle"], + "command": "bh_modules.tagnameselect" + } + } + }, + // Select the attribute to the right of the cursor (will wrap inside the tag) + { + "keys": ["ctrl+alt+super+right"], + "command": "bh_key", + "args": + { + "plugin": + { + "type": ["cfml", "html", "angle"], + "command": "bh_modules.tagattrselect", + "args": {"direction": "right"} + } + } + }, + // Select the attribute to the left of the cursor (will wrap inside the tag) + { + "keys": ["ctrl+alt+super+left"], + "command": "bh_key", + "args": + { + "plugin": + { + "type": ["cfml", "html", "angle"], + "command": "bh_modules.tagattrselect", + "args": {"direction": "left"} + } + } + }, + // Convert single quote string to double quoted string and vice versa + // Will handle escaping or unescaping quotes within the string + { + "keys": ["ctrl+alt+super+q"], + "command": "bh_key", + "args": + { + "lines" : true, + "plugin": + { + "type": ["single_quote", "double_quote", "py_single_quote", "py_double_quote"], + "command": "bh_modules.swapquotes" + } + } + }, + // Fold contents between brackets + { + "keys": ["ctrl+alt+super+["], + "command": "bh_key", + "args": + { + "plugin": { + "type": ["__all__"], + "command" : "bh_modules.foldbracket" + } + } + }, + // Swap brackets with another type + { + "keys": ["ctrl+alt+super+e"], + "command": "swap_brackets" + }, + // Surround selection with brackets from quick panel + { + "keys": ["ctrl+alt+super+w"], + "command": "wrap_brackets" + }, + // Toggle high visibility mode + { + "keys": ["ctrl+alt+super+v"], + "command": "bh_toggle_high_visibility" + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/Main.sublime-menu b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/Main.sublime-menu new file mode 100644 index 0000000..1c2a3b8 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/Main.sublime-menu @@ -0,0 +1,260 @@ +[ + { + "id": "tools", + "caption": "Tools", + "children": + [ + { + "id": "packages", + "caption": "Packages", + "children": + [ + { + "id": "brackethighlighter", + "caption": "BracketHighlighter", + "children": + [ + // Toggle Global Enable + { + "caption": "BracketHighlighter: Toggle Global Enable", + "command": "bh_toggle_enable" + }, + { "caption": "-" }, + // Search to end of file for bracket + { + "caption": "Match Brackets (ignore threshold)", + "command": "bh_key", + "args": {"lines" : true} + }, + // Remove brackets + { + "caption": "Remove Brackets", + "command": "bh_remove_brackets" + }, + // Go to left bracket + { + "caption": "Jump to Left Bracket", + "command": "bh_key", + "args": + { + "lines" : true, + "plugin": + { + "type": ["__all__"], + "command": "bh_modules.bracketselect", + "args": {"select": "left"} + } + } + }, + // Go to right bracket + { + "caption": "Jump to Right Bracket", + "command": "bh_key", + "args": + { + "lines" : true, + "plugin": + { + "type": ["__all__"], + "command": "bh_modules.bracketselect", + "args": {"select": "right"} + } + } + }, + // Select text between brackets + { + "caption": "Select Bracket Content", + "command": "bh_key", + "args": + { + "lines" : true, + "plugin": + { + "type": ["__all__"], + "command": "bh_modules.bracketselect" + } + } + }, + // Fold contents between brackets + { + "caption": "Fold Bracket Content", + "command": "bh_key", + "args": + { + "plugin": { + "type": ["__all__"], + "command" : "bh_modules.foldbracket" + } + } + }, + { "caption": "-" }, + // Toggle between string and regex escape mode for string brackets + { + "caption": "Toggle String Bracket Escape Mode", + "command": "bh_toggle_string_escape_mode" + }, + // Toggle high visibility mode + { + "caption": "Toggle High Visibility Mode", + "command": "bh_toggle_high_visibility" + }, + { "caption": "-" }, + // Select tag name of HTML/XML tag (both opening name and closing) + { + "caption": "Select Tag Name (closing and opening)", + "command": "bh_key", + "args": + { + "plugin": + { + "type": ["cfml", "html", "angle"], + "command": "bh_modules.tagnameselect" + } + } + }, + // Select the attribute to the right of the cursor (will wrap inside the tag) + { + "caption": "Select Next Attribute (right)", + "command": "bh_key", + "args": + { + "plugin": + { + "type": ["cfml", "html", "angle"], + "command": "bh_modules.tagattrselect", + "args": {"direction": "right"} + } + } + }, + // Select the attribute to the left of the cursor (will wrap inside the tag) + { + "caption": "Select Next Attribute (left)", + "command": "bh_key", + "args": + { + "plugin": + { + "type": ["cfml", "html", "angle"], + "command": "bh_modules.tagattrselect", + "args": {"direction": "left"} + } + } + }, + { "caption": "-" }, + // Convert single quote string to double quoted string and vice versa + // Will handle escaping or unescaping quotes within the string + { + "caption": "Swap Quotes", + "command": "bh_key", + "args": + { + "lines" : true, + "plugin": + { + "type": ["single_quote", "double_quote", "py_single_quote", "py_double_quote"], + "command": "bh_modules.swapquotes" + } + } + }, + // Swap brackets + { + "caption": "Swap Brackets", + "command": "swap_brackets" + }, + // Surround selection with brackets from quick panel + { + "caption": "Wrap Selections with Brackets", + "command": "wrap_brackets" + } + ] + } + ] + } + ] + }, + { + "caption": "Preferences", + "mnemonic": "n", + "id": "preferences", + "children": + [ + { + "caption": "Package Settings", + "mnemonic": "P", + "id": "package-settings", + "children": + [ + { + "caption": "Bracket Highlighter", + "children": + [ + { + "command": "open_file", + "args": {"file": "${packages}/BracketHighlighter/bh_core.sublime-settings"}, + "caption": "Bracket Settings – Default" + }, + { + "command": "open_file", + "args": {"file": "${packages}/User/bh_core.sublime-settings"}, + "caption": "Bracket Settings – User" + }, + { "caption": "-" }, + { + "command": "open_file", + "args": {"file": "${packages}/BracketHighlighter/bh_wrapping.sublime-settings"}, + "caption": "Wrap Settings – Default" + }, + { + "command": "open_file", + "args": {"file": "${packages}/User/bh_wrapping.sublime-settings"}, + "caption": "Wrap Settings – User" + }, + { "caption": "-" }, + { + "command": "open_file", + "args": {"file": "${packages}/BracketHighlighter/bh_swapping.sublime-settings"}, + "caption": "Swap Settings – Default" + }, + { + "command": "open_file", + "args": {"file": "${packages}/User/bh_swapping.sublime-settings"}, + "caption": "Swap Settings – User" + }, + { "caption": "-" }, + { + "command": "open_file", + "args": {"file": "${packages}/BracketHighlighter/Example.sublime-keymap"}, + "caption": "Example Key Bindings" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/User/Default (Windows).sublime-keymap", + "platform": "Windows" + }, + "caption": "Key Bindings – User" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/User/Default (OSX).sublime-keymap", + "platform": "OSX" + }, + "caption": "Key Bindings – User" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/User/Default (Linux).sublime-keymap", + "platform": "Linux" + }, + "caption": "Key Bindings – User" + }, + { "caption": "-" } + ] + } + ] + } + ] + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_core.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_core.py new file mode 100644 index 0000000..8e644b6 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_core.py @@ -0,0 +1,1405 @@ +from os.path import basename, exists, join, normpath +import sublime +import sublime_plugin +from time import time, sleep +import thread +import re +from bh_plugin import BracketPlugin, BracketRegion, ImportModule +from collections import namedtuple +import traceback + +BH_MATCH_TYPE_NONE = 0 +BH_MATCH_TYPE_SELECTION = 1 +BH_MATCH_TYPE_EDIT = 2 +DEFAULT_STYLES = { + "default": { + "icon": "dot", + "color": "brackethighlighter.default", + "style": "underline" + }, + "unmatched": { + "icon": "question", + "color": "brackethighlighter.unmatched", + "style": "outline" + } +} +HV_RSVD_VALUES = ["__default__", "__bracket__"] + +HIGH_VISIBILITY = False + +GLOBAL_ENABLE = True + + +def bh_logging(msg): + print("BracketHighlighter: %s" % msg) + + +def bh_debug(msg): + if sublime.load_settings("bh_core.sublime-settings").get('debug_enable', False): + bh_logging(msg) + + +def underline(regions): + """ + Convert sublime regions into underline regions + """ + + r = [] + for region in regions: + start = region.begin() + end = region.end() + while start < end: + r.append(sublime.Region(start)) + start += 1 + return r + + +def load_modules(obj, loaded): + """ + Load bracket plugin modules + """ + + plib = obj.get("plugin_library") + if plib is None: + return + + try: + module = ImportModule.import_module(plib, loaded) + obj["compare"] = getattr(module, "compare", None) + obj["post_match"] = getattr(module, "post_match", None) + loaded.add(plib) + except: + bh_logging("Could not load module %s\n%s" % (plib, str(traceback.format_exc()))) + raise + + +def select_bracket_style(option): + """ + Configure style of region based on option + """ + + style = sublime.HIDE_ON_MINIMAP + if option == "outline": + style |= sublime.DRAW_OUTLINED + elif option == "none": + style |= sublime.HIDDEN + elif option == "underline": + style |= sublime.DRAW_EMPTY_AS_OVERWRITE + return style + + +def select_bracket_icons(option, icon_path): + """ + Configure custom gutter icons if they can be located. + """ + + icon = "" + small_icon = "" + open_icon = "" + small_open_icon = "" + close_icon = "" + small_close_icon = "" + # Icon exist? + if not option == "none" and not option == "": + if exists(normpath(join(sublime.packages_path(), icon_path, option + ".png"))): + icon = "../%s/%s" % (icon_path, option) + if exists(normpath(join(sublime.packages_path(), icon_path, option + "_small.png"))): + small_icon = "../%s/%s" % (icon_path, option + "_small") + if exists(normpath(join(sublime.packages_path(), icon_path, option + "_open.png"))): + open_icon = "../%s/%s" % (icon_path, option + "_open") + else: + open_icon = icon + if exists(normpath(join(sublime.packages_path(), icon_path, option + "_open_small.png"))): + small_open_icon = "../%s/%s" % (icon_path, option + "_open_small") + else: + small_open_icon = small_icon + if exists(normpath(join(sublime.packages_path(), icon_path, option + "_close.png"))): + close_icon = "../%s/%s" % (icon_path, option + "_close") + else: + close_icon = icon + if exists(normpath(join(sublime.packages_path(), icon_path, option + "_close_small.png"))): + small_close_icon = "../%s/%s" % (icon_path, option + "_close_small") + else: + small_close_icon = small_icon + + return icon, small_icon, open_icon, small_open_icon, close_icon, small_close_icon + + +def exclude_bracket(enabled, filter_type, language_list, language): + """ + Exclude or include brackets based on filter lists. + """ + + exclude = True + if enabled: + # Black list languages + if filter_type == 'blacklist': + exclude = False + if language != None: + for item in language_list: + if language == item.lower(): + exclude = True + break + #White list languages + elif filter_type == 'whitelist': + if language != None: + for item in language_list: + if language == item.lower(): + exclude = False + break + return exclude + + +class BhEventMgr(object): + """ + Object to manage when bracket events should be launched. + """ + + @classmethod + def load(cls): + """ + Initialize variables for determining + when to initiate a bracket matching event. + """ + + cls.wait_time = 0.12 + cls.time = time() + cls.modified = False + cls.type = BH_MATCH_TYPE_SELECTION + cls.ignore_all = False + +BhEventMgr.load() + + +class BhThreadMgr(object): + """ + Object to help track when a new thread needs to be started. + """ + + restart = False + + +class BhEntry(object): + """ + Generic object for bracket regions. + """ + + def move(self, begin, end): + """ + Create a new object with the points moved to the specified locations. + """ + + return self._replace(begin=begin, end=end) + + def size(self): + """ + Size of bracket selection. + """ + + return abs(self.begin - self.end) + + def toregion(self): + """ + Convert to sublime Region. + """ + + return sublime.Region(self.begin, self.end) + + +class BracketEntry(namedtuple('BracketEntry', ['begin', 'end', 'type'], verbose=False), BhEntry): + """ + Bracket object. + """ + + pass + + +class ScopeEntry(namedtuple('ScopeEntry', ['begin', 'end', 'scope', 'type'], verbose=False), BhEntry): + """ + Scope bracket object. + """ + + pass + + +class BracketSearchSide(object): + """ + Userful structure to specify bracket matching direction. + """ + + left = 0 + right = 1 + + +class BracektSearchType(object): + """ + Userful structure to specify bracket matching direction. + """ + + opening = 0 + closing = 1 + + +class BracketSearch(object): + """ + Object that performs regex search on the view's buffer and finds brackets. + """ + + def __init__(self, bfr, window, center, pattern, scope_check, scope): + """ + Prepare the search object + """ + + self.center = center + self.pattern = pattern + self.bfr = bfr + self.scope = scope + self.scope_check = scope_check + self.prev_match = [None, None] + self.return_prev = [False, False] + self.done = [False, False] + self.start = [None, None] + self.left = [[], []] + self.right = [[], []] + self.findall(window) + + def reset_end_state(self): + """ + Reset the the current search flags etc. + This is usually done before searching the other direction. + """ + + self.start = [None, None] + self.done = [False, False] + self.prev_match = [None, None] + self.return_prev = [False, False] + + def remember(self, match_type): + """ + Remember the current match. + Don't get the next bracket on the next + request, but return the current one again. + """ + + self.return_prev[match_type] = True + self.done[match_type] = False + + def findall(self, window): + """ + Find all of the brackets and sort them + to "left of the cursor" and "right of the cursor" + """ + + for m in self.pattern.finditer(self.bfr, window[0], window[1]): + g = m.lastindex + try: + start = m.start(g) + end = m.end(g) + except: + continue + + match_type = int(not bool(g % 2)) + bracket_id = (g / 2) - match_type + + if not self.scope_check(start, bracket_id, self.scope): + if (end <= self.center if match_type else start < self.center): + self.left[match_type].append(BracketEntry(start, end, bracket_id)) + elif (end > self.center if match_type else start >= self.center): + self.right[match_type].append(BracketEntry(start, end, bracket_id)) + + def get_open(self, bracket_code): + """ + Get opening bracket. Accepts a bracket code that + determines which side of the cursor the next match is returned from. + """ + + for b in self._get_bracket(bracket_code, BracektSearchType.opening): + yield b + + def get_close(self, bracket_code): + """ + Get closing bracket. Accepts a bracket code that + determines which side of the cursor the next match is returned from. + """ + + for b in self._get_bracket(bracket_code, BracektSearchType.closing): + yield b + + def is_done(self, match_type): + """ + Retrieve done flag. + """ + + return self.done[match_type] + + def _get_bracket(self, bracket_code, match_type): + """ + Get the next bracket. Accepts bracket code that determines + which side of the cursor the next match is returned from and + the match type which determines whether a opening or closing + bracket is desired. + """ + + if self.done[match_type]: + return + if self.return_prev[match_type]: + self.return_prev[match_type] = False + yield self.prev_match[match_type] + if bracket_code == BracketSearchSide.left: + if self.start[match_type] is None: + self.start[match_type] = len(self.left[match_type]) + for x in reversed(range(0, self.start[match_type])): + b = self.left[match_type][x] + self.prev_match[match_type] = b + self.start[match_type] -= 1 + yield b + else: + if self.start[match_type] is None: + self.start[match_type] = 0 + for x in range(self.start[match_type], len(self.right[match_type])): + b = self.right[match_type][x] + self.prev_match[match_type] = b + self.start[match_type] += 1 + yield b + + self.done[match_type] = True + + +class BracketDefinition(object): + """ + Normal bracket definition. + """ + + def __init__(self, bracket): + """ + Setup the bracket object by reading the passed in dictionary. + """ + + self.name = bracket["name"] + self.style = bracket.get("style", "default") + self.compare = bracket.get("compare") + sub_search = bracket.get("find_in_sub_search", "false") + self.find_in_sub_search_only = sub_search == "only" + self.find_in_sub_search = sub_search == "true" or self.find_in_sub_search_only + self.post_match = bracket.get("post_match") + self.scope_exclude_exceptions = bracket.get("scope_exclude_exceptions", []) + self.scope_exclude = bracket.get("scope_exclude", []) + self.ignore_string_escape = bracket.get("ignore_string_escape", False) + + +class ScopeDefinition(object): + """ + Scope bracket definition. + """ + + def __init__(self, bracket): + """ + Setup the bracket object by reading the passed in dictionary. + """ + + self.style = bracket.get("style", "default") + self.open = re.compile("\\A" + bracket.get("open", "."), re.MULTILINE | re.IGNORECASE) + self.close = re.compile(bracket.get("close", ".") + "\\Z", re.MULTILINE | re.IGNORECASE) + self.name = bracket["name"] + sub_search = bracket.get("sub_bracket_search", "false") + self.sub_search_only = sub_search == "only" + self.sub_search = self.sub_search_only == True or sub_search == "true" + self.compare = bracket.get("compare") + self.post_match = bracket.get("post_match") + self.scopes = bracket["scopes"] + + +class StyleDefinition(object): + """ + Styling definition. + """ + + def __init__(self, name, style, default_highlight, icon_path): + """ + Setup the style object by reading the + passed in dictionary. And other parameters. + """ + + self.name = name + self.selections = [] + self.open_selections = [] + self.close_selections = [] + self.center_selections = [] + self.color = style.get("color", default_highlight["color"]) + self.style = select_bracket_style(style.get("style", default_highlight["style"])) + self.underline = self.style & sublime.DRAW_EMPTY_AS_OVERWRITE + ( + self.icon, self.small_icon, self.open_icon, + self.small_open_icon, self.close_icon, self.small_close_icon + ) = select_bracket_icons(style.get("icon", default_highlight["icon"]), icon_path) + self.no_icon = "" + + +class BhToggleStringEscapeModeCommand(sublime_plugin.TextCommand): + """ + Toggle between regex escape and + string escape for brackets in strings. + """ + + def run(self, edit): + default_mode = sublime.load_settings("bh_core.sublime-settings").get('bracket_string_escape_mode', 'string') + if self.view.settings().get('bracket_string_escape_mode', default_mode) == "regex": + self.view.settings().set('bracket_string_escape_mode', "string") + sublime.status_message("Bracket String Escape Mode: string") + else: + self.view.settings().set('bracket_string_escape_mode', "regex") + sublime.status_message("Bracket String Escape Mode: regex") + + +class BhShowStringEscapeModeCommand(sublime_plugin.TextCommand): + """ + Shoe current string escape mode for sub brackets in strings. + """ + + def run(self, edit): + default_mode = sublime.load_settings("BracketHighlighter.sublime-settings").get('bracket_string_escape_mode', 'string') + sublime.status_message("Bracket String Escape Mode: %s" % self.view.settings().get('bracket_string_escape_mode', default_mode)) + + +class BhToggleHighVisibilityCommand(sublime_plugin.ApplicationCommand): + """ + Toggle a high visibility mode that + highlights the entire bracket extent. + """ + + def run(self): + global HIGH_VISIBILITY + HIGH_VISIBILITY = not HIGH_VISIBILITY + + +class BhToggleEnableCommand(sublime_plugin.ApplicationCommand): + """ + Toggle global enable for BracketHighlighter. + """ + + def run(self): + global GLOBAL_ENABLE + GLOBAL_ENABLE = not GLOBAL_ENABLE + + +class BhKeyCommand(sublime_plugin.WindowCommand): + """ + Command to process shortcuts, menu calls, and command palette calls. + This is how BhCore is called with different options. + """ + + def run(self, threshold=True, lines=False, adjacent=False, ignore={}, plugin={}): + # Override events + BhEventMgr.ignore_all = True + BhEventMgr.modified = False + self.bh = BhCore( + threshold, + lines, + adjacent, + ignore, + plugin, + True + ) + self.view = self.window.active_view() + sublime.set_timeout(self.execute, 100) + + def execute(self): + bh_debug("Key Event") + self.bh.match(self.view) + BhEventMgr.ignore_all = False + BhEventMgr.time = time() + + +class BhCore(object): + """ + Bracket matching class. + """ + plugin_reload = False + + def __init__(self, override_thresh=False, count_lines=False, adj_only=None, ignore={}, plugin={}, keycommand=False): + """ + Load settings and setup reload events if settings changes. + """ + + self.settings = sublime.load_settings("bh_core.sublime-settings") + self.keycommand = keycommand + if not keycommand: + self.settings.clear_on_change('reload') + self.settings.add_on_change('reload', self.setup) + self.setup(override_thresh, count_lines, adj_only, ignore, plugin) + + def setup(self, override_thresh=False, count_lines=False, adj_only=None, ignore={}, plugin={}): + """ + Initialize class settings from settings file and inputs. + """ + + # Init view params + self.last_id_view = None + self.last_id_sel = None + self.view_tracker = (None, None) + self.ignore_threshold = override_thresh + self.adj_only = adj_only if adj_only is not None else bool(self.settings.get("match_only_adjacent", False)) + self.auto_selection_threshold = int(self.settings.get("auto_selection_threshold", 10)) + self.no_multi_select_icons = bool(self.settings.get("no_multi_select_icons", False)) + self.count_lines = count_lines + self.default_string_escape_mode = str(self.settings.get('bracket_string_escape_mode', "string")) + self.show_unmatched = bool(self.settings.get("show_unmatched", True)) + + # Init bracket objects + self.bracket_types = self.settings.get("brackets", []) + self.scope_types = self.settings.get("scope_brackets", []) + + # Init selection params + self.use_selection_threshold = True + self.selection_threshold = int(self.settings.get("search_threshold", 5000)) + self.new_select = False + self.loaded_modules = set([]) + + # High Visibility options + self.hv_style = select_bracket_style(self.settings.get("high_visibility_style", "outline")) + self.hv_underline = self.hv_style & sublime.DRAW_EMPTY_AS_OVERWRITE + self.hv_color = self.settings.get("high_visibility_color", HV_RSVD_VALUES[1]) + + # Init plugin + self.plugin = None + self.transform = set([]) + if 'command' in plugin: + self.plugin = BracketPlugin(plugin, self.loaded_modules) + self.new_select = True + if 'type' in plugin: + for t in plugin["type"]: + self.transform.add(t) + + def init_bracket_regions(self): + """ + Load up styled regions for brackets to use. + """ + + self.bracket_regions = {} + styles = self.settings.get("bracket_styles", DEFAULT_STYLES) + icon_path = self.settings.get("icon_path", "Theme - Default").replace('\\', '/').strip('/') + # Make sure default and unmatched styles in styles + for key, value in DEFAULT_STYLES.items(): + if key not in styles: + styles[key] = value + continue + for k, v in value.items(): + if k not in styles[key]: + styles[key][k] = v + # Initialize styles + default_settings = styles["default"] + for k, v in styles.items(): + self.bracket_regions[k] = StyleDefinition(k, v, default_settings, icon_path) + + def is_valid_definition(self, params, language): + """ + Ensure bracket definition should be and can be loaded. + """ + + return ( + not exclude_bracket( + params.get("enabled", True), + params.get("language_filter", "blacklist"), + params.get("language_list", []), + language + ) and + params["open"] is not None and params["close"] is not None + ) + + def init_brackets(self, language): + """ + Initialize bracket match definition objects from settings file. + """ + + self.find_regex = [] + self.sub_find_regex = [] + self.index_open = {} + self.index_close = {} + self.brackets = [] + self.scopes = [] + self.view_tracker = (language, self.view.id()) + self.enabled = False + self.sels = [] + self.multi_select = False + scopes = {} + loaded_modules = self.loaded_modules.copy() + + for params in self.bracket_types: + if self.is_valid_definition(params, language): + try: + load_modules(params, loaded_modules) + entry = BracketDefinition(params) + self.brackets.append(entry) + if not entry.find_in_sub_search_only: + self.find_regex.append(params["open"]) + self.find_regex.append(params["close"]) + else: + self.find_regex.append(r"([^\s\S])") + self.find_regex.append(r"([^\s\S])") + + if entry.find_in_sub_search: + self.sub_find_regex.append(params["open"]) + self.sub_find_regex.append(params["close"]) + else: + self.sub_find_regex.append(r"([^\s\S])") + self.sub_find_regex.append(r"([^\s\S])") + except Exception, e: + bh_logging(e) + + scope_count = 0 + for params in self.scope_types: + if self.is_valid_definition(params, language): + try: + load_modules(params, loaded_modules) + entry = ScopeDefinition(params) + for x in entry.scopes: + if x not in scopes: + scopes[x] = scope_count + scope_count += 1 + self.scopes.append({"name": x, "brackets": [entry]}) + else: + self.scopes[scopes[x]]["brackets"].append(entry) + except Exception, e: + bh_logging(e) + + if len(self.brackets): + bh_debug( + "Search patterns:\n" + + "(?:%s)\n" % '|'.join(self.find_regex) + + "(?:%s)" % '|'.join(self.sub_find_regex) + ) + self.sub_pattern = re.compile("(?:%s)" % '|'.join(self.sub_find_regex), re.MULTILINE | re.IGNORECASE) + self.pattern = re.compile("(?:%s)" % '|'.join(self.find_regex), re.MULTILINE | re.IGNORECASE) + self.enabled = True + + def init_match(self): + """ + Initialize matching for the current view's syntax. + """ + + self.chars = 0 + self.lines = 0 + syntax = self.view.settings().get('syntax') + language = basename(syntax).replace('.tmLanguage', '').lower() if syntax != None else "plain text" + + if language != self.view_tracker[0] or self.view.id() != self.view_tracker[1]: + self.init_bracket_regions() + self.init_brackets(language) + else: + for r in self.bracket_regions.values(): + r.selections = [] + r.open_selections = [] + r.close_selections = [] + r.center_selections = [] + + def unique(self): + """ + Check if the current selection(s) is different from the last. + """ + + id_view = self.view.id() + id_sel = "".join([str(sel.a) for sel in self.view.sel()]) + is_unique = False + if id_view != self.last_id_view or id_sel != self.last_id_sel: + self.last_id_view = id_view + self.last_id_sel = id_sel + is_unique = True + return is_unique + + def store_sel(self, regions): + """ + Store the current selection selection to be set at the end. + """ + + if self.new_select: + for region in regions: + self.sels.append(region) + + def change_sel(self): + """ + Change the view's selections. + """ + + if self.new_select and len(self.sels) > 0: + if self.multi_select == False: + self.view.show(self.sels[0]) + self.view.sel().clear() + map(lambda x: self.view.sel().add(x), self.sels) + + def hv_highlight_color(self, b_value): + """ + High visibility highlight decesions. + """ + + color = self.hv_color + if self.hv_color == HV_RSVD_VALUES[0]: + color = self.bracket_regions["default"].color + elif self.hv_color == HV_RSVD_VALUES[1]: + color = b_value + return color + + def highlight_regions(self, name, icon_type, selections, bracket, regions): + """ + Apply the highlightes for the highlight region. + """ + + if len(selections): + self.view.add_regions( + name, + getattr(bracket, selections), + self.hv_highlight_color(bracket.color) if HIGH_VISIBILITY else bracket.color, + getattr(bracket, icon_type), + self.hv_style if HIGH_VISIBILITY else bracket.style + ) + regions.append(name) + + def highlight(self, view): + """ + Highlight all bracket regions. + """ + + for region_key in self.view.settings().get("bh_regions", []): + self.view.erase_regions(region_key) + + regions = [] + icon_type = "no_icon" + open_icon_type = "no_icon" + close_icon_type = "no_icon" + if not self.no_multi_select_icons or not self.multi_select: + icon_type = "small_icon" if self.view.line_height() < 16 else "icon" + open_icon_type = "small_open_icon" if self.view.line_height() < 16 else "open_icon" + close_icon_type = "small_close_icon" if self.view.line_height() < 16 else "close_icon" + for name, r in self.bracket_regions.items(): + self.highlight_regions("bh_" + name, icon_type, "selections", r, regions) + self.highlight_regions("bh_" + name + "_center", "no_icon", "center_selections", r, regions) + self.highlight_regions("bh_" + name + "_open", open_icon_type, "open_selections", r, regions) + self.highlight_regions("bh_" + name + "_close", close_icon_type, "close_selections", r, regions) + # Track which regions were set in the view so that they can be cleaned up later. + self.view.settings().set("bh_regions", regions) + + def get_search_bfr(self, sel): + """ + Read in the view's buffer for scanning for brackets etc. + """ + + # Determine how much of the buffer to search + view_min = 0 + view_max = self.view.size() + if not self.ignore_threshold: + left_delta = sel.a - view_min + right_delta = view_max - sel.a + limit = self.selection_threshold / 2 + rpad = limit - left_delta if left_delta < limit else 0 + lpad = limit - right_delta if right_delta < limit else 0 + llimit = limit + lpad + rlimit = limit + rpad + self.search_window = ( + sel.a - llimit if left_delta >= llimit else view_min, + sel.a + rlimit if right_delta >= rlimit else view_max + ) + else: + self.search_window = (0, view_max) + + # Search Buffer + return self.view.substr(sublime.Region(0, view_max)) + + def match(self, view, force_match=True): + """ + Preform matching brackets surround the selection(s) + """ + + if view == None: + return + if not GLOBAL_ENABLE: + for region_key in view.settings().get("bh_regions", []): + view.erase_regions(region_key) + return + + if self.keycommand: + BhCore.plugin_reload = True + + if not self.keycommand and BhCore.plugin_reload: + self.setup() + BhCore.plugin_reload = False + + # Setup views + self.view = view + self.last_view = view + num_sels = len(view.sel()) + self.multi_select = (num_sels > 1) + + if self.unique() or force_match: + # Initialize + self.init_match() + + # Nothing to search for + if not self.enabled: + return + + # Abort if selections are beyond the threshold + if self.use_selection_threshold and num_sels >= self.selection_threshold: + self.highlight(view) + return + + multi_select_count = 0 + # Process selections. + for sel in view.sel(): + bfr = self.get_search_bfr(sel) + if not self.ignore_threshold and multi_select_count >= self.auto_selection_threshold: + self.store_sel([sel]) + multi_select_count += 1 + continue + if not self.find_scopes(bfr, sel): + self.sub_search_mode = False + self.find_matches(bfr, sel) + multi_select_count += 1 + + # Highlight, focus, and display lines etc. + self.change_sel() + self.highlight(view) + if self.count_lines: + sublime.status_message('In Block: Lines ' + str(self.lines) + ', Chars ' + str(self.chars)) + + def save_incomplete_regions(self, left, right, regions): + """ + Store single incomplete brackets for highlighting. + """ + + found = left if left is not None else right + bracket = self.bracket_regions["unmatched"] + if bracket.underline: + bracket.selections += underline((found.toregion(),)) + else: + bracket.selections += [found.toregion()] + self.store_sel(regions) + + def save_regions(self, left, right, regions): + """ + Saved matched regions. Perform any special considerations for region formatting. + """ + + bracket = self.bracket_regions.get(self.bracket_style, self.bracket_regions["default"]) + lines = abs(self.view.rowcol(right.begin)[0] - self.view.rowcol(left.end)[0] + 1) + if self.count_lines: + self.chars += abs(right.begin - left.end) + self.lines += lines + if HIGH_VISIBILITY: + if lines <= 1: + if self.hv_underline: + bracket.selections += underline((sublime.Region(left.begin, right.end),)) + else: + bracket.selections += [sublime.Region(left.begin, right.end)] + else: + bracket.open_selections += [sublime.Region(left.begin)] + if self.hv_underline: + bracket.center_selections += underline((sublime.Region(left.begin + 1, right.end - 1),)) + else: + bracket.center_selections += [sublime.Region(left.begin, right.end)] + bracket.close_selections += [sublime.Region(right.begin)] + elif bracket.underline: + if lines <= 1: + bracket.selections += underline((left.toregion(), right.toregion())) + else: + bracket.open_selections += [sublime.Region(left.begin)] + bracket.close_selections += [sublime.Region(right.begin)] + if left.size(): + bracket.center_selections += underline((sublime.Region(left.begin + 1, left.end),)) + if right.size(): + bracket.center_selections += underline((sublime.Region(right.begin + 1, right.end),)) + else: + if lines <= 1: + bracket.selections += [left.toregion(), right.toregion()] + else: + bracket.open_selections += [left.toregion()] + bracket.close_selections += [right.toregion()] + self.store_sel(regions) + + def sub_search(self, sel, search_window, bfr, scope=None): + """ + Search a scope bracket match for bracekts within. + """ + + bracket = None + left, right = self.match_brackets(bfr, search_window, sel, scope) + + regions = [sublime.Region(sel.a, sel.b)] + + if left is not None and right is not None: + bracket = self.brackets[left.type] + left, right, regions = self.run_plugin(bracket.name, left, right, regions) + + # Matched brackets + if left is not None and right is not None and bracket is not None: + self.save_regions(left, right, regions) + return True + return False + + def find_scopes(self, bfr, sel): + """ + Find brackets by scope definition. + """ + + # Search buffer + left, right, bracket, sub_matched = self.match_scope_brackets(bfr, sel) + if sub_matched: + return True + regions = [sublime.Region(sel.a, sel.b)] + + if left is not None and right is not None: + left, right, regions = self.run_plugin(bracket.name, left, right, regions) + if left is None and right is None: + self.store_sel(regions) + return True + + if left is not None and right is not None: + self.save_regions(left, right, regions) + return True + elif (left is not None or right is not None) and self.show_invalid: + self.save_incomplete_regions(left, right, regions) + return True + return False + + def find_matches(self, bfr, sel): + """ + Find bracket matches + """ + + bracket = None + left, right = self.match_brackets(bfr, self.search_window, sel) + + regions = [sublime.Region(sel.a, sel.b)] + + if left is not None and right is not None: + bracket = self.brackets[left.type] + left, right, regions = self.run_plugin(bracket.name, left, right, regions) + + # Matched brackets + if left is not None and right is not None and bracket is not None: + self.save_regions(left, right, regions) + + # Unmatched brackets + elif (left is not None or right is not None) and self.show_unmatched: + self.save_incomplete_regions(left, right, regions) + + else: + self.store_sel(regions) + + def escaped(self, pt, ignore_string_escape, scope): + """ + Check if sub bracket in string scope is escaped. + """ + + if not ignore_string_escape: + return False + if scope and scope.startswith("string"): + return self.string_escaped(pt) + return False + + def string_escaped(self, pt): + """ + Check if bracket is follows escaping characters. + Account for if in string or regex string scope. + """ + + escaped = False + start = pt - 1 + first = False + if self.view.settings().get("bracket_string_escape_mode", self.default_string_escape_mode) == "string": + first = True + while self.view.substr(start) == "\\": + if first: + first = False + else: + escaped = False if escaped else True + start -= 1 + return escaped + + def is_illegal_scope(self, pt, bracket_id, scope=None): + """ + Check if scope at pt X should be ignored. + """ + + bracket = self.brackets[bracket_id] + if self.sub_search_mode and not bracket.find_in_sub_search: + return True + illegal_scope = False + # Scope sent in, so we must be scanning whatever this scope is + if scope != None: + if self.escaped(pt, bracket.ignore_string_escape, scope): + illegal_scope = True + return illegal_scope + # for exception in bracket.scope_exclude_exceptions: + elif len(bracket.scope_exclude_exceptions) and self.view.match_selector(pt, ", ".join(bracket.scope_exclude_exceptions)): + pass + elif len(bracket.scope_exclude) and self.view.match_selector(pt, ", ".join(bracket.scope_exclude)): + illegal_scope = True + return illegal_scope + + def compare(self, first, second, bfr, scope_bracket=False): + """ + Compare brackets. This function allows bracket plugins to add aditional logic. + """ + + if scope_bracket: + match = first is not None and second is not None + else: + match = first.type == second.type + if match: + bracket = self.scopes[first.scope]["brackets"][first.type] if scope_bracket else self.brackets[first.type] + try: + if bracket.compare is not None and match: + match = bracket.compare( + bracket.name, + BracketRegion(first.begin, first.end), + BracketRegion(second.begin, second.end), + bfr + ) + except: + bh_logging("Plugin Compare Error:\n%s" % str(traceback.format_exc())) + return match + + def post_match(self, left, right, center, bfr, scope_bracket=False): + """ + Peform special logic after a match has been made. + This function allows bracket plugins to add aditional logic. + """ + + if left is not None: + if scope_bracket: + bracket = self.scopes[left.scope]["brackets"][left.type] + bracket_scope = left.scope + else: + bracket = self.brackets[left.type] + bracket_type = left.type + elif right is not None: + if scope_bracket: + bracket = self.scopes[right.scope]["brackets"][right.type] + bracket_scope = right.scope + else: + bracket = self.brackets[right.type] + bracket_type = right.type + else: + return left, right + + self.bracket_style = bracket.style + + if bracket.post_match is not None: + try: + lbracket, rbracket, self.bracket_style = bracket.post_match( + self.view, + bracket.name, + bracket.style, + BracketRegion(left.begin, left.end) if left is not None else None, + BracketRegion(right.begin, right.end) if right is not None else None, + center, + bfr, + self.search_window + ) + + if scope_bracket: + left = ScopeEntry(lbracket.begin, lbracket.end, bracket_scope, bracket_type) if lbracket is not None else None + right = ScopeEntry(rbracket.begin, rbracket.end, bracket_scope, bracket_type) if rbracket is not None else None + else: + left = BracketEntry(lbracket.begin, lbracket.end, bracket_type) if lbracket is not None else None + right = BracketEntry(rbracket.begin, rbracket.end, bracket_type) if rbracket is not None else None + except: + bh_logging("Plugin Post Match Error:\n%s" % str(traceback.format_exc())) + return left, right + + def run_plugin(self, name, left, right, regions): + """ + Run a bracket plugin. + """ + + lbracket = BracketRegion(left.begin, left.end) + rbracket = BracketRegion(right.begin, right.end) + + if ( + ("__all__" in self.transform or name in self.transform) and + self.plugin != None and + self.plugin.is_enabled() + ): + lbracket, rbracket, regions = self.plugin.run_command(self.view, name, lbracket, rbracket, regions) + left = left.move(lbracket.begin, lbracket.end) if lbracket is not None else None + right = right.move(rbracket.begin, rbracket.end) if rbracket is not None else None + return left, right, regions + + def match_scope_brackets(self, bfr, sel): + """ + See if scope should be searched, and then check + endcaps to determine if valid scope bracket. + """ + + center = sel.a + left = None + right = None + scope_count = 0 + before_center = center - 1 + bracket_count = 0 + partial_find = None + max_size = self.view.size() - 1 + selected_scope = None + bracket = None + + # Cannot be inside a bracket pair if cursor is at zero + if center == 0: + return left, right, selected_scope, False + + # Identify if the cursor is in a scope with bracket definitions + for s in self.scopes: + scope = s["name"] + extent = None + exceed_limit = False + if self.view.match_selector(center, scope) and self.view.match_selector(before_center, scope): + extent = self.view.extract_scope(center) + while not exceed_limit and extent.begin() != 0: + if self.view.match_selector(extent.begin() - 1, scope): + extent = extent.cover(self.view.extract_scope(extent.begin() - 1)) + if extent.begin() < self.search_window[0] or extent.end() > self.search_window[1]: + extent = None + exceed_limit = True + else: + break + while not exceed_limit and extent.end() != max_size: + if self.view.match_selector(extent.end(), scope): + extent = extent.cover(self.view.extract_scope(extent.end())) + if extent.begin() < self.search_window[0] or extent.end() > self.search_window[1]: + extent = None + exceed_limit = True + else: + break + + if extent is None: + scope_count += 1 + continue + + # Search the bracket patterns of this scope + # to determine if this scope matches the rules. + bracket_count = 0 + scope_bfr = bfr[extent.begin():extent.end()] + for b in s["brackets"]: + m = b.open.search(scope_bfr) + if m and m.group(1): + left = ScopeEntry(extent.begin() + m.start(1), extent.begin() + m.end(1), scope_count, bracket_count) + m = b.close.search(scope_bfr) + if m and m.group(1): + right = ScopeEntry(extent.begin() + m.start(1), extent.begin() + m.end(1), scope_count, bracket_count) + if not self.compare(left, right, bfr, scope_bracket=True): + left, right = None, None + # Track partial matches. If a full match isn't found, + # return the first partial match at the end. + if partial_find is None and bool(left) != bool(right): + partial_find = (left, right) + left = None + right = None + if left and right: + break + bracket_count += 1 + if left and right: + break + scope_count += 1 + + # Full match not found. Return partial match (if any). + if (left is None or right is None) and partial_find is not None: + left, right = partial_find[0], partial_find[1] + + # Make sure cursor in highlighted sub group + if (left and center <= left.begin) or (right and center >= right.end): + left, right = None, None + + if left is not None: + selected_scope = self.scopes[left.scope]["name"] + elif right is not None: + selected_scope = self.scopes[right.scope]["name"] + + if left is not None and right is not None: + bracket = self.scopes[left.scope]["brackets"][left.type] + if bracket.sub_search: + self.sub_search_mode = True + if self.sub_search(sel, (left.begin, right.end), bfr, scope): + return left, right, self.brackets[left.type], True + elif bracket.sub_search_only: + left, right, bracket = None, None, None + + if self.adj_only: + left, right = self.adjacent_check(left, right, center) + + left, right = self.post_match(left, right, center, bfr, scope_bracket=True) + return left, right, bracket, False + + def match_brackets(self, bfr, window, sel, scope=None): + """ + Regex bracket matching. + """ + + center = sel.a + left = None + right = None + stack = [] + pattern = self.pattern if not self.sub_search_mode else self.sub_pattern + bsearch = BracketSearch(bfr, window, center, pattern, self.is_illegal_scope, scope) + for o in bsearch.get_open(BracketSearchSide.left): + if len(stack) and bsearch.is_done(BracektSearchType.closing): + if self.compare(o, stack[-1], bfr): + stack.pop() + continue + for c in bsearch.get_close(BracketSearchSide.left): + if o.end <= c.begin: + stack.append(c) + continue + elif len(stack): + bsearch.remember(BracektSearchType.closing) + break + + if len(stack): + b = stack.pop() + if self.compare(o, b, bfr): + continue + else: + left = o + break + + bsearch.reset_end_state() + stack = [] + + # Grab each closest closing right side bracket and attempt to match it. + # If the closing bracket cannot be matched, select it. + for c in bsearch.get_close(BracketSearchSide.right): + if len(stack) and bsearch.is_done(BracektSearchType.opening): + if self.compare(stack[-1], c, bfr): + stack.pop() + continue + for o in bsearch.get_open(BracketSearchSide.right): + if o.end <= c.begin: + stack.append(o) + continue + else: + bsearch.remember(BracektSearchType.opening) + break + + if len(stack): + b = stack.pop() + if self.compare(b, c, bfr): + continue + else: + if left is None or self.compare(left, c, bfr): + right = c + break + + if self.adj_only: + left, right = self.adjacent_check(left, right, center) + + return self.post_match(left, right, center, bfr) + + def adjacent_check(self, left, right, center): + if left and right: + if left.end < center < right.begin: + left, right = None, None + elif (left and left.end < center) or (right and center < right.begin): + left, right = None, None + return left, right + +bh_match = BhCore().match +bh_debug("Match object loaded.") + + +class BhListenerCommand(sublime_plugin.EventListener): + """ + Manage when to kick off bracket matching. + Try and reduce redundant requests by letting the + background thread ensure certain needed match occurs + """ + + def on_load(self, view): + """ + Search brackets on view load. + """ + + if self.ignore_event(view): + return + BhEventMgr.type = BH_MATCH_TYPE_SELECTION + sublime.set_timeout(bh_run, 0) + + def on_modified(self, view): + """ + Update highlighted brackets when the text changes. + """ + + if self.ignore_event(view): + return + BhEventMgr.type = BH_MATCH_TYPE_EDIT + BhEventMgr.modified = True + BhEventMgr.time = time() + + def on_activated(self, view): + """ + Highlight brackets when the view gains focus again. + """ + + if self.ignore_event(view): + return + BhEventMgr.type = BH_MATCH_TYPE_SELECTION + sublime.set_timeout(bh_run, 0) + + def on_selection_modified(self, view): + """ + Highlight brackets when the selections change. + """ + + if self.ignore_event(view): + return + if BhEventMgr.type != BH_MATCH_TYPE_EDIT: + BhEventMgr.type = BH_MATCH_TYPE_SELECTION + now = time() + if now - BhEventMgr.time > BhEventMgr.wait_time: + sublime.set_timeout(bh_run, 0) + else: + BhEventMgr.modified = True + BhEventMgr.time = now + + def ignore_event(self, view): + """ + Ignore request to highlight if the view is a widget, + or if it is too soon to accept an event. + """ + + return (view.settings().get('is_widget') or BhEventMgr.ignore_all) + + +def bh_run(): + """ + Kick off matching of brackets + """ + + BhEventMgr.modified = False + window = sublime.active_window() + view = window.active_view() if window != None else None + BhEventMgr.ignore_all = True + bh_match(view, True if BhEventMgr.type == BH_MATCH_TYPE_EDIT else False) + BhEventMgr.ignore_all = False + BhEventMgr.time = time() + + +def bh_loop(): + """ + Start thread that will ensure highlighting happens after a barage of events + Initial highlight is instant, but subsequent events in close succession will + be ignored and then accounted for with one match by this thread + """ + + while not BhThreadMgr.restart: + if BhEventMgr.modified == True and time() - BhEventMgr.time > BhEventMgr.wait_time: + sublime.set_timeout(bh_run, 0) + sleep(0.5) + + if BhThreadMgr.restart: + BhThreadMgr.restart = False + sublime.set_timeout(lambda: thread.start_new_thread(bh_loop, ()), 0) + +if not 'running_bh_loop' in globals(): + running_bh_loop = True + thread.start_new_thread(bh_loop, ()) + bh_debug("Starting Thread") +else: + bh_debug("Restarting Thread") + BhThreadMgr.restart = True diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_core.sublime-settings b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_core.sublime-settings new file mode 100644 index 0000000..de60409 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_core.sublime-settings @@ -0,0 +1,372 @@ +{ + //Debug logging + "debug_enable": false, + + // Path to find icons at + "icon_path": "BracketHighlighter/icons", + + // When only either the left or right bracket can be found + // this defines if the unmatched bracket should be shown. + "show_unmatched" : true, + + // High visibilty style and color for high visibility mode + // (solid|outline|underline) + "high_visibility_style": "outline", + // (scope|__default__|__bracket__) + "high_visibility_color": "__bracket__", + + // Match brackets only when the cursor is touching the inside of the bracket + "match_only_adjacent": false, + + // Character threshold to search + "search_threshold": 5000, + + // Set mode for string escapes to ignore (regex|string) + "bracket_string_escape_mode": "string", + + // Set max number of multi-select brackets that will be searched automatically + "auto_selection_threshold" : 10, + + // Disable gutter icons when doing multi-select + "no_multi_select_icons": false, + + // Rules that define the finding and matching of brackets + // that are contained in a common scope. + // Useful for bracket pairs that are the same but + // share a common scope. Brackets are found by + // Finding the extent of the scope and using regex + // to look at the beginning and end to identify bracket. + // Use only if they cannot be targeted with traditional bracket + // rules. + "scope_brackets": [ + // Quotes + { + "name": "py_single_quote", + "open": "u?r?((?:'')?')", + "close": "((?:'')?')", + "style": "single_quote", + "scopes": ["string"], + "language_filter": "whitelist", + "language_list": ["Python"], + "sub_bracket_search": "true", + "enabled": true + }, + { + "name": "py_double_quote", + "open": "u?r?((?:\"\")?\")", + "close": "((?:\"\")?\")", + "style": "double_quote", + "scopes": ["string"], + "language_filter": "whitelist", + "language_list": ["Python"], + "sub_bracket_search": "true", + "enabled": true + }, + { + "name": "single_quote", + "open": "(')", + "close": "(')", + "style": "single_quote", + "scopes": ["string"], + "language_filter": "blacklist", + "language_list": ["Plain text"], + "sub_bracket_search": "true", + "enabled": true + }, + { + "name": "double_quote", + "open": "(\")", + "close": "(\")", + "style": "double_quote", + "scopes": ["string"], + "language_filter": "blacklist", + "language_list": ["Plain text"], + "sub_bracket_search": "true", + "enabled": true + }, + // Regex for different Languages + { + "name": "jsregex", + "open": " *(/)", + "close": "(/)[igm]*", + "style": "regex", + "scopes": ["string"], + "language_filter": "whitelist", + "language_list": ["JavaScript"], + "sub_bracket_search": "true", + "enabled": true + }, + { + "name": "perlregex", + "open": "(?:m|s|tr)(.|\n)", + "close": "(.|\n)(?:[igmos]*)", + "style": "regex", + "scopes": ["string.regexp"], + "language_filter": "whitelist", + "language_list": ["Perl"], + "sub_bracket_search": "true", + "enabled": true + }, + { + "name": "rubyregex", + "open": " *(/)", + "close": "(/)[imxo]*", + "style": "regex", + "scopes": ["string"], + "language_filter": "whitelist", + "language_list": ["Ruby"], + "sub_bracket_search": "true", + "enabled": true + }, + // Markdown + { + "name": "mditalic", + "open": "(\\*|_)", + "close": "(\\*|_)", + "style": "default", + "scopes": ["markup.italic"], + "language_filter": "whitelist", + "language_list": ["Markdown"], + "sub_bracket_search": "true", + "enabled": true + }, + { + "name": "mdbold", + "open": "(\\*\\*|__)", + "close": "(\\*\\*|__)", + "style": "default", + "scopes": ["markup.bold"], + "language_filter": "whitelist", + "language_list": ["Markdown"], + "sub_bracket_search": "true", + "enabled": true + } + ], + + // Rule definitions for finding and matching brackets. + // Brackets are found by using regex and can use scope + // qualifiers exclude certain matches. + // Once all matches are found, the closest pair surrounding + // the cursor are selected. + "brackets": [ + // Basic brackets + { + "name": "curly", + "open": "(\\{)", + "close": "(\\})", + "style": "curly", + "scope_exclude": ["string", "comment"], + "scope_exclude_exceptions": ["string.other.math.block.environment.latex"], + "language_filter": "blacklist", + "language_list": ["Plain text"], + "find_in_sub_search": "true", + "ignore_string_escape": true, + "enabled": true + }, + { + "name": "round", + "open": "(\\()", + "close": "(\\))", + "style": "round", + "scope_exclude_exceptions": ["string.other.math.block.environment.latex"], + "scope_exclude": ["string", "comment"], + "language_filter": "blacklist", + "language_list": ["Plain text"], + "find_in_sub_search": "true", + "ignore_string_escape": true, + "enabled": true + }, + { + "name": "square", + "open": "(\\[)", + "close": "(\\])", + "style": "square", + "scope_exclude": ["string", "comment"], + "scope_exclude_exceptions": ["string.other.math.block.environment.latex"], + "language_filter": "blacklist", + "language_list": ["Plain text"], + "find_in_sub_search": "true", + "ignore_string_escape": true, + "enabled": true + }, + // HTML + { + "name": "html", + "open": "(<)(?=[\\w\\:\\-]+(?:(?:\\s+[\\w\\-:]+(?:\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^>\\s]+))?)*)\\s*\\/?>|\\/[\\w\\:\\-]+[^>]*>)", + "close": "(?<=<)(?:[\\w\\:\\-]+(?:(?:\\s+[\\w\\-:]+(?:\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^>\\s]+))?)*)\\s*\\/?|\\/[\\w\\:\\-]+[^>]*)(>)", + "style": "tag", + "scope_exclude": ["string", "comment"], + "language_filter": "whitelist", + "language_list": ["HTML", "HTML 5", "XML", "PHP"], + "plugin_library": "bh_modules.tags", + "find_in_sub_search": "only", + "enabled": false + }, + // CFML + { + "name": "cfml", + "open": "(<)(?=[\\w\\:\\-]+(?:(?:\\s+[\\w\\-\\.:]+(?:\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^>\\s]+))?)*|(?:(?<=cfif)|(?<=cfelseif))[^>]+)\\s*\\/?>|\\/[\\w\\:\\-]+[^>]*>)", + "close": "(?<=<)(?:[\\w\\:\\-]+(?:(?:\\s+[\\w\\-\\.:]+(?:\\s*=\\s*(?:\"[^\"]*\"|'[^']*'|[^>\\s]+))?)*|(?:(?<=cfif)|(?<=cfelseif))[^>]+)\\s*\\/?|\\/[\\w\\:\\-]+[^>]*)(>)", + "style": "tag", + "scope_exclude": ["string", "comment"], + "language_filter": "whitelist", + "language_list": ["HTML+CFML", "ColdFusion", "ColdFusionCFC"], + "plugin_library": "bh_modules.tags", + "find_in_sub_search": "only", + "enabled": false + }, + // Angle + { + "name": "angle", + "open": "(<)", + "close": "(>)", + "style": "angle", + "scope_exclude": ["string", "comment", "keyword.operator"], + "language_filter": "whitelist", + "language_list": ["HTML", "HTML 5", "XML", "PHP", "HTML+CFML", "ColdFusion", "ColdFusionCFC"], + "plugin_library": "bh_modules.tags", + "enabled": true + }, + // CSSedit groups + { + "name": "cssedit_groups", + "open": "(/\\* *@group .*\\*/)", + "close": "(/\\* *@end *\\*/)", + "style": "default", + "scope_exclude": [], + "language_filter": "whitelist", + "language_list": ["CSS"], + "enabled": true + }, + // Ruby conditional statements + { + "name": "ruby", + "open": "(^\\s*\\b(?:if|case|until|unless|while|begin|class|module|def\\b\\s*[a-zA-Z_\\d]+)|\\bdo)\\b", + "close": "\\b(end)\\b", + "style": "default", + "scope_exclude": ["string", "comment"], + "plugin_library": "bh_modules.rubykeywords", + "language_filter": "whitelist", + "language_list": ["Ruby", "Ruby on Rails", "HTML (Rails)"], + "enabled": true + }, + // C/C++ compile switches + { + "name": "c_compile_switch", + "open": "(\\#(?:if|ifdef|ifndef))\\b", + "close": "(\\#endif)\\b", + "style": "default", + "scope_exclude": ["string", "comment"], + "language_filter": "whitelist", + "language_list": ["C++", "C", "Objective-C"], + "enabled": true + }, + // PHP conditional keywords + { + "name": "php_keywords", + "open": "(?:^\\s*|<\\?(?:php)?\\s*)?\\b(if|foreach|for|while|switch)\\b(?=.*:\\s*(?:\\?>\\s*)?$)", + "close": "(?:^\\s*|<\\?(?:php)?\\s*)?\\b(endif|endfor|endforeach|endwhile|endswitch)\\b(?=\\s*;\\s*(?:\\?>\\s*)?$)", + "style": "default", + "language_filter": "whitelist", + "scope_exclude": ["string", "comment"], + "plugin_library": "bh_modules.phpkeywords", + "language_list": ["HTML", "HTML 5", "XML", "PHP", "HTML+CFML", "ColdFusion", "ColdFusionCFC"], + "enabled": true + }, + // Erlang conditional statements + { + "name": "erlang", + "open": "\\s*(\\b(?:if|case|begin|try|fun(?=\\s*\\()|receive)\\b)", + "close": "\\b(end)\\b", + "style": "default", + "scope_exclude": ["string", "comment"], + "language_filter": "whitelist", + "language_list": ["Erlang", "HTML (Erlang)"], + "enabled": true + } + ], + + // Define region highlight styles + "bracket_styles": { + // "default" and "unmatched" styles are special + // styles. If they are not defined here, + // they will be generated internally with + // internal defaults. + + // "default" style defines attributes that + // will be used for any style that does not + // explicitly define that attribute. So if + // a style does not define a color, it will + // use the color from the "default" style. + "default": { + "icon": "dot", + "color": "brackethighlighter.default", + "style": "underline" + }, + + // This particular style is used to highlight + // unmatched bracekt pairs. It is a special + // style. + "unmatched": { + "icon": "question", + // "color": "brackethighlighter.unmatched", + "style": "outline" + }, + // User defined region styles + "curly": { + "icon": "curly_bracket" + // "color": "brackethighlighter.curly", + // "style": "underline" + }, + "round": { + "icon": "round_bracket" + // "color": "brackethighlighter.round", + // "style": "underline" + }, + "square": { + "icon": "square_bracket" + // "color": "brackethighlighter.square", + // "style": "underline" + }, + "angle": { + "icon": "angle_bracket" + // "color": "brackethighlighter.angle", + // "style": "underline" + }, + "tag": { + "icon": "tag", + // "color": "brackethighlighter.tag", + "style": "outline" + }, + "single_quote": { + "icon": "single_quote" + // "color": "brackethighlighter.quote", + // "style": "underline" + }, + "double_quote": { + "icon": "double_quote" + // "color": "brackethighlighter.quote", + // "style": "underline" + }, + "regex": { + "icon": "regex" + // "color": "brackethighlighter.quote", + // "style": "underline" + } + }, + + /* Plugin settings */ + + // Style to use for matched tags + "tag_style": "tag", + + // Scopes to exclude from tag searches + "tag_scope_exclude": ["string", "comment"], + + // Determine which style of tag-matching to use in which syntax + "tag_mode": { + "xhtml": ["XML"], + "html": ["HTML", "HTML 5", "PHP"], + "cfml": ["HTML+CFML", "ColdFusion", "ColdFusionCFC"] + } +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/__init__.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/__init__.py @@ -0,0 +1 @@ + diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/bracketremove.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/bracketremove.py new file mode 100644 index 0000000..3e62d38 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/bracketremove.py @@ -0,0 +1,41 @@ +import bh_plugin +import re +import sublime + + +class BracketRemove(bh_plugin.BracketPluginCommand): + def decrease_indent_level(self, edit, row_first, row_last): + tab_size = self.view.settings().get("tab_size", 4) + indents = re.compile(r"^(?:\t| {%d}| *)((?:\t| {%d}| )*)([\s\S]*)" % (tab_size, tab_size)) + if not self.single_line: + for x in reversed(range(row_first, row_last + 1)): + line = self.view.full_line(self.view.text_point(x, 0)) + text = self.view.substr(line) + m = indents.match(text) + if m: + self.view.replace(edit, line, m.group(1) + m.group(2)) + + def run(self, edit, name, remove_content=False, remove_indent=False, remove_block=False): + if remove_content: + self.view.replace(edit, sublime.Region(self.left.begin, self.right.end), "") + else: + row_first = self.view.rowcol(self.left.end)[0] + 1 + row_last = self.view.rowcol(self.right.begin)[0] - 1 + self.single_line = not row_first <= row_last + if remove_block and not self.single_line: + self.view.replace(edit, self.view.full_line(self.right.toregion()), "") + else: + self.view.replace(edit, self.right.toregion(), "") + if remove_indent: + self.decrease_indent_level(edit, row_first, row_last) + if remove_block and not self.single_line: + self.view.replace(edit, self.view.full_line(self.left.toregion()), "") + else: + self.view.replace(edit, self.left.toregion(), "") + + self.left = None + self.right = None + + +def plugin(): + return BracketRemove diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/bracketselect.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/bracketselect.py new file mode 100644 index 0000000..99e1db0 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/bracketselect.py @@ -0,0 +1,33 @@ +import bh_plugin +import sublime + +DEFAULT_TAGS = ["cfml", "html", "angle"] + + +class SelectBracket(bh_plugin.BracketPluginCommand): + def run(self, edit, name, select='', tags=DEFAULT_TAGS): + left, right = self.left, self.right + first, last = left.end, right.begin + if select == 'left': + if name in tags and left.size() > 1: + first, last = left.begin + 1, left.begin + 1 + else: + first, last = left.end, left.end + elif select == 'right': + if left.end != right.end: + if name in tags and left.size() > 1: + first, last = right.begin + 1, right.begin + 1 + else: + first, last = right.begin, right.begin + else: + # There is no second bracket, so just select the first + if name in tags and left.size() > 1: + first, last = left.begin + 1, left.begin + 1 + else: + first, last = right.end, right.end + + self.selection = [sublime.Region(first, last)] + + +def plugin(): + return SelectBracket diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/foldbracket.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/foldbracket.py new file mode 100644 index 0000000..adead9b --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/foldbracket.py @@ -0,0 +1,16 @@ +import bh_plugin +import sublime + + +class FoldBrackets(bh_plugin.BracketPluginCommand): + def run(self, edit, name): + content = sublime.Region(self.left.end, self.right.begin) + new_content = [content] + if content.size > 0: + if self.view.fold(content) == False: + new_content = self.view.unfold(content) + self.selection = new_content + + +def plugin(): + return FoldBrackets diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/phpkeywords.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/phpkeywords.py new file mode 100644 index 0000000..cf49e3a --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/phpkeywords.py @@ -0,0 +1,2 @@ +def compare(name, first, second, bfr): + return "end" + bfr[first.begin:first.end].lower() == bfr[second.begin:second.end].lower() diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/rubykeywords.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/rubykeywords.py new file mode 100644 index 0000000..e4684f3 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/rubykeywords.py @@ -0,0 +1,12 @@ +import re + + +def post_match(view, name, style, first, second, center, bfr, threshold): + if first is not None: + # Strip whitespace from the beginning of first bracket + open_bracket = bfr[first.begin:first.end] + if open_bracket != "do": + m = re.match(r"^(\s*\b)[\w\W]*", open_bracket) + if m: + first = first.move(first.begin + m.end(1), first.end) + return first, second, style diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/swapbrackets.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/swapbrackets.py new file mode 100644 index 0000000..9ed3abe --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/swapbrackets.py @@ -0,0 +1,14 @@ +import sublime +from bh_plugin import ImportModule as ImpMod +BracketRemove = ImpMod.import_from("bh_modules.bracketremove", "BracketRemove") + + +class SwapBrackets(BracketRemove): + def run(self, edit, name, remove_content=False, remove_indent=False, remove_block=False): + offset = self.left.toregion().size() + self.selection = [sublime.Region(self.left.begin, self.right.begin - offset)] + super(SwapBrackets, self).run(edit, name) + + +def plugin(): + return SwapBrackets diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/swapquotes.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/swapquotes.py new file mode 100644 index 0000000..42793b7 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/swapquotes.py @@ -0,0 +1,46 @@ +import bh_plugin +import sublime + + +class SwapQuotes(bh_plugin.BracketPluginCommand): + def escaped(self, idx): + view = self.view + escaped = False + while idx >= 0 and view.substr(idx) == '\\': + escaped = ~escaped + idx -= 1 + return escaped + + def run(self, edit, name): + view = self.view + quote = view.substr(self.left.begin) + if quote != "'" and quote != '"': + return + new = "'" if (quote == '"') else '"' + old = quote + begin = self.left.end + end = self.right.begin + content_end = self.right.begin + + view.replace(edit, self.left.toregion(), view.substr(self.left.toregion()).replace(old, new)) + view.replace(edit, self.right.toregion(), view.substr(self.right.toregion()).replace(old, new)) + + offset = 0 + while begin < end + offset: + char = view.substr(begin) + if char == old and self.escaped(begin - 1): + view.replace(edit, sublime.Region(begin - 1, begin), '') + offset -= 1 + content_end -= 1 + elif char == new and not self.escaped(begin - 1): + view.insert(edit, begin, "\\") + offset += 1 + content_end += 1 + begin += 1 + + self.right = self.right.move(content_end, end + offset) + self.selection = [sublime.Region(content_end)] + + +def plugin(): + return SwapQuotes diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/tagattrselect.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/tagattrselect.py new file mode 100644 index 0000000..a4319ec --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/tagattrselect.py @@ -0,0 +1,54 @@ +import bh_plugin + + +class SelectAttr(bh_plugin.BracketPluginCommand): + def run(self, edit, name, direction='right'): + if self.left.size() <= 1: + return + tag_name = r'[\w\:\-]+' + attr_name = r'''([\w\-\.:]+)(?:\s*=\s*(?:(?:"((?:\.|[^"])*)")|(?:'((?:\.|[^'])*)')|([^>\s]+)))?''' + tname = self.view.find(tag_name, self.left.begin) + current = self.selection[0].b + region = self.view.find(attr_name, tname.b) + selection = self.selection + + if direction == 'left': + last = None + + # Keep track of last attr + if region != None and current <= region.b and region.b < self.left.end: + last = region + + while region != None and region.b < self.left.end: + # Select attribute until you have closest to the left of selection + if current > region.b: + selection = [region] + last = None + # Update last attr + elif last != None: + last = region + region = self.view.find(attr_name, region.b) + # Wrap right + if last != None: + selection = [last] + else: + first = None + # Keep track of first attr + if region != None and region.b < self.left.end: + first = region + + while region != None and region.b < self.left.end: + # Select closest attr to the right of the selection + if current < region.b: + selection = [region] + first = None + break + region = self.view.find(attr_name, region.b) + # Wrap left + if first != None: + selection = [first] + self.selection = selection + + +def plugin(): + return SelectAttr diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/tagnameselect.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/tagnameselect.py new file mode 100644 index 0000000..77b8fe9 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/tagnameselect.py @@ -0,0 +1,14 @@ +import bh_plugin + + +class TagNameSelect(bh_plugin.BracketPluginCommand): + def run(self, edit, name): + if self.left.size() > 1: + tag_name = '[\w\:\-]+' + region1 = self.view.find(tag_name, self.left.begin) + region2 = self.view.find(tag_name, self.right.begin) + self.selection = [region1, region2] + + +def plugin(): + return TagNameSelect diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/tags.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/tags.py new file mode 100644 index 0000000..47c2972 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_modules/tags.py @@ -0,0 +1,243 @@ +import re +from collections import namedtuple +import sublime +from os.path import basename + +FLAGS = re.MULTILINE | re.IGNORECASE +HTML_START = re.compile(r'''<([\w\:\-]+)((?:\s+[\w\-:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^>\s]+))?)*)\s*(\/?)>''', FLAGS) +CFML_START = re.compile(r'''<([\w\:\-]+)((?:\s+[\w\-\.:]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^>\s]+))?)*|(?:(?<=cfif)|(?<=cfelseif))[^>]+)\s*(\/?)>''', FLAGS) +START_TAG = { + "html": HTML_START, + "xhtml": HTML_START, + "cfml": CFML_START +} +END_TAG = re.compile(r'<\/([\w\:\-]+)[^>]*>', FLAGS) + +self_closing_tags = set("colgroup dd dt li options p td tfoot th thead tr".split()) +single_tags = set("area base basefont br col frame hr img input isindex link meta param embed".split()) + + +class TagEntry(namedtuple('TagEntry', ['begin', 'end', 'name', 'self_closing', 'single'], verbose=False)): + def move(self, begin, end): + return self._replace(begin=begin, end=end) + + +def compare_languge(language, lang_list): + found = False + for l in lang_list: + if language == l.lower(): + found = True + break + return found + + +def get_tag_mode(view, tag_mode_config): + default_mode = None + syntax = view.settings().get('syntax') + language = basename(syntax).replace('.tmLanguage', '').lower() if syntax != None else "plain text" + for mode in ["html", "xhtml", "cfml"]: + if compare_languge(language, tag_mode_config.get(mode, [])): + return mode + return default_mode + + +def post_match(view, name, style, first, second, center, bfr, threshold): + left, right = first, second + threshold = [0, len(bfr)] if threshold is None else threshold + tag_settings = sublime.load_settings("bh_core.sublime-settings") + tag_mode = get_tag_mode(view, tag_settings.get("tag_mode", {})) + tag_style = tag_settings.get("tag_style", "angle") + bracket_style = style + + if first is not None and tag_mode is not None: + matcher = TagMatch(view, bfr, threshold, first, second, center, tag_mode) + left, right = matcher.match() + if not matcher.no_tag: + bracket_style = tag_style + + return left, right, bracket_style + + +class TagSearch(object): + def __init__(self, view, bfr, window, center, pattern, match_type): + self.start = window[0] + self.end = window[1] + self.center = center + self.pattern = pattern + self.match_type = match_type + self.bfr = bfr + self.prev_match = None + self.return_prev = False + self.done = False + self.view = view + self.scope_exclude = sublime.load_settings("bh_core.sublime-settings").get("tag_scope_exclude") + + def scope_check(self, pt): + illegal_scope = False + for exclude in self.scope_exclude: + illegal_scope |= bool(self.view.score_selector(pt, exclude)) + return illegal_scope + + def reset_end_state(self): + self.done = False + self.prev_match = None + self.return_prev = False + + def remember(self): + self.return_prev = True + self.done = False + + def get_tags(self, bracket_code): + if self.done: + return + if self.return_prev: + self.return_prev = False + yield self.prev_match + for m in self.pattern.finditer(self.bfr, self.start, self.end): + name = m.group(1).lower() + if not self.match_type: + single = bool(m.group(3) != "" or name in single_tags) + self_closing = name in self_closing_tags or name.startswith("cf") + else: + single = False + self_closing = False + start = m.start(0) + end = m.end(0) + if not self.scope_check(start): + self.prev_match = TagEntry(start, end, name, self_closing, single) + self.start = end + yield self.prev_match + self.done = True + + +class TagMatch(object): + def __init__(self, view, bfr, threshold, first, second, center, mode): + self.view = view + self.bfr = bfr + self.mode = mode + tag, tag_type, tag_end = self.get_first_tag(first[0]) + self.left, self.right = None, None + self.window = None + self.no_tag = False + if tag and first[0] < center < tag_end: + if tag.single: + self.left = tag + self.right = tag + else: + if tag_type == "open": + self.left = tag + self.window = (tag_end, len(bfr) if threshold is None else threshold[1]) + else: + self.right = tag + self.window = (0 if threshold is None else threshold[0], first[0]) + else: + self.left = first + self.right = second + self.no_tag = True + + def get_first_tag(self, offset): + tag = None + tag_type = None + self_closing = False + single = False + m = START_TAG[self.mode].match(self.bfr[offset:]) + end = None + if m: + name = m.group(1).lower() + single = bool(m.group(3) != "" or name in single_tags) + if self.mode == "html": + self_closing = name in self_closing_tags + elif self.mode == "cfml": + self_closing = name in self_closing_tags or name.startswith("cf") + start = m.start(0) + offset + end = m.end(0) + offset + tag = TagEntry(start, end, name, self_closing, single) + tag_type = "open" + self.center = end + else: + m = END_TAG.match(self.bfr[offset:]) + if m: + name = m.group(1).lower() + start = m.start(0) + offset + end = m.end(0) + offset + tag = TagEntry(start, end, name, self_closing, single) + tag_type = "close" + self.center = offset + return tag, tag_type, end + + def compare_tags(self, left, right): + return left.name == right.name + + def resolve_self_closing(self, stack, c): + found_tag = None + b = stack[-1] + if self.compare_tags(b, c): + found_tag = b + stack.pop() + else: + while b is not None and b.self_closing: + stack.pop() + if len(stack): + b = stack[-1] + if self.compare_tags(b, c): + found_tag = b + stack.pop() + break + else: + b = None + return found_tag + + def match(self): + stack = [] + + # No tags to search for + if self.no_tag or (self.left and self.right): + return self.left, self.right + + # Init tag matching objects + osearch = TagSearch(self.view, self.bfr, self.window, self.center, START_TAG[self.mode], 0) + csearch = TagSearch(self.view, self.bfr, self.window, self.center, END_TAG, 1) + + # Searching for opening or closing tag to match + match_type = 0 if self.right else 1 + + # Match the tags + for c in csearch.get_tags(match_type): + if len(stack) and osearch.done: + if self.resolve_self_closing(stack, c): + continue + for o in osearch.get_tags(match_type): + if o.end <= c.begin: + if not o.single: + stack.append(o) + continue + else: + osearch.remember() + break + + if len(stack): + if self.resolve_self_closing(stack, c): + continue + elif match_type == 0 and not osearch.done: + continue + if match_type == 1: + if self.left is None or self.compare_tags(self.left, c): + self.right = c + elif self.left.self_closing: + self.right = self.left + break + + if match_type == 0: + # Find the rest of the the unmatched left side open brackets + # approaching the cursor if all closing brackets were matched + # Select the most recent open bracket on the stack. + for o in osearch.get_tags(0): + if not o.single: + stack.append(o) + if len(stack): + self.left = self.resolve_self_closing(stack, self.right) + elif self.right is None and self.left is not None and self.left.self_closing: + # Account for the opening tag that was found being a self closing + self.right = self.left + + return self.left, self.right diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_plugin.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_plugin.py new file mode 100644 index 0000000..cd3bafb --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_plugin.py @@ -0,0 +1,140 @@ +import sublime +from os.path import normpath, join +import imp +from collections import namedtuple +import sys +import traceback +import warnings + + +class BracketRegion (namedtuple('BracketRegion', ['begin', 'end'], verbose=False)): + """ + Bracket Regions for plugins + """ + + def move(self, begin, end): + """ + Move bracket region to different points + """ + + return self._replace(begin=begin, end=end) + + def size(self): + """ + Get the size of the region + """ + + return abs(self.begin - self.end) + + def toregion(self): + """ + Convert to sublime region + """ + + return sublime.Region(self.begin, self.end) + + +def is_bracket_region(obj): + """ + Check if object is a BracketRegion + """ + + return isinstance(obj, BracketRegion) + + +class ImportModule(object): + @classmethod + def import_module(cls, module_name, loaded=None): + # Pull in built-in and custom plugin directory + if module_name.startswith("bh_modules."): + path_name = join(sublime.packages_path(), "BracketHighlighter", normpath(module_name.replace('.', '/'))) + else: + path_name = join(sublime.packages_path(), normpath(module_name.replace('.', '/'))) + path_name += ".py" + if loaded is not None and module_name in loaded: + module = sys.modules[module_name] + else: + with warnings.catch_warnings(record=True) as w: + # Ignore warnings about plugin folder not being a python package + warnings.simplefilter("always") + module = imp.new_module(module_name) + sys.modules[module_name] = module + source = None + with open(path_name) as f: + source = f.read().replace('\r', '') + cls.__execute_module(source, module_name) + w = filter(lambda i: issubclass(i.category, UserWarning), w) + return module + + @classmethod + def __execute_module(cls, source, module_name): + exec(compile(source, module_name, 'exec'), sys.modules[module_name].__dict__) + + @classmethod + def import_from(cls, module_name, attribute): + return getattr(cls.import_module(module_name), attribute) + + +class BracketPlugin(object): + """ + Class for preparing and running plugins + """ + + def __init__(self, plugin, loaded): + """ + Load plugin module + """ + + self.enabled = False + self.args = plugin['args'] if ("args" in plugin) else {} + self.plugin = None + if 'command' in plugin: + plib = plugin['command'] + try: + module = ImportModule.import_module(plib, loaded) + self.plugin = getattr(module, 'plugin')() + loaded.add(plib) + self.enabled = True + except Exception: + print 'BracketHighlighter: Load Plugin Error: %s\n%s' % (plugin['command'], traceback.format_exc()) + + def is_enabled(self): + """ + Check if plugin is enabled + """ + + return self.enabled + + def run_command(self, view, name, left, right, selection): + """ + Load arguments into plugin and run + """ + + plugin = self.plugin() + setattr(plugin, "left", left) + setattr(plugin, "right", right) + setattr(plugin, "view", view) + setattr(plugin, "selection", selection) + edit = view.begin_edit() + self.args["edit"] = edit + self.args["name"] = name + try: + plugin.run(**self.args) + left, right, selection = plugin.left, plugin.right, plugin.selection + except Exception: + print "BracketHighlighter: Plugin Run Error:\n%s" % str(traceback.format_exc()) + view.end_edit(edit) + return left, right, selection + + +class BracketPluginCommand(object): + """ + Bracket Plugin base class + """ + + def run(self, bracket, content, selection): + """ + Runs the plugin class + """ + + pass diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_remove.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_remove.py new file mode 100644 index 0000000..ed1da63 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_remove.py @@ -0,0 +1,51 @@ +import sublime_plugin +from collections import namedtuple + +MENU = namedtuple("Menu", "simple content block block_indent")( + "Remove Brackets", + "Remove Brackets and Content", + "Remove Brackets: Block", + "Remove Brackets: Indented Block" +) + + +class BhRemoveBracketsCommand(sublime_plugin.WindowCommand): + """ + Command to remove current highlighted brackets and optionally content + """ + + def remove_brackets(self, value): + """ + Perform removal of brackets + """ + + if value != -1: + menu_item = MENU[value] + indent = menu_item == MENU.block_indent + block = menu_item == MENU.block or menu_item == MENU.block_indent + content = menu_item == MENU.content + + self.window.run_command( + "bh_key", + { + "plugin": { + "type": ["__all__"], + "command": "bh_modules.bracketremove", + "args": { + "remove_indent": indent, + "remove_block": block, + "remove_content": content + } + } + } + ) + + def run(self): + """ + Show menu of removal options + """ + + self.window.show_quick_panel( + list(MENU), + self.remove_brackets + ) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_swapping.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_swapping.py new file mode 100644 index 0000000..7516406 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_swapping.py @@ -0,0 +1,44 @@ +import sublime_plugin +import bh_wrapping + + +class SwapBrackets(bh_wrapping.WrapBrackets): + def wrap(self, wrap_entry): + if wrap_entry < 0: + return + + self._style = ["inline"] + + self.brackets = self._brackets[wrap_entry] + self.wrap_brackets(0) + + +class SwapBracketsCommand(sublime_plugin.WindowCommand): + def swap_brackets(self, value): + if value < 0: + return + + self.brackets = self.wrap._brackets[value] + + self.window.run_command( + "bh_key", + { + "plugin": { + "type": ["__all__"], + "command": "bh_modules.swapbrackets" + } + } + ) + self.wrap.wrap(value) + + def run(self): + view = self.window.active_view() + if view is None: + return + self.wrap = SwapBrackets(view, "bh_swapping.sublime-settings", "swapping") + + if len(self.wrap._menu): + self.window.show_quick_panel( + self.wrap._menu, + self.swap_brackets + ) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_swapping.sublime-settings b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_swapping.sublime-settings new file mode 100644 index 0000000..09e583e --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_swapping.sublime-settings @@ -0,0 +1,46 @@ +{ + "swapping": [ + { + "enabled": true, "language_list": [], "language_filter": "whitelist", "entries": [ + {"name": "<> Angle", "brackets": ["<", ">${BH_SEL}"]} + ] + }, + { + "enabled": true, "language_list": ["Plain text"], "language_filter": "blacklist", "entries": [ + {"name": "{} Curly", "brackets": ["{", "}${BH_SEL}"]} + ] + }, + { + "enabled": true, "language_list": ["Plain text"], "language_filter": "blacklist", "entries": [ + {"name": "() Round", "brackets": ["(", ")${BH_SEL}"]} + ] + }, + { + "enabled": true, "language_list": ["Plain text"], "language_filter": "blacklist", "entries": [ + {"name": "[] Square", "brackets": ["[", "]${BH_SEL}"]} + ] + }, + { + "enabled": true, "language_list": ["HTML", "HTML 5", "XML", "PHP", "HTML+CFML", "ColdFusion", "ColdFusionCFC"], "language_filter": "whitelist", "entries": [ + {"name": "HTML/XML Tag", "brackets": ["<${BH_SEL:NAME}>", ""]} + ] + }, + { + "enabled": true, "language_list": ["Markdown"], "language_filter": "whitelist", "entries": [ + {"name": "Mardown: Bold", "brackets": ["**", "**${BH_SEL}"]}, + {"name": "Mardown: Italic", "brackets": ["_", "_${BH_SEL}"]} + ] + }, + { + "enabled": true, "language_list": ["C++", "C"], "language_filter": "whitelist", "entries": [ + {"name": "C/C++: #if", "brackets": ["#if ${BH_SEL}", "#endif"]}, + {"name": "C/C++: #if, #else", "brackets": ["#if${BH_SEL}", "#else\n${BH_TAB:/* CODE */}\n#endif"]}, + {"name": "C/C++: #if, #elif", "brackets": ["#if${BH_SEL}", "#elif ${BH_TAB:/* CONDITION */}\n${BH_TAB:/* CODE */}\n#endif"]}, + {"name": "C/C++: #ifdef", "brackets": ["#ifdef${BH_SEL}", "#endif"]}, + {"name": "C/C++: #ifdef, #else", "brackets": ["#ifdef${BH_SEL}", "#else\n${BH_TAB:/* CODE */}\n#endif"]}, + {"name": "C/C++: #ifndef", "brackets": ["#ifndef${BH_SEL}", "#endif"]}, + {"name": "C/C++: #ifndef, #else", "brackets": ["#ifndef${BH_SEL}", "#else\n${BH_TAB:/* CODE */}\n#endif"]} + ] + } + ] +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_wrapping.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_wrapping.py new file mode 100644 index 0000000..fc58251 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_wrapping.py @@ -0,0 +1,368 @@ +import sublime +import sublime_plugin +from os.path import basename +import re + + +BH_TABSTOPS = re.compile(r"(\$\{BH_(SEL|TAB)(?:\:([^\}]+))?\})") +TAB_REGION = "bh_plugin_wrapping_tabstop" +SEL_REGION = "bh_plugin_wrapping_select" +OUT_REGION = "bh_plugin_wrapping_outlier" + +VALID_INSERT_STYLES = ( + ("inline", "Inline Insert"), + ("block", "Block Insert"), + ("indent_block", "Indented Block Insert") +) + + +def exclude_entry(enabled, filter_type, language_list, language): + """ + Exclude bracket wrapping entry by filter + """ + + exclude = True + if enabled: + # Black list languages + if filter_type == 'blacklist': + exclude = False + if language != None: + for item in language_list: + if language == item.lower(): + exclude = True + break + #White list languages + elif filter_type == 'whitelist': + if language != None: + for item in language_list: + if language == item.lower(): + exclude = False + break + return exclude + + +class TextInsertion(object): + """ + Wrapper class for inserting text + """ + + def __init__(self, view, edit): + """ + Store view and edit objects + """ + + self.view = view + self.edit = edit + + def insert(self, pt, text): + """ + Peform insertion + """ + + return self.view.insert(self.edit, pt, text) + + +class WrapBrackets(object): + """ + Wrap the current selection(s) with the defined wrapping options + """ + + def __init__(self, view, setting_file, attribute): + self.view = view + self._menu = [] + self._brackets = [] + self._insert = [] + self._style = [] + self.read_wrap_entries(setting_file, attribute) + + def inline(self, edit, sel): + """ + Inline wrap + """ + + ti = TextInsertion(self.view, edit) + + offset1 = ti.insert(sel.begin(), self.brackets[0]) + self.insert_regions.append(sublime.Region(sel.begin(), sel.begin() + offset1)) + offset2 = ti.insert(sel.end() + offset1, self.brackets[1]) + self.insert_regions.append(sublime.Region(sel.end() + offset1, sel.end() + offset1 + offset2)) + + def block(self, edit, sel, indent=False): + """ + Wrap brackets around selection and block off the content + """ + + # Calculate number of lines between brackets + self.calculate_lines(sel) + # Calculate the current indentation of first bracket + self.calculate_indentation(sel) + + ti = TextInsertion(self.view, edit) + + line_offset = 0 + first_end = 0 + second_end = 0 + second_start = sel.end() + + for b in reversed(self.brackets[1].split('\n')): + second_end += ti.insert(sel.end(), "\n" + self.indent_to_col + b) + num_open_lines = self.brackets[0].count('\n') + for b in reversed(self.brackets[0].split('\n')): + if line_offset == num_open_lines: + line = b + "\n" + else: + line = self.indent_to_col + b + "\n" + first_end += ti.insert(sel.begin(), line) + line_offset += 1 + self.insert_regions.append(sublime.Region(sel.begin(), sel.begin() + first_end)) + + if indent: + second_start += self.indent_content(ti, line_offset) + else: + pt = self.view.text_point(self.first_line + line_offset, 0) + second_start += ti.insert(pt, self.indent_to_col) + + self.insert_regions.append(sublime.Region(first_end + second_start, first_end + second_start + second_end)) + + def indent_content(self, ti, line_offset): + """ + Indent the block content + """ + + first = True + offset = 0 + for l in range(line_offset, self.total_lines + line_offset): + pt = self.view.text_point(self.first_line + l, 0) + if first: + offset += ti.insert(pt, self.indent_to_col + "\t") + first = False + else: + offset += ti.insert(pt, "\t") + return offset + + def calculate_lines(self, sel): + """ + Calculate lines between brackets + """ + + self.first_line, self.col_position = self.view.rowcol(sel.begin()) + last_line = self.view.rowcol(sel.end())[0] + self.total_lines = last_line - self.first_line + 1 + + def calculate_indentation(self, sel): + """ + Calculate how much lines should be indented + """ + + tab_size = self.view.settings().get("tab_size", 4) + tab_count = self.view.substr(sublime.Region(sel.begin() - self.col_position, sel.begin())).count('\t') + spaces = self.col_position - tab_count + self.indent_to_col = "\t" * tab_count + "\t" * (spaces / tab_size) + " " * (spaces % tab_size if spaces >= tab_size else spaces) + + def select(self, edit): + """ + Select defined regions after wrapping + """ + + self.view.sel().clear() + map(lambda x: self.view.sel().add(x), self.insert_regions) + + final_sel = [] + initial_sel = [] + for s in self.view.sel(): + string = self.view.substr(s) + matches = [m for m in BH_TABSTOPS.finditer(string)] + multi_offset = 0 + if matches: + for m in matches: + r = sublime.Region(s.begin() + multi_offset + m.start(1), s.begin() + multi_offset + m.end(1)) + if m.group(3): + replace = m.group(3) + self.view.erase(edit, r) + added = self.view.insert(edit, r.begin(), replace) + final_sel.append(sublime.Region(s.begin() + multi_offset + m.start(1), s.begin() + multi_offset + m.start(1) + added)) + multi_offset += added - r.size() + else: + self.view.erase(edit, r) + final_sel.append(sublime.Region(s.begin() + multi_offset + m.start(1))) + multi_offset -= r.size() + if m.group(2) == "SEL": + initial_sel.append(final_sel[-1]) + + if len(initial_sel) != len(final_sel): + self.view.add_regions(TAB_REGION, final_sel, "", "", sublime.HIDDEN) + + # Re-position cursor + self.view.sel().clear() + if len(initial_sel): + map(lambda x: self.view.sel().add(x), initial_sel) + elif len(final_sel): + self.view.sel().add(final_sel[0]) + + def read_wrap_entries(self, setting_file, attribute): + """ + Read wrap entries from the settings file + """ + + settings = sublime.load_settings(setting_file) + syntax = self.view.settings().get('syntax') + language = basename(syntax).replace('.tmLanguage', '').lower() if syntax != None else "plain text" + wrapping = settings.get(attribute, []) + for i in wrapping: + if not exclude_entry(i["enabled"], i["language_filter"], i["language_list"], language): + for j in i.get("entries", []): + try: + menu_entry = j["name"] + bracket_entry = j["brackets"] + insert_style = j.get("insert_style", ["inline"]) + self._menu.append(menu_entry) + self._brackets.append(bracket_entry) + self._insert.append(insert_style) + except Exception: + pass + + def wrap_brackets(self, value): + """ + Wrap selection(s) with defined brackets + """ + + if value < 0: + return + + # Use new edit object since the main run has already exited + # and the old edit is more than likely closed now + edit = self.view.begin_edit() + + # Wrap selections with brackets + style = self._style[value] + self.insert_regions = [] + + for sel in self.view.sel(): + # Determine indentation style + if style == "indent_block": + self.block(edit, sel, True) + elif style == "block": + self.block(edit, sel) + else: + self.inline(edit, sel) + + self.select(edit) + + self.view.end_edit(edit) + + def wrap_style(self, value): + """ + Choose insert style for wrapping. + """ + + if value < 0: + return + + style = [] + + self.brackets = self._brackets[value] + for s in VALID_INSERT_STYLES: + if s[0] in self._insert[value]: + self._style.append(s[0]) + style.append(s[1]) + + if len(style) > 1: + self.view.window().show_quick_panel( + style, + self.wrap_brackets + ) + else: + self.wrap_brackets(0) + + +class WrapBracketsCommand(sublime_plugin.TextCommand, WrapBrackets): + def run(self, edit): + """ + Display the wrapping menu + """ + + self._menu = [] + self._brackets = [] + self._insert = [] + self._style = [] + self.read_wrap_entries("bh_wrapping.sublime-settings", "wrapping") + + if len(self._menu): + self.view.window().show_quick_panel( + self._menu, + self.wrap_style + ) + + +class BhNextWrapSelCommand(sublime_plugin.TextCommand): + """ + Navigate wrapping tab stop regions + """ + + def run(self, edit): + """ + Look for the next wrapping tab stop region + """ + + regions = self.view.get_regions(SEL_REGION) + self.view.get_regions(OUT_REGION) + if len(regions): + self.view.sel().clear() + map(lambda x: self.view.sel().add(x), regions) + + # Clean up unneed sections + self.view.erase_regions(SEL_REGION) + self.view.erase_regions(OUT_REGION) + + +class BhWrapListener(sublime_plugin.EventListener): + """ + Listen for wrapping tab stop tabbing + """ + + def on_query_context(self, view, key, operator, operand, match_all): + """ + Mark the next regions to navigate to. + """ + + accept_query = False + if key == "bh_wrapping": + select = [] + outlier = [] + regions = view.get_regions(TAB_REGION) + tabstop = [] + sels = view.sel() + + if len(regions) == 0: + return False + + for s in sels: + count = 0 + found = False + for r in regions[:]: + if found: + select.append(r) + tabstop.append(r) + del regions[count] + break + if r.begin() <= s.begin() <= r.end(): + del regions[count] + found = True + continue + count += 1 + if not found: + outlier.append(s) + tabstop += regions + + if len(tabstop) == len(select): + if len(tabstop): + tabstop = [] + accept_query = True + elif len(tabstop) != 0: + accept_query = True + + # Mark regions to make the "next" command aware of what to do + view.add_regions(SEL_REGION, select, "", "", sublime.HIDDEN) + view.add_regions(OUT_REGION, outlier, "", "", sublime.HIDDEN) + view.add_regions(TAB_REGION, tabstop, "", "", sublime.HIDDEN) + + return accept_query diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_wrapping.sublime-settings b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_wrapping.sublime-settings new file mode 100644 index 0000000..a2899bd --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/bh_wrapping.sublime-settings @@ -0,0 +1,71 @@ +{ + "wrapping": [ + { + "enabled": true, "language_list": [], "language_filter": "whitelist", "entries": [ + {"name": "<> Angle", "brackets": ["<", ">${BH_SEL}"]} + ] + }, + { + "enabled": true, "language_list": ["Plain text"], "language_filter": "blacklist", "entries": [ + {"name": "{} Curly", "brackets": ["{", "}${BH_SEL}"]} + ] + }, + { + "enabled": true, "language_list": ["Plain text"], "language_filter": "blacklist", "entries": [ + {"name": "() Round", "brackets": ["(", ")${BH_SEL}"]} + ] + }, + { + "enabled": true, "language_list": ["Plain text"], "language_filter": "blacklist", "entries": [ + {"name": "[] Square", "brackets": ["[", "]${BH_SEL}"]} + ] + }, + { + "enabled": true, "language_list": ["Plain text"], "language_filter": "blacklist", "entries": [ + {"name": "'' Single Quotes", "brackets": ["'", "'${BH_SEL}"], "insert_style": ["inline"]}, + {"name": "\"\" Double Quotes", "brackets": ["\"", "\"${BH_SEL}"], "insert_style": ["inline"]} + ] + }, + { + "enabled": true, "language_list": ["Python"], "language_filter": "whitelist", "entries": [ + {"name": "'''''' Triple Single Quotes", "brackets": ["'''", "'''${BH_SEL}"], "insert_style": ["inline", "block"]}, + {"name": "\"\"\"\"\"\" Triple Double Quotes", "brackets": ["\"\"\"", "\"\"\"${BH_SEL}"], "insert_style": ["inline", "block"]} + ] + }, + { + "enabled": true, "language_list": ["HTML", "HTML 5", "XML", "PHP", "HTML+CFML", "ColdFusion", "ColdFusionCFC"], "language_filter": "whitelist", "entries": [ + {"name": "HTML/XML Tag", "brackets": ["<${BH_SEL:NAME}>", ""], "insert_style": ["inline", "block", "indent_block"]} + ] + }, + { + "enabled": true, "language_list": ["Markdown"], "language_filter": "whitelist", "entries": [ + {"name": "Mardown: Bold", "brackets": ["**", "**${BH_SEL}"]}, + {"name": "Mardown: Italic", "brackets": ["_", "_${BH_SEL}"]} + ] + }, + { + "enabled": true, "language_list": ["C++", "C"], "language_filter": "whitelist", "entries": [ + {"name": "C/C++: #if", "brackets": ["#if ${BH_SEL:/* CONDITION */}", "#endif"], "insert_style": ["block"]}, + {"name": "C/C++: #if, #else", "brackets": ["#if ${BH_SEL:/* CONDITION */}", "#else\n${BH_TAB:/* CODE */}\n#endif"], "insert_style": ["block"]}, + {"name": "C/C++: #if, #elif", "brackets": ["#if ${BH_SEL:/* CONDITION */}", "#elif ${BH_TAB:/* CONDITION */}\n${BH_TAB:/* CODE */}\n#endif"], "insert_style": ["block"]}, + {"name": "C/C++: #ifdef", "brackets": ["#ifdef ${BH_SEL:/* DEFINE */}", "#endif"], "insert_style": ["block"]}, + {"name": "C/C++: #ifdef, #else", "brackets": ["#ifdef ${BH_SEL:/* DEFINE */}", "#else\n${BH_TAB:/* CODE */}\n#endif"], "insert_style": ["block"]}, + {"name": "C/C++: #ifndef", "brackets": ["#ifndef ${BH_SEL:/* DEFINE */}", "#endif"], "insert_style": ["block"]}, + {"name": "C/C++: #ifndef, #else", "brackets": ["#ifndef ${BH_SEL:/* DEFINE */}", "#else\n${BH_TAB:/* CODE */}\n#endif"], "insert_style": ["block"]} + ] + }, + { + "enabled": true, "language_list": ["Ruby"], "language_filter": "whitelist", "entries": [ + {"name": "Ruby: if", "brackets": ["if ${BH_SEL:CONDITION}", "end"], "insert_style": ["indent_block"]}, + {"name": "Ruby: until", "brackets": ["until ${BH_SEL:CONDITION}", "end"], "insert_style": ["indent_block"]}, + {"name": "Ruby: while", "brackets": ["while ${BH_SEL:CONDITION}", "end"], "insert_style": ["indent_block"]}, + {"name": "Ruby: def", "brackets": ["def ${BH_SEL:NAME}", "end"], "insert_style": ["indent_block"]} + ] + }, + { + "enabled": true, "language_list": ["CSS"], "language_filter": "whitelist", "entries": [ + {"name": "CSS: @group", "brackets": ["/* @group ${BH_SEL:NAME} */", "/* @end */"], "insert_style": ["block"]} + ] + } + ] +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/angle_bracket.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/angle_bracket.png new file mode 100644 index 0000000000000000000000000000000000000000..eb72330e2acb3d78ddaaadec80d240a7b5682f27 GIT binary patch literal 217 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`3p`yMLn>~)y=cqVcZ=6#+^&M)J^Y&%2u?f@IULh<<-Hjl+-&Uim@);Wt)`ybpDKCn`%zopr0C*Hv;Q#;t literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/angle_bracket_close.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/angle_bracket_close.png new file mode 100644 index 0000000000000000000000000000000000000000..d638c0edc2f25834d575abe0bb8f9cbb2bf04b2d GIT binary patch literal 155 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`NuDl_Ar_~TfBgS%lF+h3) zN5|@h853ieWg33S3gojTF&6Dzz?@ z$C0fLdnU**vouz43+!i2st)2cV0u*TP$S)O)#Iy^kw88BBWDL4A3uo$JQi$67Cm88 zI>G(D@duw}oCDvHwHF+=ELD+u-B5Ayf^TQ@9R>!4Tin;LBuYL4+Q#7N>gTe~DWM4f D5;`>S literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/angle_bracket_open_small.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/angle_bracket_open_small.png new file mode 100644 index 0000000000000000000000000000000000000000..dc7be3d5447d0ad0ded92c25cbdbe93eaf40be8a GIT binary patch literal 138 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`fu1goAr_~TfBgS%l@q+XL zjw4$g_DqmrW@)V87TC|4R9(cggZ;?%#)w`S{e}bXPO~_7KClzGuXvqxgTW0hUgl`8 lq=kGP;=FfTOoVtC7#7^&RQ+A5e;sHzgQu&X%Q~loCIHdTE6xA_ literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/angle_bracket_small.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/angle_bracket_small.png new file mode 100644 index 0000000000000000000000000000000000000000..d55828a494650bf8acd25fafc5bda475ea9a1814 GIT binary patch literal 191 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`ZJsWUAr_~TfBgS%lF+l17 z$C0fLdjw9!vm6NxC~RWmVEQPgP{)1bTYw?ELLK{&*^Lnm6BxfY{4f((y|6Re;psc3 zjf#5A4iA*Y&Kj&?oBF^1JnQ$yAADlYAq_vou86j^97@nO;mKK;r0|P(V>3DAqM~e literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/bookmark_small.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/bookmark_small.png new file mode 100644 index 0000000000000000000000000000000000000000..4aa0a1201c534445db3c9cfbfe7d46de611cd2a8 GIT binary patch literal 118 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`ww^AIAr_~TfBgS%&#XG5@fMp6 zQxc;W4@2W(hco`2j0y!T9hD~x4+x)}Fq=7v;b`0f3jxi(V~zfu$`=_JPV4hc*)Y|= Q9%vAQr>mdKI;Vst06(%L$p8QV literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/circle.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/circle.png new file mode 100644 index 0000000000000000000000000000000000000000..8073357311e16b93fc80a6b9f2121fa0b439215d GIT binary patch literal 220 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`i#=T&Lo7}w|M>sko>^65{t_ku zYln@D7uo*&`+NKG{r&aF|1V}-aO1Ci{XZ80BZY_dj32(gzyC6Vm*M80`5p`=D$Iu& z76_jg&SnUBTdZ)jF{j~R{Ut|+O^lo=5*HZE8D6P>;#E*(n8>t=;U>4tg$6}djRi-T zuDB#6C)!?XVVQEnuO%&@g!_S4r;Nk&FTxul8)lqY!q7XDO=BHH5^qqka-al5`}qd8 ULlV!<0iDa>>FVdQ&MBb@0Ho$qVgLXD literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/circle_small.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/circle_small.png new file mode 100644 index 0000000000000000000000000000000000000000..da6d4b455611ff68241c65be527b80b763de0edd GIT binary patch literal 215 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`^E_P~Lo7}w|M>sko>^65wt})i zHd7&sz;ULHiUP*M*$mI`@2{WT_~rJ0jt{aLW|AJf%B&Z2&erW9Q9VjpV zQQ`x4$J-B*0YBy&yf~Q2aAE#d7YPH=<&695|8Hxx5|-~c^qjR!A+Et&-JpgqxtsG# z#zdA!y%AsJnUY+SgoVz}x353c_{BwG9@`O42OSSDuO$|m{U*!|lONewYEBgn0lJjI M)78&qol`;+0IN4mYybcN literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/curly_bracket.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/curly_bracket.png new file mode 100644 index 0000000000000000000000000000000000000000..66b88172bfe430db503c88030b18cab2345f76f0 GIT binary patch literal 242 zcmV-4Y3my+YsEkac>BzDQ?`D0<;0M5d3r?slg!x7v-*dhp-<(i-ZTbUbVpwtA;$gB zfEmL`5hUy^z(54D_7XHt<^TWy07*qoM6N<$f<4J#&j0`b literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/curly_bracket_close.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/curly_bracket_close.png new file mode 100644 index 0000000000000000000000000000000000000000..b16d32079cce94ce5624f048296293d6d7d3910e GIT binary patch literal 173 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`C7v#hAr_~TfBgS%&&>BnykkAX zMoBqS9-a!7qYXbK1?H<>XAro@a%8`zVZzyZ4u#tdPXm}|oG@L$lI36{uz5l{Wp)5myv0h<9|gib&XgNEYYMJAf_6|XY{r0rrm@@z^)qr$Qq3=D}s W#IBaCEO`R7pTX1B&t;ucLK6UGaXgs- literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/curly_bracket_close_small.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/curly_bracket_close_small.png new file mode 100644 index 0000000000000000000000000000000000000000..fde34df1f371e61867c2d0540612c92468cdc659 GIT binary patch literal 156 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`$(}BbAr_~TfBgS%&#Wpje+iF( zc0iH?o4|dxr1&CUhKE-j_RJGve#Z1s%!}s*>mz4}GcM~GXF1fcc6?{vsA9<`!tgOu zA&fC8t%zrad!|G~QlhNt%LjY{zj-3OmRRWX2r@IMi*PNOy?Uw}&^iWBS3j3^P6wr-aPXgPcgcXdePb3(ae>cu( zm0@<66||sDWTG_NnJF!dp-GKeY=77u`A(525bKC%*l4knErQ`=sF#tjqt8Z0hUL@5 VUKsPAn+UX@!PC{xWt~$(69AV-H^2Y@ literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/curly_bracket_open_small.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/curly_bracket_open_small.png new file mode 100644 index 0000000000000000000000000000000000000000..9a7584e6e671970178f85604e93d6ed8f343440b GIT binary patch literal 145 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`5uPrNAr_~TfBgS%_!j;QG z$9Wy&KZhFe4&xK13CaTN7aie_Zm7^xWs_lelvl)igZ;?##)#$^W(TSB%!L;l+74DJ s#QAnI?z>Pj!7@Q}qE=<%1Zf6_?><};t=-S41MOh&boFyt=akR{0Jg9#y#N3J literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/curly_bracket_small.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/curly_bracket_small.png new file mode 100644 index 0000000000000000000000000000000000000000..b8a4d91fe866894da5c52796f8d10ecd9aad3f3d GIT binary patch literal 211 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`vpiiKLn?0Fy=2JM>>%K9QFWfv zh7BGI%R8S)-D-_-E@H8DXz@5;)s@TDy`+0y>Su0k$y9?S8!OHl{Yfh8+4@cW(qex8 zv?m9zv-L>`9PUY~5y)gJk7+%~v{WrXQJBp*WR^p7h{T=+NAom49N=yg;P~jqTdpUt z;BX|zoDWCj&drj2a!YoKMq1s&Wt=V&6MWt+IQ}qy`ie~t4~QQ&mSA5I<(2?+C4;A{ KpUXO@geCxL$xwv= literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/dot.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/dot.png new file mode 100644 index 0000000000000000000000000000000000000000..95f2c7d0e2ef96e7bd74c2a6a95e6eac9d4e184b GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`d7dtgAr_~TfBgS%l@rU#S zhNCh|q!K>7zrX+W|Ah=E>iCy1b(l6VuXy;c-h@HqH{TMG4nu_p_Iw|{zrW9v#N+X! zUphn}xq;=%&KnKPfsGs`S!`m@zfTtF-Cy^26T>B5g+IT)=l2WFnZlXGz+kJy*yhz? Q4z!uU)78&qol`;+01#Y0pa1{> literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/dot_small.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/dot_small.png new file mode 100644 index 0000000000000000000000000000000000000000..32c26d23fb361acfd1e15333cc1b61c6748cb64f GIT binary patch literal 148 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`(Vi}jAr_~TfBgS%lF+l17 z=aGL7G8{+#ERj0EdxZO+@&P>sG4-PjGyZ2hcz=Jt=n{zuf8~X@OiW@pvGaJM)&IZ0 x%b7hgCrWjA1w>cZ{rj`h+T`-V6^trL462p_j(#3e+kn&s{3*;q#Gcl1WqVQE|Q^O};>(iQ4x#He8z0m>OhPyg~j`kU@HdsXcGW85tY4 zW#xx1`4t@a@a}HBzQu_TulCGgW0Nz>y(hBo-!c0SP6>>gTe~ HDWM4f)^<85 literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_close.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_close.png new file mode 100644 index 0000000000000000000000000000000000000000..7c25d7689601cff68ff5b46aa7dc3464bc126b1b GIT binary patch literal 125 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`E}kxqAr_~TfBgS%l@q$zX z&xVgL(+|YU)HJbua5A+yd{8~Z!6f7`kI6riB;E@4BcB^*@ORqtznY{tiO-E!fsvVE X!el9-I77BTpm_|Qu6{1-oD!M<*q0`F literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_close_small.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_close_small.png new file mode 100644 index 0000000000000000000000000000000000000000..71af73bb83035359ae05fd1957d197490a79e122 GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`UY;(FAr_~TfBgS%lF+iGu zIng6$6HkZI^@h*@-!g|9mX7}uvm9gu>{*fw72dHwTD(N+14GB*nVvtZe0;x+P$t!j{J07EbdT7YI)buTfo~{rWQVs;?Ir&L2pNP0bXUwQ%Dq z=M)8L|8^CXOK0{vE1InMyrO)KYEDwko1V~fyM737SDz*Iq51q4<|{I0-|m@e$pdX= N@O1TaS?83{1OUIMKD__{ literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_offset_open.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_offset_open.png new file mode 100644 index 0000000000000000000000000000000000000000..aaaa8b6f86a6526ac5d37cecb7afec7fc6d0777f GIT binary patch literal 133 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`KAtX)Ar_~TfBgS%lF+l17 z_mR&HKV+wzXFu|J;tv+y8Q^7 z_9M?5BX~P5H~e5+$tJ`4s7^tQ`BA%rjQc9a{|!Gl1aALrZwOr?#lRfsk+X@1L-~3` h;sic7UIj*GhNG|eCN2}Y#tk%>!PC{xWt~$(698b)D%Su2 literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_offset_small.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_offset_small.png new file mode 100644 index 0000000000000000000000000000000000000000..131c95b9e22bf7ff1527a400ae193c9ecdc44d02 GIT binary patch literal 169 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`1)eUBAr_~TfBgS%l@q`2e zbD~GiCY}zZ>kXj+zGV(IEFJ$7jUD(q&NFY65?Idoky&M@g+d+2ktByZ{72k3N*0J{ zX|jdNw5xn)$XX$FMaVXX?N77SdA?V7LYEw4I@h3dwZWi>SK^uH%rc^7 z_9M?5BX~P5H~e5+$tJ`4s7^tQ`BA%rjQc9a{|!Gl1aALrZwOr?#lRfsk+X@1L-~3` h;sic7UIj*GhNG|eCN2}Y#tk%>!PC{xWt~$(698b)D%Su2 literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_small.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/double_quote_small.png new file mode 100644 index 0000000000000000000000000000000000000000..3d4daeb3967d9544e363f52fc20fc1f7f1710be7 GIT binary patch literal 167 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`d7dtgAr_~TfBgS%lF+g$w z`;q635xgCj8-6fOlHqAl+-S&S!FA-XgAUsf{zePFZpOeLJU`fvxHsJ3Ta_R>>5kTl z8L}Z~?Q_^3Y+-#ht3BZQSLfL7%MAuPY&H#T-BKoVzObd6^hp^60Il`DCjVti!ZLQC O%?zHdelF{r5}E*5r8(08 literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/question.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/question.png new file mode 100644 index 0000000000000000000000000000000000000000..fc0bf7a2393cc3e273f6d6a020b41821f50b481b GIT binary patch literal 176 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`<(@8%Ar`&KKmPx>XI7P%zr<3& zonfPmdKI;Vst0BRRG8~^|S literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/question_small.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/question_small.png new file mode 100644 index 0000000000000000000000000000000000000000..434dc2cacedd2730708c746392ec45ba5378dbf9 GIT binary patch literal 158 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`sh%#5Ar`&KKmPx>XI7P%y?{+X zona%pK=H(62WbIzhC*I}-;%nE#}*BQzbt}!Pu7V$2yQ0$d*$Y*9)9?1Oi+J)yXKnoc>UHx3vIVCg! E0961iHvj+t literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/quote.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/quote.png new file mode 100644 index 0000000000000000000000000000000000000000..d4fe698477ee4464c58ff8c4dd6ba5f4dd6614a1 GIT binary patch literal 125 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`E}kxqAr_~TfBgS%l@q%On z&xVgL(+|YU)HJbua5A+yd{8~Z!6f7`kI6riB;E@4BcB^*@ORqtznY{tiO-E!fti`1 Y{E(E;$>VbDK=T+pUHx3vIVCg!0Ot-TUH||9 literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/quote_small.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/quote_small.png new file mode 100644 index 0000000000000000000000000000000000000000..414e6bab27aebac15688352a2cea2c7244e6a869 GIT binary patch literal 131 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`UY;(FAr_~TfBgS%l@q#1+ zbD~GiCY}zZ>kXj+zGV(IEFJ$RW;w_R*s~-VD!gNVw0Mcs2ZoNz4H3K@=b1O&yU=|s ekeTfe1A~kY-$aRfm!1GkW$<+Mb6Mw<&;$UMjVh%8 literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/round_bracket.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/round_bracket.png new file mode 100644 index 0000000000000000000000000000000000000000..92e3c48bf729d1730411a3650be583a6eac33cae GIT binary patch literal 270 zcmV+p0rCEcP)_)*|8mr22ni>5_TXKpP86D|Uaz-O*6 z1unRQv-XQ54mgsssYo7+66Xk(ELyv$$`L~Y5%O5^nMp1OAxOk51-|7m+4-B`4IOtY U#FTn{J^%m!07*qoM6N<$f}}=eTL1t6 literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/round_bracket_close.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/round_bracket_close.png new file mode 100644 index 0000000000000000000000000000000000000000..1c23aee14f9049993b3ffa19094914db5a6ea1d6 GIT binary patch literal 188 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`&7LlfAr-f_Uf#{w;2_|7k<~t; ztI>A`C!5lZ<4PSFX|hcc5gpqS7xk`q9X;a;ubf4}qDJYn8p&s-OGX9gtxM%JF9~tr z`QUJj&B@|HO9N*F?~Ro=9F!V6d0m8V+;GrnoXUGdNaAM0G>3yMH8)z1vsCR+Oy$#T l=g#^zMf}1a0j3ppfwDhu6{1-oD!M4vu3FAN`g9+QQ)J>gTe~DWM4fW!^B9 literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/round_bracket_small.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/round_bracket_small.png new file mode 100644 index 0000000000000000000000000000000000000000..b1198309e1f09364d5ebe06b06c1a4ca57020d80 GIT binary patch literal 206 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Q$1ZALn>~)y=2JMY#`E*xPHO9 zuqytep&9D~ybrqWX?q~RpX{IHImacVf6~9?HVM|k;va3|lj=`?-g0fXjF84f$;}R1 zO(#5VzG88DK|wTkjKSg!F*OgnZ-{0nK8$8kblbA7;fVCTCb?#9g#`i;a=#~J#58Kx zRYWoI`*d8Zd&t*%#=_e`;r=dm(-y;Wjq?0Qk`dq7k31Fej{4RIaxH_WtDnm{r-UW| DwKGo~ literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote.png new file mode 100644 index 0000000000000000000000000000000000000000..a69f2c0fd8a960f202900c38e2f7d0590fdd8a4a GIT binary patch literal 137 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`0iG_7Ar_~TfBgS%lF+l17 z`;pI$KV&CK7%5~N;8}4fqlmYHE%Ze9(T4p;BtD3D=k5vkXPm@y;MtuFje4n_Yz<5$ ka@;#Ag=%b literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote_close_small.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote_close_small.png new file mode 100644 index 0000000000000000000000000000000000000000..ddd2499e1b85c06b1cba9eb499f900e13208e557 GIT binary patch literal 104 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%``kpS1Ar_~TfBgS%l@q#o1 zGjsX@L4|+ros54SYNiM=pJU#5??T_PK;{=f4Nu;%+A@F7+5yzc;OXk;vd$@?2>_Fr BAVvTH literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote_offset.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote_offset.png new file mode 100644 index 0000000000000000000000000000000000000000..bc59327dbd62f9eccb0b22360d32970a38fc3bc1 GIT binary patch literal 141 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`A)YRdAr_~TfBgS%l@q|PJ z58Jhd{YNAYupjx{_(OJ*g#ML=^dk}m%&RPS@*^ B9#sGU literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote_offset_small.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote_offset_small.png new file mode 100644 index 0000000000000000000000000000000000000000..98460c6690c200570c1210e4287584e59ef723c4 GIT binary patch literal 124 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`&YmugAr_~TfBgS%l(L<7f znK}J{RuXRn&yoEbJB~Acv`^ys!5iA6$9HK@s|52u=egcxItjcwyQB@*^ B9#sGU literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote_small.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/single_quote_small.png new file mode 100644 index 0000000000000000000000000000000000000000..f21e44eb5e13fe335a0d6c76b16839a16c90ffc1 GIT binary patch literal 124 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`&YmugAr_~TfBgS%lF+ggA zL`QwHU^`uWYoYk=V+o&(DSUahK6k!HBMCpE+6@e0N-W(*9< X*ZCRk{21;4jbre1^>bP0l+XkKFYGFH literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/square_bracket.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/square_bracket.png new file mode 100644 index 0000000000000000000000000000000000000000..84bdf1c6be70ee332e6a5ece415be724e4cd0830 GIT binary patch literal 101 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`I-V|$Ar_~TfBgUdf7bsM4UD|X y&B8A}@p!Nq2pm|<)+1#Q#G_zs9N401#=y{i&@`#LN$mnqAA_f>pUXO@geCwtl^s?9 literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/square_bracket_close.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/square_bracket_close.png new file mode 100644 index 0000000000000000000000000000000000000000..3e86fbca9ee07b2851798f897794750d6e3129fc GIT binary patch literal 97 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`8lEnWAr`&KKmPx>XXfkS@p}-| u?rprwaWPwul))rk2R4zN4EoD%Ffh3PkmC8)CNdMKhr!d;&t;ucLK6ULmKv-8 literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/square_bracket_close_small.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/square_bracket_close_small.png new file mode 100644 index 0000000000000000000000000000000000000000..c743d19008e57dfce48617dd932bfd1717ad0829 GIT binary patch literal 111 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`W}YsNAr_~TfBgS%&#XG5aT*&3 z(??l_e@sWFYZM+-QrPF+$=L1AyWkGPrsT$~mkAFheG)KaU^x4kSLn%wAL&377(8A5 KT-G@yGywpo%OpJj literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/square_bracket_open.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/square_bracket_open.png new file mode 100644 index 0000000000000000000000000000000000000000..0281a6357ec6e5d2a5494398deb15d1fe06734dc GIT binary patch literal 98 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`nw~C>Ar_~TfBgS%&&;>ynZ4G5 uReg;d%pX=tnkHOnn3%$Fb7rT8&qhXu7^%MmDKhsleN>X!)_CN-Q1XE!88#g`(}oIWjV{)%%LfmrKUK_N ZV95R?BR79rhAPlR22WQ%mvv4FO#tm|C+`3N literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/star.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/star.png new file mode 100644 index 0000000000000000000000000000000000000000..a754806c7b21d041fcffb2c6d092569d6bdd96db GIT binary patch literal 244 zcmVXI7P%-l8DT z9hBr?CSdM-vuBBf0cVGFg9TqlcjFF@j_^rI4B{b=V-@PSj_5k*xO6dAIqc!;IM48r zRpB1bk+3Ba0Srg{9Ax;9Jax$7JTmnOOA?#F^$;S%xzN`89qJcnxXhF&kblX NgQu&X%Q~loCID%HG+qDz literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/tag.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/icons/tag.png new file mode 100644 index 0000000000000000000000000000000000000000..7e8f47cf3c049286559099ff0776473007246928 GIT binary patch literal 306 zcmV-20nPr2P)U zzb=G96b0}pBqG@ag-)U%bP~4GC^g~@yunsFrAlN=5~W&r2ug)oL?aP}O0{JD+n;7W z8Dl=5b(5RiIdkrrd(Q*}gb)iMMWjftI)?=O0etnYVIPMTFp0aoWfjM{#xmy6$~6NP za1bx6ILj3_eUEN&Oe~h3sLiTqVFlB51USNTtS559D@L)E47rpNdU(f2Lg?p1-Y1tY zm?~{?i9vLap?1Qpe z84LP^^*O zF=dfQA|ZwcqLSL>_b3WyF6gkEHshh}O$+w83&+@t3OQ{a*ya6eWPZ8pET6wa;7*`R O89ZJ6T-G@yGywn@qEH_I literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/package-metadata.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/package-metadata.json new file mode 100644 index 0000000..b699bc8 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/package-metadata.json @@ -0,0 +1 @@ +{"url": "https://github.com/facelessuser/BracketHighlighter", "version": "2013.03.27.09.00.08", "description": "Bracket and tag highlighter for Sublime Text 2"} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/readme.md b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/readme.md new file mode 100644 index 0000000..f9067f3 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/BracketHighlighter/readme.md @@ -0,0 +1,470 @@ +# About +This is a fork of pyparadigm's _SublimeBrackets_ and _SublimeTagmatcher_ (both are no longer available). I forked this to fix some issues I had and to add some features I wanted. I also wanted to improve the efficiency of the matching. This cuts down on the parallel searching that is now streamlined in one search. Since then, I have rewritten the entire code base to bring more flexibility, speed, and features. + + + +## Sublime Text 3 Support? +ST3 support is found here (at the present time): https://github.com/facelessuser/BracketHighlighter/tree/BH2ST3 + +## Overview +Bracket Highlighter matches a variety of brackets such as: ```[]```, ```()```, ```{}```, ```""```, ```''```, ``````, and even custom brackets. + +# FeatureList +- Customizable to highlight almost any bracket +- Customizable bracket highlight style +- High visibility bracket highlight mode +- Selectively disable or enable specific matching of tags, brackets, or quotes +- Selectively whitelist or blacklist matching of specific tags, brackets, or quotes based on language +- When bound to a shortcut, allow option to show line count and char count between match in the status bar +- Highlight basic brackets within strings +- Works with multi-select +- Configurable custom gutter icons +- Toggle bracket escape mode for string brackets (regex|string) +- Bracket plugins that can jump between bracket ends, select content, remove brackets and/or content, wrap selectios with brackets, swap brackets, swap quotes (handling quote escaping between the main quotes), fold/unfold conent between brackets, toggle through tag attribute selecection, select both the opening and closing tag name to change both simultaneously. + +# General Use +In general BracketHighligher (BH) will automatically highlight brackets (or defined bracket like start and end blocks) its between. By default, BH will but opening and closing icons in the gutter of the corresponding line containing open or closising bracket. BH, by default, will underline the closing and opening bracket as well. + +## Built-in Supported brackets +Currently BH supports the following brackets out of the box: + +- round +- square +- curly +- angle +- single and double quotes +- python single and double quotes (unicode and raw) +- python tripple single and double quotes (unicode and raw) +- Javascript regex +- Perl regex +- Ruby regex +- Markdown italic +- Markdown bold +- CSSedit groups +- Ruby conditional statements +- C/C++ compiler switches +- PHP conditional keywords +- Erlang conditional statements +- HTML/ColdFusion/XML tags + +BH also supports highlighting basic sub brackets ```(), [], {}``` within supported regex and strings. + +## Additional Features +BH has a couple of additonal features built-in. + +### Toggle Global Enable (bh_toggle_enable) +This command enables and disables BH globally + +### Toggle String Bracket Escape Mode (bh_toggle_string_escape_mode) +This toggles BH's recognition mode of escaped sub brackets in strings and regex. The modes are string escape mode and regex escape mode. + +### Settings +When changing settings, you should copy the entire ```bh_core.sublime-settings``` to your ```User``` folder before changing. Style and color will be discussed in greater depth in the ```Configuring Highlight Style``` section. + +These are the basic settings you can change: +```javascript + //Debug logging + "debug_enable": false, + + // When only either the left or right bracket can be found + // this defines if the unmatched bracket should be shown. + "show_unmatched" : true, + + // High visibilty style and color for high visibility mode + // (solid|outline|underline) + "high_visibility_style": "outline", + // (scope|__default__|__bracket__) + "high_visibility_color": "__bracket__", + + // Match brackets only when the cursor is touching the inside of the bracket + "match_only_adjacent": false, + + // Character threshold to search + "search_threshold": 5000, + + // Set mode for string escapes to ignore (regex|string) + "bracket_string_escape_mode": "string", + + // Set max number of multi-select brackets that will be searched automatically + "auto_selection_threshold" : 10, + + // Disable gutter icons when doing multi-select + "no_multi_select_icons": false, +``` + +### Bracket Plugins +Bh is also extendable via plugins and provides an number of plugins by default. See ```Bracket Plugins``` to learn more about the included plugins. + +## Bracket Plugin +BH provides a number of built in Bracket Plugins that take advantage of BH's matching to provide additional features. Most plugin features are available via the Tools->Packages->BracketHighlighter menu or the command palette. To see how to configure shortcuts, see the ```Example.sublime-settings``` file. + +### Bracket Select Plugin +This plugin changes the selection inside between the brackets. It can select the content or move the bracket to the opening and closing bracket. Behavior is slightly modified for tags. + +### Bracket Remove Plugin +Removes the surrounding brackets. + +### Fold Bracket Plugin +Folds the content of the current surrounding brackets. + +### Swap Quotes Plugin +Swap the quotes style of surrounding quotes from double to single or vice versa. It also handlings escaping and unescaping of sub quotes. + +### Tag Plugin +Plugin used to help highlight tags + +Additional tag settings found in ```bh_core.sublime-settings```: +```javascript + /* Plugin settings */ + + // Style to use for matched tags + "tag_style": "tag", + + // Scopes to exclude from tag searches + "tag_scope_exclude": ["string", "comment"], + + // Determine which style of tag-matching to use in which syntax + "tag_mode": { + "xhtml": ["XML"], + "html": ["HTML", "HTML 5", "PHP"], + "cfml": ["HTML+CFML", "ColdFusion", "ColdFusionCFC"] + } +``` + +### Tag Attribute Select Plugin +Cycle through selecting tag attributes of tags. + +### Tag Name Select Plugin +Select the opening and closing tag name of current tag. + +### Bracket Wrapping Plugin +Wrap the current selection with supported bracket of your choice. Wrapping definitions are configured in ```bh_wrapping.sublime-settings```. + +### Bracket Swapping Plugin +Swap the current surrounding bracket with supported bracket of your choice. Swapping definitions are configured in ```bh_swapping.sublime-settings```. + +## Shortcuts +By default BH provides no shortcuts to avoid shortcut conflicts, but you can view the included ```Example.sublime-keymaps``` file to get an idea how to set up your own. + +# Customizing BracketHighligher +BH is extremely flexible and be customized and extended to fit a User's needs. The first step is to copy the ```bh_core.sublime-settings``` to your ```User``` folder. + +## Configuring Brackets +BH has been written to allow users to define any brackets they would like to have highlighted. There are two kinds of brackets you can define ```scope_brackets``` (search file for scope regions and then use regex to test for opening and closing brackets) and ```brackets``` (use regex to find opening and closing brackets). ```bracket``` type should usually be the preferred type. ```scope_brackets``` are usually used for brackets whose opening and closing are the same and not distinguishable form one another by regex; scope brackets must be contained in a continuous scope region like string for quotes etc. + +### Configuring Brackets +Brackets are defined under ```brackets``` in ```bh_core.sublime-settings```. + +Angle and Curly bracket will be used as an eample (not all options may be shown in these examples): + +```javascript + { + "name": "angle", + "open": "(<)", + "close": "(>)", + "style": "angle", + "scope_exclude": ["string", "comment", "keyword.operator"], + "language_filter": "whitelist", + "language_list": ["HTML", "HTML 5", "XML", "PHP", "HTML+CFML", "ColdFusion", "ColdFusionCFC"], + "plugin_library": "bh_modules.tags", + "enabled": true + }, + { + "name": "curly", + "open": "(\\{)", + "close": "(\\})", + "style": "curly", + "scope_exclude": ["string", "comment"], + "scope_exclude_exceptions": ["string.other.math.block.environment.latex"], + "language_filter": "blacklist", + "language_list": ["Plain text"], + "find_in_sub_search": "true", + "ignore_string_escape": true, + "enabled": true + }, +``` + +- **name**: the name of the bracket (should be unique) +- **open**: defines the opening bracket (one and only one captureing group must be present) +- **close**: defines the closing bracket (one and only one captureing group must be present) +- **style**: Name of style definition to be used to highlight the brackets. See ```Configuring Bracket Styles``` for more info. +- **scope_exclude**: Scopes where the opening and closing brackets should be ignored. +- **language_filter**: This works in conjunction with ```language_list```. It specifies whether ```language_list``` is a ```blacklist``` or ```whitelist```. +- **language_list**: an array of tmLanguage file names that should be avoided or included for highlighting. Looks to ```language_filter``` to determine if avoidance or inclusion is used. +- **enabled**: disable or enable rule +- **scope_exclude_exceptions (optional)***: used to ignore exluding of sub scopes such as in the curly example above where ```string``` is excluded, but not ```string.other.math.block.environment.latex```. +- **plugin_library (optional)**: defines plugin to use for determining matches (see Bracket Plugin API for more info on matching plugins) +- **find_in_sub_search (optional)**: this rule should be included when doing sub bracket matching in ```scope_brackets``` (like finding round brackets between quotes etc.). The setting must be as string and can be either (true|false|only); only means this bracket is only matched as a sub bracket of a ```scope_bracket```. +- **ignore_string_escape (optional)**: Do not ignore sub brackets found in strings and regex when escaped, but use internal escape logic to determine if the brackets should be ignored based on whether regex or string escape mode is set. + +### Configuring Scope Brackets +Scope Brackets are defined under ```scope_brackets``` in ```bh_core.sublime-settings```. + +Python Single Quote bracket will be used as an eample (not all options are shown in this example): + +```javascript + { + "name": "py_single_quote", + "open": "u?r?((?:'')?')", + "close": "((?:'')?')", + "style": "single_quote", + "scopes": ["string"], + "language_filter": "whitelist", + "language_list": ["Python"], + "sub_bracket_search": "true", + "enabled": true + }, +``` + +- **name**: the name of the bracket (should be unique) +- **open**: defines the opening bracket (one and only one captureing group must be present) +- **close**: defines the closing bracket (one and only one captureing group must be present) +- **style**: Name of style definition to be used to highlight the brackets. See ```Configuring Bracket Styles``` for more info. +- **scopes**: scope that should be searched to find the opening and closing brackets. +- **language_filter**: This works in conjunction with ```language_list```. It specifies whether ```language_list``` is a ```blacklist``` or ```whitelist```. +- **language_list**: an array of tmLanguage file names that should be avoided or included for highlighting. Looks to ```language_filter``` to determine if avoidance or inclusion is used. +- **sub_bracket_search**: should this scope bracket also search for sub brackets (like curly brackets in strings etc.). +- **enabled**: disable or enable rule +- **plugin_library (optional)**: defines plugin to use for determining matches (see Bracket Plugin API for more info on matching plugins) + +## Configuring Highlight Style +Each bracket definition (described in ```Configuring Scope Brackets``` and ```Configuring Brackets```) has a ```style``` setting that you give a style definition to. Style definitions are defined under ```bracket_styles``` in ```bh_core.sublime-settings```. + +There are two special style definitions whose names are reserved: ```default``` and ```unmatched```, but you can configure them. All other custom style definitions follow the same pattern (see ```curly``` below and compare to the special style defintions; format is the same) All custom styles follow this pattern. See description below: + +```javascript + // "default" style defines attributes that + // will be used for any style that does not + // explicitly define that attribute. So if + // a style does not define a color, it will + // use the color from the "default" style. + "default": { + "icon": "dot", + "color": "brackethighlighter.default", + "style": "underline" + }, + + // This particular style is used to highlight + // unmatched bracekt pairs. It is a special + // style. + "unmatched": { + "icon": "question", + // "color": "brackethighlighter.unmatched", + "style": "outline" + }, + // User defined region styles + "curly": { + "icon": "curly_bracket" + // "color": "brackethighlighter.curly", + // "style": "underline" + }, +``` + +- **icon**: icon to show in gutter. Available options are (angle|round|curly|square|tag|star|dot|bookmark|question|quote|double_quote|single_quote|single_quote_offset|double_quote_offset|none) +- **color**: scope to define color +- **style**: higlight style. Available options are (solid|outline|underline|none) + +As shown in the example above, if an option is omitted, it will use the setting in ```default```. So ```curly```, in this example, defines ```icon```, but will use ```default``` for the ```color``` and ```style```. + +To customize the color for ```curly``` you can create your own custom scope. + +Add this to your color scheme: +```XML + + name + Bracket Curly + scope + brackethighlighter.curly + settings + + foreground + #CC99CC + + +``` + +And then use the scope: +```javascript + "curly": { + "icon": "curly_bracket" + "color": "brackethighlighter.curly", + // "style": "underline" + }, +``` + +# Bracket Plugin API +There are two kinds of plugins that can be written ```definition``` plugins (plugins attached to bracket definitions via the ```plugin_library``` option) or ```run instance``` plugins (plugins that are that are fed in the BracketHighligher via the command parameter ```plugin```). + +Bracket plugins use ```BracketRegions```. ```BracketRegions``` are simple objects containing a begin pt and end pt of a bracket. + +Class: + +- **BracketRegion(begin_pt, end_pt)** + +Attributes of BracketRegion: + +- **begin**: the start pt of the BracketRegion +- **end**: the end pt of the BracketRegion + +Methods of BracketRegion: + +- **size()**: returns size of region +- **move(begin_pt, end_pt)**: returns a new BracketRegion object with the points moved as specified by the parameters +- **toregion**: returns a sublime Region() object + +## 'Defintion' Plugins +These are plugins that are attached to the bracket definition and aid in processing the brackets. These kids of plugins have two methods you can provide ```post_match``` and/or ```compare```. + +### compare +```compare``` is run when comparing the opening bracket with closing brackets. This allows you to provide logic to accept or regect a the pairing of an opening bracket with a closing bracket. You should not change the text in the view during this operation. + +The ```compare``` method receives the following paramters: + +- **name**: the name of the bracket definition being evaluated +- **first**: a bracket region for the opening bracket +- **second**: a bracket region for the closing bracket +- **bfr**: the file buffer + +Returns: + +- **Boolean**: indicating whether the the comparison yields a suitable match + +Example (from phphekywords.py): +```python +def compare(name, first, second, bfr): + return "end" + bfr[first.begin:first.end].lower() == bfr[second.begin:second.end].lower() +``` + +### post_match +```post_match``` is run after the brackets have been matched. You can do things like alter the highlighting region and change the bracket_style if needed. You should not change the text in the view during this operation. + +The ```post_match``` method receives the following parameters: + +- **name**: the name of the bracket definition being evaluated +- **style**: the style definition name that is to be used to highlight the region +- **first**: a bracket region for the opening bracket +- **second**: a bracket region for the closing bracket +- **center**: position (pt) of cursor (in retrospect, probably not the most intuitive name; not sure why I named it such) +- **bfr**: the file buffer +- **threshold**: the calculated search window of the buffer that is being searched + +Returns: + +- **BracketRegion**: opening bracket region +- **BracketRegion**: closing bracekt region +- **style**: the name of the style definition to use + +Example (from rubykeywords.py): +```python +import re + + +def post_match(view, name, style, first, second, center, bfr, threshold): + if first is not None: + # Strip whitespace from the beginning of first bracket + open_bracket = bfr[first.begin:first.end] + if open_bracket != "do": + m = re.match(r"^(\s*\b)[\w\W]*", open_bracket) + if m: + first = first.move(first.begin + m.end(1), first.end) + return first, second, style +``` + +Example (snippet from tags.py) +```python +def post_match(view, name, style, first, second, center, bfr, threshold): + left, right = first, second + threshold = [0, len(bfr)] if threshold is None else threshold + tag_settings = sublime.load_settings("bh_core.sublime-settings") + tag_mode = get_tag_mode(view, tag_settings.get("tag_mode", {})) + tag_style = tag_settings.get("tag_style", "angle") + bracket_style = style + + if first is not None and tag_mode is not None: + matcher = TagMatch(view, bfr, threshold, first, second, center, tag_mode) + left, right = matcher.match() + if not matcher.no_tag: + bracket_style = tag_style + + return left, right, bracket_style +``` + +## Run Instance Plugins +```Run instance``` plugins are fed into the command executing a BracketHighlighter match via the ```plugin``` parameter. + +Example of run instance plugin getting called: +```javascript +// Go to left bracket + { + "caption": "BracketHighlighter: Jump to Left Bracket", + "command": "bh_key", + "args": + { + "lines" : true, + "plugin": + { + "type": ["__all__"], + "command": "bh_modules.bracketselect", + "args": {"select": "left"} + } + } + }, +``` + +The ```plugin``` paramter is a dictionary that contains 3 parameters to define what plugin should get run, with what arguments, and on what bracket defintion. + +- **type**: an array containing the bracket definition names that the plugin should be run on. Use ```__all__``` for all bracket definitions. +- **command**: the plugin to run. For internal plugins, they are referenced by ```bh_modules.```. For custom plugins, you should use the folder path releative to ```Packages```. So if I had a plugin called ```myplugin.py``` in my ```User``` folder, I would use ```User.myplugin```. +- **args**: a dictionary contianing the arguments to feed into the plugin. + +You create ```run instance``` plugins by deriving a class from the ```BracketPluginCommand``` class. Then you provide a method called ```plugin``` that returns the class. + +Class: + +- **BracketPluginCommand()** + +Parameters of BracketPluginCommand: + +- **edit**: sublime edit object +- **name**: name of tag definition being evaluated + +Attributes of BracketPluginCommand: + +- **view**: the sublime view containg the bracket (don't change this) +- **left**: a bracket region for the opening bracket (can be changed) +- **right**: a bracket region for the closing bracket (can be changed) +- **selection**: an array containing the selection that triggered the match (can be changed) + +Methods of BracketPluginCommand: + +- **run(edit, name, )**: (edit is a sublime edit object and name is the bracket definition being evaluated) + +Example (from foldbracket.py): +```python +import BracketHighlighter.bh_plugin as bh_plugin +import sublime + + +class FoldBrackets(bh_plugin.BracketPluginCommand): + def run(self, edit, name): + content = sublime.Region(self.left.end, self.right.begin) + new_content = [content] + if content.size() > 0: + if self.view.fold(content) == False: + new_content = self.view.unfold(content) + self.selection = new_content + + +def plugin(): + return FoldBrackets +``` + +# Credits +- pyparadigm: for his original efforts with SublimeBrackets and SublimeTagmatcher which originally BracketHighlighter was built off of and the inspiration behind the current implementation. +- BoundInCode: for his Tag icon + +# Version 2.0.0 +- Re-write of BracketHighlighter + +# Version Older +- See [Complete Changelog](https://github.com/facelessuser/BracketHighlighter/blob/BH2/CHANGELOG.md) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/Default.sublime-commands b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/Default.sublime-commands new file mode 100644 index 0000000..ebd4a05 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/Default.sublime-commands @@ -0,0 +1,25 @@ +[ + { + "caption": "Bracketeer: Indent Text", + "command": "bracketeer_indent" + }, + { + "caption": "Bracketeer: Select Inside Braces", + "command": "bracketeer_select" + }, + { + "caption": "Bracketeer: Insert curly brackets", + "command": "bracketeer", + "args": { "braces": "{}" } + }, + { + "caption": "Bracketeer: Insert square brackets", + "command": "bracketeer", + "args": { "braces": "[]" } + }, + { + "caption": "Bracketeer: Insert round brackets, aka parentheses", + "command": "bracketeer", + "args": { "braces": "()" } + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/Example.sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/Example.sublime-keymap new file mode 100644 index 0000000..7d87ac3 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/Example.sublime-keymap @@ -0,0 +1,276 @@ +[ + { "keys": ["super+]"], "command": "bracketeer_indent" }, + { "keys": ["ctrl+shift+["], "command": "bracketeer_select" }, + + { "keys": ["ctrl+["], "command": "bracketeer_goto", "args": { "goto": "left" } }, + { "keys": ["ctrl+]"], "command": "bracketeer_goto", "args": { "goto": "right" } }, + { "keys": ["ctrl+alt+["], "command": "bracketeer_goto", "args": { "goto": "both" } }, + { "keys": ["ctrl+alt+]"], "command": "bracketeer_goto", "args": { "goto": "both" } }, + //| + //| BRACKETEER + //| + { "keys": ["{"], "command": "bracketeer", "args": { "braces": "{}", "unindent": true } }, + { "keys": ["}"], "command": "bracketeer", "args": { "braces": "{}", "pressed": "}", "unindent": true } }, + { "keys": ["["], "command": "bracketeer", "args": { "braces": "[]" } }, + { "keys": ["]"], "command": "bracketeer", "args": { "braces": "[]", "pressed": "]" } }, + { "keys": ["("], "command": "bracketeer", "args": { "braces": "()" } }, + { "keys": [")"], "command": "bracketeer", "args": { "braces": "()", "pressed": ")" } }, + + //| reStructured Text + { "keys": ["alt+`"], "command": "bracketeer", "args": { "braces": "````", "pressed": "``" }, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.restructuredtext" } + ] + }, + { "keys": ["*"], "command": "bracketeer", "args": { "braces": "**", "pressed": "*" }, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.restructuredtext" } + ] + }, + + //| DJANGO CURLIES + // For django, liquid, jinja. All the grammars *I* have list 'source.smarty' as + // when the cursor is inside "{}"s + { "keys": ["{"], "command": "bracketeer", "args": { "braces": "{ }" }, "context": + [{ "key": "selector", "operator": "equal", "operand": "source.smarty" }] + }, + { "keys": ["{"], "command": "bracketeer", "args": { "braces": "{ }" }, "context": + [{ "key": "selector", "operator": "equal", "operand": "meta.brace.curly" }] + }, + { "keys": ["%"], "command": "bracketeer", "args": { "braces": "% %" }, "context": + [{ "key": "selector", "operator": "equal", "operand": "source.smarty" }] + }, + { "keys": ["%"], "command": "bracketeer", "args": { "braces": "% %" }, "context": + [ + { "key": "selector", "operator": "equal", "operand": "meta.brace.curly" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "<$", "match_all": true } + ] + }, + { "keys": ["%"], "command": "insert_snippet", "args": { "contents": " $1 %>$0" }, "context": + [ + { "key": "selector", "operator": "equal", "operand": "source.ruby" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "<%$", "match_all": true } + ] + }, + { "keys": [">"], "command": "insert_snippet", "args": { "contents": ">$1<% $0" }, "context": + [ + { "key": "selector", "operator": "equal", "operand": "source.ruby" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "%$", "match_all": true } + ] + }, + { "keys": ["="], "command": "insert_snippet", "args": { "contents": "= $1 %>$0" }, "context": + [ + { "key": "selector", "operator": "equal", "operand": "source.ruby" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "<%$", "match_all": true } + ] + }, + { "keys": ["-"], "command": "insert_snippet", "args": { "contents": "- $1 %>$0" }, "context": + [ + { "key": "selector", "operator": "equal", "operand": "source.ruby" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "<%$", "match_all": true } + ] + }, + { "keys": ["#"], "command": "bracketeer", "args": { "braces": "# #" }, "context": + [{ "key": "selector", "operator": "equal", "operand": "source.smarty" }] + }, + + //| QUOTES + { "keys": ["\""], "command": "bracketeer", "args": { "braces": "\"\"", "pressed": "\"" } }, + { "keys": ["ctrl+'","ctrl+'"], "command": "bracketeer", "args": { "braces": "\"\"\"\n\n\"\"\"" } }, + { "keys": ["'"], "command": "bracketeer", "args": { "braces": "''", "pressed": "'" } }, + { "keys": ["ctrl+'","'"], "command": "bracketeer", "args": { "braces": "'''\n\n'''" } }, + { "keys": ["`"], "command": "bracketeer", "args": { "braces": "``", "pressed": "`" } }, + { "keys": ["ctrl+'","`"], "command": "insert_snippet", "args": { "contents": "```${1:syntax}\n$0\n```" } }, + { "keys": ["«"], "command": "bracketeer", "args": { "braces": "«»" } }, + { "keys": ["»"], "command": "bracketeer", "args": { "braces": "«»", "pressed": "»" } }, + { "keys": ["‹"], "command": "bracketeer", "args": { "braces": "‹›" } }, + { "keys": ["›"], "command": "bracketeer", "args": { "braces": "‹›", "pressed": "›" } }, + { "keys": ["“"], "command": "bracketeer", "args": { "braces": "“”" } }, + { "keys": ["”"], "command": "bracketeer", "args": { "braces": "“”", "pressed": "”" } }, + { "keys": ["‘"], "command": "bracketeer", "args": { "braces": "‘’" } }, + { "keys": ["’"], "command": "bracketeer", "args": { "braces": "‘’", "pressed": "’" } }, + + //| + //| AUTO DELETE MATCHING '', "", [], etc. + //| + { "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Left Right.sublime-macro"}, "context": + [ + { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, + { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "\"$" }, + { "key": "following_text", "operator": "regex_contains", "operand": "^\"" } + ] + }, + { "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Left Right.sublime-macro"}, "context": + [ + { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, + { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "'$" }, + { "key": "following_text", "operator": "regex_contains", "operand": "^'" } + ] + }, + { "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Left Right.sublime-macro"}, "context": + [ + { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, + { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "`$" }, + { "key": "following_text", "operator": "regex_contains", "operand": "^`" } + ] + }, + { "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Left Right.sublime-macro"}, "context": + [ + { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, + { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "«$" }, + { "key": "following_text", "operator": "regex_contains", "operand": "^»" } + ] + }, + { "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Left Right.sublime-macro"}, "context": + [ + { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, + { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "‹$" }, + { "key": "following_text", "operator": "regex_contains", "operand": "^›" } + ] + }, + { "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Left Right.sublime-macro"}, "context": + [ + { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, + { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "“$" }, + { "key": "following_text", "operator": "regex_contains", "operand": "^”" } + ] + }, + { "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Left Right.sublime-macro"}, "context": + [ + { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, + { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "‘$" }, + { "key": "following_text", "operator": "regex_contains", "operand": "^’" } + ] + }, + { "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Left Right.sublime-macro"}, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.restructuredtext" }, + { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, + { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "\\*$" }, + { "key": "following_text", "operator": "regex_contains", "operand": "^\\*" } + ] + }, + + //| + //| Bracket and select + //| + { "keys": ["ctrl+alt+[", "backspace"], "command": "bracketeer", "args": { "braces": "", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "d"], "command": "bracketeer", "args": { "braces": ["do", "end"], "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "d"], "command": "bracketeer", "args": { "braces": ["do", "end"], "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "{"], "command": "bracketeer", "args": { "braces": "{}", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "{"], "command": "bracketeer", "args": { "braces": "{}", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", " "], "command": "bracketeer", "args": { "braces": " ", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", " "], "command": "bracketeer", "args": { "braces": " ", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "["], "command": "bracketeer", "args": { "braces": "[]", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "["], "command": "bracketeer", "args": { "braces": "[]", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "("], "command": "bracketeer", "args": { "braces": "()", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "("], "command": "bracketeer", "args": { "braces": "()", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "\""], "command": "bracketeer", "args": { "braces": "\"\"", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "\""], "command": "bracketeer", "args": { "braces": "\"\"", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "ctrl+shift+'"], "command": "bracketeer", "args": { "braces": "\"\"\"\"\"\"", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "ctrl+shift+'"], "command": "bracketeer", "args": { "braces": "\"\"\"\"\"\"", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "'"], "command": "bracketeer", "args": { "braces": "''", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "'"], "command": "bracketeer", "args": { "braces": "''", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "ctrl+'"], "command": "bracketeer", "args": { "braces": "''''''", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "ctrl+'"], "command": "bracketeer", "args": { "braces": "''''''", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "`"], "command": "bracketeer", "args": { "braces": "``", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "`"], "command": "bracketeer", "args": { "braces": "``", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "ctrl+`"], "command": "bracketeer", "args": { "braces": "``````", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "ctrl+`"], "command": "bracketeer", "args": { "braces": "``````", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "«"], "command": "bracketeer", "args": { "braces": "«»", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "«"], "command": "bracketeer", "args": { "braces": "«»", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "‹"], "command": "bracketeer", "args": { "braces": "‹›", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "‹"], "command": "bracketeer", "args": { "braces": "‹›", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "“"], "command": "bracketeer", "args": { "braces": "“”", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "“"], "command": "bracketeer", "args": { "braces": "“”", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "‘"], "command": "bracketeer", "args": { "braces": "‘’", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "‘"], "command": "bracketeer", "args": { "braces": "‘’", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "alt+`"], "command": "bracketeer", "args": { "braces": "````", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "alt+`"], "command": "bracketeer", "args": { "braces": "````", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "*"], "command": "bracketeer", "args": { "braces": "**", "select": true }, "context": + [ + { "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }, + { "key": "selector", "operator": "equal", "operand": "text.restructuredtext" } + ] + }, + { "keys": ["ctrl+alt+[", "*"], "command": "bracketeer", "args": { "braces": "**", "select": true, "replace": true }, "context": + [ + { "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }, + { "key": "selector", "operator": "equal", "operand": "text.restructuredtext" } + ] + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/README.md b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/README.md new file mode 100644 index 0000000..ef342ae --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/README.md @@ -0,0 +1,133 @@ +Bracketeer plugin for Sublime Text 2 +===================================== + +Some bracket manipulation, selection, and insertion commands. + + +Installation +------------ + +1. Using Package Control, install "Bracketeer" + +Or: + +1. Open the Sublime Text 2 Packages folder + + - OS X: ~/Library/Application Support/Sublime Text 2/Packages/ + - Windows: %APPDATA%/Sublime Text 2/Packages/ + - Linux: ~/.Sublime Text 2/Packages/ + +2. clone this repo +3. Install keymaps for the commands (see Example.sublime-keymap for my preferred keys) + +Commands +-------- + +`bracketeer`: Surrounds selected text with braces (or quotes - anything, really), and prevents indentation mishaps. + +`bracketeer_indent`: Indents sensibly - allows a clever use of enter, indent, and '{' to surround code in '{}'. See example below. + +`bracketeer_goto`: Goes to the matching bracket - either opener (ctrl+[), closer (ctrl+]), or *both* (ctrl+alt+[). + +`bracketeer_select`: Searches for matching brackets and selects what is inside, or expands the selection to include the brackets. + + +### bracketeer + + +Required args: + +`braces`: Two characters. Default key bindings support: + +* `{}` +* `[]` +* `()` +* `<>` +* `«»` +* `‹›` +* `""` +* `''` +* `“”` +* `‘’` +* `\`\`` + +Select some text and press one of these keys. The default Sublime Text braces will re-indent the text, and it looks really silly. This plugin indents sensibly. Helpful in languages that use curlies, e.g. `C`, `Java`, `PHP`. + +In addition, the "super+]" indent command is modified (using `bracketeer_indent`) so that the first and last lines are not indented. Makes it easy to add curly braces. Select some lines of code, with a blank line above and below. Or, if you like your braces on the same line as the `if|while|do`, put the start of the selection at the end of that line. + +Press `super+]`, then press "{". The block of code will be indented, leaving the code highlighted, then you can surround it in braces. + + 1. if ( a ) + 2. echo NULL; + + # add blank lines + 1. if ( a ) + 2. + 3. echo NULL; + 4. + + # select text + 1. if ( a ) + 2. | + 3. echo NULL; + 4. | + + # press super+] + 1. if ( a ) + 2. | + 3. echo NULL; + 4. | + + # press { + 1. if ( a ) + 2. { + 3. echo NULL; + 4. }| + + +### bracketeer_indent + + +Default key combination is super+] + +If the first line of selected text is empty (and keep in mind this *ignores* whatever text is to the left of the selection, so not necessarily an empty line), that line will not be indented. See example usage above. + + +### bracketeer_select + + +Default key combination is ctrl+shift+[ + +Expands the current region to include text *within* brackets, and if pressed again to include the brackets themselves. + +I will use '|' as the caret or selection start and end points: + + 1. do_something([1, '[', {'brace':'{', 'test'}])| + + # move caret into the 'test' string + 1. do_something([1, '[', {'brace':'{', 'te|st'}]) + + # press ctrl+shift+[ + # the first bracket it finds is the '}', so it will match {}s + # notice it will ignore the '{', which would otherwise look like the matching brace + 1. do_something([1, '[', {|'brace':'{', 'test'|}]) + + # press ctrl+shift+[ + # adds the {} to the selection + 1. do_something([1, '[', |{'brace':'{', 'test'}|]) + + # press ctrl+shift+[ + # selects between the []s. + 1. do_something([|1, '[', {'brace':'{', 'test'}|]) + + # press ctrl+shift+[ + # selects the []s. + 1. do_something(|[1, '[', {'brace':'{', 'test'}]|) + + # press ctrl+shift+[ + # selects the ()s. It would have expanded to select *between* the (), but that is what the selection already *was to start with* + 1. do_something|([1, '[', {'brace':'{', 'test'}])| + + # press ctrl+shift+[ + # does nothing. No more brackets to match! + 1. do_something|([1, '[', {'brace':'{', 'test'}])| diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/bracketeer.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/bracketeer.py new file mode 100644 index 0000000..372450b --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/bracketeer.py @@ -0,0 +1,393 @@ +# coding: utf8 +import sublime +import sublime_plugin +from sublime import Region +import re + +# for detecting "real" brackets in BracketeerCommand, and bracket matching in BracketeerBracketMatcher +OPENING_BRACKETS = ['{', '[', '('] +OPENING_BRACKET_LIKE = ['{', '[', '(', '"', "'", u'“', '‘', '«', '‹'] +CLOSING_BRACKETS = ['}', ']', ')'] +CLOSING_BRACKET_LIKE = ['}', ']', ')', '"', "'", u'”', '’', '»', '›'] +QUOTING_BRACKETS = ['\'', "\""] + + +class BracketeerCommand(sublime_plugin.TextCommand): + def run(self, edit, **kwargs): + e = self.view.begin_edit('bracketeer') + regions = [region for region in self.view.sel()] + + # sort by region.end() DESC + def get_end(region): + return region.end() + regions.sort(key=get_end, reverse=True) + + for region in regions: + self.run_each(edit, region, **kwargs) + + self.view.end_edit(e) + + def complicated_quote_checker(self, insert_braces, region, pressed, after, r_brace): + in_string_scope = self.view.score_selector(region.a, 'string') + in_double_string_scope = in_string_scope and self.view.score_selector(region.a, 'string.quoted.double') + in_single_string_scope = in_string_scope and self.view.score_selector(region.a, 'string.quoted.single') + at_eol = region.a == self.view.line(region.a).b + in_comment_scope = self.view.score_selector(region.a, 'comment') + in_text_scope = self.view.score_selector(region.a, 'text') + in_embedded_scope = self.view.score_selector(region.a, 'source.php') + self.view.score_selector(region.a, 'source.js') + in_text_scope = in_text_scope and not in_embedded_scope + + if pressed and pressed in QUOTING_BRACKETS and (in_comment_scope or in_text_scope or in_string_scope): + # if the cursor: + # (a) is preceded by odd numbers of '\'s? + if in_comment_scope: + scope_test = 'comment' + else: + scope_test = 'string' + begin_of_string = region.a + while begin_of_string and self.view.score_selector(begin_of_string - 1, scope_test): + begin_of_string -= 1 + check_a = self.view.substr(Region(begin_of_string, region.a)) + check_a = len(re.search(r'[\\]*$', check_a).group(0)) + check_a = check_a % 2 + + # (b) is an apostrophe and (inside double quotes or in text or comment scope) + check_b = (in_double_string_scope or in_text_scope or in_comment_scope) and pressed == "'" + + # (c) we are at the end of the line and pressed the closing quote + check_c = at_eol and ( + in_single_string_scope and pressed == "'" + or + in_double_string_scope and pressed == '"' + ) + + # then don't insert both, just insert the one. + if check_a or check_b or check_c: + return pressed + + def run_each(self, edit, region, braces='{}', pressed=None, unindent=False, select=False, replace=False): + self.view.sel().subtract(region) + if self.view.settings().get('translate_tabs_to_spaces'): + tab = ' ' * self.view.settings().get('tab_size') + else: + tab = "\t" + + row, col = self.view.rowcol(region.begin()) + indent_point = self.view.text_point(row, 0) + if indent_point < region.begin(): + indent = self.view.substr(Region(indent_point, region.begin())) + indent = re.match('[ \t]*', indent).group(0) + else: + indent = '' + line = self.view.substr(self.view.line(region.a)) + selection = self.view.substr(region) + + # for braces that have newlines ("""), insert the current line's indent + if isinstance(braces, list): + l_brace = braces[0] + r_brace = braces[1] + braces = ''.join(braces) + braces = braces.replace("\n", "\n" + indent) + length = len(l_brace) + else: + braces = braces.replace("\n", "\n" + indent) + length = len(braces) / 2 + l_brace = braces[:length] + r_brace = braces[length:] + + if region.empty(): + after = self.view.substr(Region(region.a, region.a + length)) + + insert_braces = braces + complicated_check = self.complicated_quote_checker(insert_braces, region, pressed, after, r_brace) + + if complicated_check: + insert_braces = complicated_check + elif pressed and after == r_brace and r_brace[-1] == pressed: # and (pressed not in QUOTING_BRACKETS or in_string_scope): + # in this case we pressed the closing character, and that's the character that is to the right + # so do nothing except advance cursor position + insert_braces = False + elif unindent and row > 0 and indent and line == indent: + # indent has the current line's indent + # get previous line's indent: + prev_point = self.view.text_point(row - 1, 0) + prev_line = self.view.line(prev_point) + prev_indent = self.view.substr(prev_line) + prev_indent = re.match('[ \t]*', prev_indent).group(0) + + if (not pressed or pressed == l_brace) and len(indent) > len(prev_indent) and indent[len(prev_indent):] == tab: + # move region.a back by 'indent' amount + region = Region(region.a - len(tab), region.b - len(tab)) + # and remove the tab + self.view.replace(edit, Region(region.a, region.a + len(tab) - 1), '') + elif pressed and pressed == r_brace: + if len(indent) == len(prev_indent): + # move region.a back by 'indent' amount + region = Region(region.a - len(tab), region.b - len(tab)) + # and remove the tab + self.view.replace(edit, Region(region.a, region.a + len(tab) - 1), '') + insert_braces = r_brace + elif pressed and pressed != l_brace: + # we pressed the closing bracket or quote. This *never* + insert_braces = r_brace + + if insert_braces: + self.view.insert(edit, region.a, insert_braces) + self.view.sel().add(Region(region.a + length, region.a + length)) + elif selection in QUOTING_BRACKETS and pressed in QUOTING_BRACKETS and selection != pressed: + # changing a quote from single <=> double, just insert the quote. + self.view.replace(edit, region, pressed) + self.view.sel().add(Region(region.end(), region.end())) + elif pressed and pressed != l_brace: + b = region.begin() + len(r_brace) + self.view.replace(edit, region, r_brace) + self.view.sel().add(Region(b, b)) + else: + substitute = self.view.substr(region) + replacement = l_brace + substitute + r_brace + # if we're inserting "real" brackets, not quotes: + real_brackets = l_brace in OPENING_BRACKETS and r_brace in CLOSING_BRACKETS + # check to see if entire lines are selected, and if so do some smart indenting + bol_is_nl = region.begin() == 0 or self.view.substr(region.begin() - 1) == "\n" + eol_is_nl = region.end() == self.view.size() - 1 or self.view.substr(region.end() - 1) == "\n" + if real_brackets and bol_is_nl and eol_is_nl: + indent = '' + final = '' + m = re.match('([ \t]*)' + tab, self.view.substr(region)) + if m: + indent = m.group(1) + final = "\n" + else: + substitute = tab + substitute + replacement = indent + l_brace + "\n" + substitute + indent + r_brace + final + b = region.begin() + len(replacement) - len("\n" + indent + r_brace + final) + else: + b = region.begin() + len(replacement) + + if replace and self.view.substr(region.begin() - 1) in OPENING_BRACKET_LIKE and self.view.substr(region.end()) in CLOSING_BRACKET_LIKE: + b -= 1 + self.view.replace(edit, Region(region.begin() - 1, region.end() + 1), replacement) + elif replace and self.view.substr(region.begin()) in OPENING_BRACKET_LIKE and self.view.substr(region.end() - 1) in CLOSING_BRACKET_LIKE: + replacement = l_brace + replacement[2:-2] + r_brace + b -= 2 + self.view.replace(edit, region, replacement) + l_brace = r_brace = '' + else: + self.view.replace(edit, region, replacement) + + if select: + self.view.sel().add(Region(b - len(replacement) + len(l_brace), b - len(r_brace))) + else: + self.view.sel().add(Region(b, b)) + + +class BracketeerIndentCommand(sublime_plugin.TextCommand): + def run(self, edit): + e = self.view.begin_edit('bracketeer') + if self.view.settings().get('translate_tabs_to_spaces'): + tab = ' ' * self.view.settings().get('tab_size') + else: + tab = "\t" + + regions = [region for region in self.view.sel()] + + # sort by region.end() DESC + def get_end(region): + return region.end() + regions.sort(key=get_end, reverse=True) + + for region in regions: + if region.empty(): + # insert tab at beginning of line + point = self.view.text_point(self.view.rowcol(region.a)[0], 0) + self.view.insert(edit, point, tab) + else: + # insert tab in front of lines 1:-1 + lines = self.view.substr(region).split("\n") + # just one line? indent it + if len(lines) == 1: + substitute = tab + lines[0] + "\n" + else: + default_settings = sublime.load_settings("bracketeer.sublime-settings") + dont_indent_list = default_settings.get('dont_indent') + + # lines that start with these strings don't get indented + def dont_indent(line): + return any(dont for dont in dont_indent_list if line[:len(dont)] == dont) + + # cursor is at start of line? indent that, too + if len(lines[0]) > 0 and not dont_indent(lines[0]): + substitute = tab + else: + substitute = '' + substitute += lines[0] + "\n" + + for line in lines[1:-1]: + if len(line): + if not dont_indent(line): + substitute += tab + substitute += line + substitute += "\n" + substitute += lines[-1] + self.view.replace(edit, region, substitute) + + self.view.end_edit(e) + + +class BracketeerBracketMatcher(sublime_plugin.TextCommand): + def find_brackets(self, region, closing_search_brackets=None): + match_map = { + '}': '{', + ']': '[', + ')': '(', + } + # find next brace in closing_search_brackets + if not closing_search_brackets: + closing_search_brackets = CLOSING_BRACKETS + elif isinstance(closing_search_brackets, basestring): + closing_search_brackets = [closing_search_brackets] + + opening_search_brackets = [match_map[bracket] for bracket in closing_search_brackets] + begin_point = region.begin() - 1 + end_point = region.end() + + # LaTEX: if selection directly preceeds \right, examine the string that includes \right instead of the actual selection + if self.view.substr( Region(end_point, min(end_point+6,self.view.size())) ) == '\\right': end_point += 6 + # /LaTEX + + # end_point gets incremented immediately, which skips the first + # character, *unless* the selection is empty, in which case the + # inner contents should be selected before scanning past + if region.empty(): + # if the current character is a bracket, and the character to the left is the + # *matching* bracket, don't match the empty contents + c = self.view.substr(end_point) + if c in closing_search_brackets and self.view.substr(end_point - 1) == match_map[c]: + # cursor is between two brackets - select them and return + return Region(begin_point, end_point + 1) + + else: + # if the selection is inside two brackets, select them and return + c1 = self.view.substr(begin_point) + c2 = self.view.substr(end_point) + + if c2 in closing_search_brackets and c1 == match_map[c2]: + # LaTEX: if \left preceeds selection, select it as well + if self.view.substr(Region(max(begin_point-5,0), begin_point))=='\left': begin_point -= 5 + # /LaTEX + return Region(begin_point, end_point + 1) + + # scan forward searching for a closing bracket. + started_in_string = bool(self.view.score_selector(end_point, 'string') or self.view.score_selector(begin_point, 'string')) + bracket_count = 0 + while True: + c = self.view.substr(end_point) + if started_in_string or not self.view.score_selector(end_point, 'string'): + if bracket_count <= 0 and c in closing_search_brackets: + break + elif c in opening_search_brackets and c in OPENING_BRACKETS: + bracket_count += 1 + elif c in closing_search_brackets and c in CLOSING_BRACKETS: + bracket_count -= 1 + + end_point += 1 + if end_point >= self.view.size(): + return None + + # found a bracket, scan backwards until matching bracket is found. + # matching bracket is determined by counting closing brackets (+1) + # and opening brackets (-1) and when the count is zero and the + # matching opening bracket is found + look_for = match_map[c] + while True: + c = self.view.substr(begin_point) + if started_in_string or not self.view.score_selector(begin_point, 'string'): + if bracket_count == 0 and c == look_for: + break + elif c in opening_search_brackets and c in OPENING_BRACKETS: + bracket_count += 1 + elif c in closing_search_brackets and c in CLOSING_BRACKETS: + bracket_count -= 1 + begin_point -= 1 + if begin_point < 0: + return None + # the current point is to the left of the opening bracket, + # I want it to be to the right. + begin_point += 1 + + # LaTEX: if selection ends in \right, don't select it + if self.view.substr( Region(max(end_point-6,0), end_point) ) == '\\right': end_point -= 6 + # /LaTEX + return Region(begin_point, end_point) + + +class BracketeerGotoCommand(BracketeerBracketMatcher): + def run(self, edit, **kwargs): + e = self.view.begin_edit('bracketeer') + regions = [region for region in self.view.sel()] + + # sort by region.end() DESC + def get_end(region): + return region.end() + regions.sort(key=get_end, reverse=True) + + for region in regions: + self.run_each(edit, region, **kwargs) + self.view.end_edit(e) + + def run_each(self, edit, region, goto): + cursor = region.b + if goto == "left" and self.view.substr(cursor - 1) == '{': + cursor -= 1 + elif goto == "both" and self.view.substr(cursor) == '{': + cursor += 1 + elif goto in ["left", "both"] and self.view.substr(cursor - 1) == '}': + cursor -= 1 + + new_region = self.find_brackets(Region(cursor, cursor), '}') + + if new_region: + self.view.sel().subtract(region) + a = new_region.begin() + b = new_region.end() + if self.view.substr(a) in OPENING_BRACKETS: + a += 1 + if self.view.substr(b) in CLOSING_BRACKETS: + b += 1 + + if goto == "left": + new_region = Region(a, a) + self.view.sel().add(new_region) + self.view.show(new_region) + elif goto == "right": + new_region = Region(b, b) + self.view.sel().add(new_region) + self.view.show(new_region) + elif goto == "both": + self.view.sel().add(Region(a, a)) + self.view.sel().add(Region(b, b)) + self.view.show(new_region.b) + else: + raise ValueError("`goto` should have a value of 'left', 'right', or 'both'), not '" + goto + '"') + + +class BracketeerSelectCommand(BracketeerBracketMatcher): + def run(self, edit, **kwargs): + e = self.view.begin_edit('bracketeer') + regions = [region for region in self.view.sel()] + + # sort by region.end() DESC + def get_end(region): + return region.end() + regions.sort(key=get_end, reverse=True) + + for region in regions: + self.run_each(edit, region, **kwargs) + self.view.end_edit(e) + + def run_each(self, edit, region): + new_region = self.find_brackets(region) + if new_region: + self.view.sel().subtract(region) + self.view.sel().add(new_region) + self.view.show(new_region.b) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/bracketeer.sublime-settings b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/bracketeer.sublime-settings new file mode 100644 index 0000000..e525157 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/bracketeer.sublime-settings @@ -0,0 +1,3 @@ +{ + "dont_indent": ["", "{%", "%}", "{#", "#}"] +} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/package-metadata.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/package-metadata.json new file mode 100644 index 0000000..9406bac --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/package-metadata.json @@ -0,0 +1 @@ +{"url": "https://github.com/colinta/SublimeBracketeer", "version": "1.5.2", "description": "Some bracket manipulation, selection, and insertion commands."} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/package.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/package.json new file mode 100644 index 0000000..1c084b6 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Bracketeer/package.json @@ -0,0 +1,7 @@ +{ + "repo": "SublimeBracketeer", + "name": "Bracketeer", + "description": "Some bracket manipulation, selection, and insertion commands.", + "author": "Colin Thomas-Arnold (colinta)", + "homepage": "https://github.com/colinta/SublimeBracketeer" +} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/Default.sublime-commands b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/Default.sublime-commands new file mode 100644 index 0000000..f519741 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/Default.sublime-commands @@ -0,0 +1,42 @@ +[ + { + "caption": "Clipboard Manager: Cut", + "command": "clipboard_manager_cut" + }, + { + "caption": "Clipboard Manager: Copy", + "command": "clipboard_manager_copy" + }, + { + "caption": "Clipboard Manager: Paste", + "command": "clipboard_manager_paste" + }, + { + "caption": "Clipboard Manager: Next & Paste", + "command": "clipboard_manager_next_and_paste" + }, + { + "caption": "Clipboard Manager: Previous & Paste", + "command": "clipboard_manager_previous_and_paste" + }, + { + "caption": "Clipboard Manager: Next", + "command": "clipboard_manager_next" + }, + { + "caption": "Clipboard Manager: Previous", + "command": "clipboard_manager_previous" + }, + { + "caption": "Clipboard Manager: Choose & Paste", + "command": "clipboard_manager_choose_and_paste" + }, + { + "caption": "Clipboard Manager: Show History", + "command": "clipboard_manager_show" + }, + { + "caption": "Clipboard Manager: Show Registers", + "command": "clipboard_manager_show_registers" + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/Example.sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/Example.sublime-keymap new file mode 100644 index 0000000..f9dbca8 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/Example.sublime-keymap @@ -0,0 +1,96 @@ +[ + { "keys": ["super+x"], "command": "clipboard_manager_cut" }, + { "keys": ["super+c"], "command": "clipboard_manager_copy" }, + + { "keys": ["super+v"], "command": "clipboard_manager_paste", "args": { "indent": true } }, + { "keys": ["super+ctrl+v"], "command": "clipboard_manager_paste", "args": { "indent": false } }, + + { "keys": ["super+alt+v"], "command": "clipboard_manager_next_and_paste" }, + { "keys": ["super+shift+v"], "command": "clipboard_manager_previous_and_paste" }, + + { "keys": ["super+pageup"], "command": "clipboard_manager_next" }, + { "keys": ["super+pagedown"], "command": "clipboard_manager_previous" }, + { "keys": ["super+home"], "command": "clipboard_manager_show" }, + { "keys": ["super+end"], "command": "clipboard_manager_show_registers" }, + + { "keys": ["super+alt+ctrl+v"], "command": "clipboard_manager_choose_and_paste" }, + + { "keys": ["super+ctrl+shift+v", "?"], "command": "clipboard_manager_show_registers" }, + { "keys": ["super+ctrl+shift+v", "/"], "command": "clipboard_manager_show" }, + + { "keys": ["super+ctrl+shift+c", "1"], "command": "clipboard_manager_copy_to_register", "args": { "register": "1" } }, + { "keys": ["super+ctrl+shift+c", "2"], "command": "clipboard_manager_copy_to_register", "args": { "register": "2" } }, + { "keys": ["super+ctrl+shift+c", "3"], "command": "clipboard_manager_copy_to_register", "args": { "register": "3" } }, + { "keys": ["super+ctrl+shift+c", "4"], "command": "clipboard_manager_copy_to_register", "args": { "register": "4" } }, + { "keys": ["super+ctrl+shift+c", "5"], "command": "clipboard_manager_copy_to_register", "args": { "register": "5" } }, + { "keys": ["super+ctrl+shift+c", "6"], "command": "clipboard_manager_copy_to_register", "args": { "register": "6" } }, + { "keys": ["super+ctrl+shift+c", "7"], "command": "clipboard_manager_copy_to_register", "args": { "register": "7" } }, + { "keys": ["super+ctrl+shift+c", "8"], "command": "clipboard_manager_copy_to_register", "args": { "register": "8" } }, + { "keys": ["super+ctrl+shift+c", "9"], "command": "clipboard_manager_copy_to_register", "args": { "register": "9" } }, + { "keys": ["super+ctrl+shift+c", "0"], "command": "clipboard_manager_copy_to_register", "args": { "register": "0" } }, + + { "keys": ["super+ctrl+shift+c", "a"], "command": "clipboard_manager_copy_to_register", "args": { "register": "a" } }, + { "keys": ["super+ctrl+shift+c", "b"], "command": "clipboard_manager_copy_to_register", "args": { "register": "b" } }, + { "keys": ["super+ctrl+shift+c", "c"], "command": "clipboard_manager_copy_to_register", "args": { "register": "c" } }, + { "keys": ["super+ctrl+shift+c", "d"], "command": "clipboard_manager_copy_to_register", "args": { "register": "d" } }, + { "keys": ["super+ctrl+shift+c", "e"], "command": "clipboard_manager_copy_to_register", "args": { "register": "e" } }, + { "keys": ["super+ctrl+shift+c", "f"], "command": "clipboard_manager_copy_to_register", "args": { "register": "f" } }, + { "keys": ["super+ctrl+shift+c", "g"], "command": "clipboard_manager_copy_to_register", "args": { "register": "g" } }, + { "keys": ["super+ctrl+shift+c", "h"], "command": "clipboard_manager_copy_to_register", "args": { "register": "h" } }, + { "keys": ["super+ctrl+shift+c", "i"], "command": "clipboard_manager_copy_to_register", "args": { "register": "i" } }, + { "keys": ["super+ctrl+shift+c", "j"], "command": "clipboard_manager_copy_to_register", "args": { "register": "j" } }, + { "keys": ["super+ctrl+shift+c", "k"], "command": "clipboard_manager_copy_to_register", "args": { "register": "k" } }, + { "keys": ["super+ctrl+shift+c", "l"], "command": "clipboard_manager_copy_to_register", "args": { "register": "l" } }, + { "keys": ["super+ctrl+shift+c", "m"], "command": "clipboard_manager_copy_to_register", "args": { "register": "m" } }, + { "keys": ["super+ctrl+shift+c", "n"], "command": "clipboard_manager_copy_to_register", "args": { "register": "n" } }, + { "keys": ["super+ctrl+shift+c", "o"], "command": "clipboard_manager_copy_to_register", "args": { "register": "o" } }, + { "keys": ["super+ctrl+shift+c", "p"], "command": "clipboard_manager_copy_to_register", "args": { "register": "p" } }, + { "keys": ["super+ctrl+shift+c", "q"], "command": "clipboard_manager_copy_to_register", "args": { "register": "q" } }, + { "keys": ["super+ctrl+shift+c", "r"], "command": "clipboard_manager_copy_to_register", "args": { "register": "r" } }, + { "keys": ["super+ctrl+shift+c", "s"], "command": "clipboard_manager_copy_to_register", "args": { "register": "s" } }, + { "keys": ["super+ctrl+shift+c", "t"], "command": "clipboard_manager_copy_to_register", "args": { "register": "t" } }, + { "keys": ["super+ctrl+shift+c", "u"], "command": "clipboard_manager_copy_to_register", "args": { "register": "u" } }, + { "keys": ["super+ctrl+shift+c", "v"], "command": "clipboard_manager_copy_to_register", "args": { "register": "v" } }, + { "keys": ["super+ctrl+shift+c", "w"], "command": "clipboard_manager_copy_to_register", "args": { "register": "w" } }, + { "keys": ["super+ctrl+shift+c", "x"], "command": "clipboard_manager_copy_to_register", "args": { "register": "x" } }, + { "keys": ["super+ctrl+shift+c", "y"], "command": "clipboard_manager_copy_to_register", "args": { "register": "y" } }, + { "keys": ["super+ctrl+shift+c", "z"], "command": "clipboard_manager_copy_to_register", "args": { "register": "z" } }, + + { "keys": ["super+ctrl+shift+v", "1"], "command": "clipboard_manager_paste_from_register", "args": { "register": "1" } }, + { "keys": ["super+ctrl+shift+v", "2"], "command": "clipboard_manager_paste_from_register", "args": { "register": "2" } }, + { "keys": ["super+ctrl+shift+v", "3"], "command": "clipboard_manager_paste_from_register", "args": { "register": "3" } }, + { "keys": ["super+ctrl+shift+v", "4"], "command": "clipboard_manager_paste_from_register", "args": { "register": "4" } }, + { "keys": ["super+ctrl+shift+v", "5"], "command": "clipboard_manager_paste_from_register", "args": { "register": "5" } }, + { "keys": ["super+ctrl+shift+v", "6"], "command": "clipboard_manager_paste_from_register", "args": { "register": "6" } }, + { "keys": ["super+ctrl+shift+v", "7"], "command": "clipboard_manager_paste_from_register", "args": { "register": "7" } }, + { "keys": ["super+ctrl+shift+v", "8"], "command": "clipboard_manager_paste_from_register", "args": { "register": "8" } }, + { "keys": ["super+ctrl+shift+v", "9"], "command": "clipboard_manager_paste_from_register", "args": { "register": "9" } }, + { "keys": ["super+ctrl+shift+v", "0"], "command": "clipboard_manager_paste_from_register", "args": { "register": "0" } }, + + { "keys": ["super+ctrl+shift+v", "a"], "command": "clipboard_manager_paste_from_register", "args": { "register": "a" } }, + { "keys": ["super+ctrl+shift+v", "b"], "command": "clipboard_manager_paste_from_register", "args": { "register": "b" } }, + { "keys": ["super+ctrl+shift+v", "c"], "command": "clipboard_manager_paste_from_register", "args": { "register": "c" } }, + { "keys": ["super+ctrl+shift+v", "d"], "command": "clipboard_manager_paste_from_register", "args": { "register": "d" } }, + { "keys": ["super+ctrl+shift+v", "e"], "command": "clipboard_manager_paste_from_register", "args": { "register": "e" } }, + { "keys": ["super+ctrl+shift+v", "f"], "command": "clipboard_manager_paste_from_register", "args": { "register": "f" } }, + { "keys": ["super+ctrl+shift+v", "g"], "command": "clipboard_manager_paste_from_register", "args": { "register": "g" } }, + { "keys": ["super+ctrl+shift+v", "h"], "command": "clipboard_manager_paste_from_register", "args": { "register": "h" } }, + { "keys": ["super+ctrl+shift+v", "i"], "command": "clipboard_manager_paste_from_register", "args": { "register": "i" } }, + { "keys": ["super+ctrl+shift+v", "j"], "command": "clipboard_manager_paste_from_register", "args": { "register": "j" } }, + { "keys": ["super+ctrl+shift+v", "k"], "command": "clipboard_manager_paste_from_register", "args": { "register": "k" } }, + { "keys": ["super+ctrl+shift+v", "l"], "command": "clipboard_manager_paste_from_register", "args": { "register": "l" } }, + { "keys": ["super+ctrl+shift+v", "m"], "command": "clipboard_manager_paste_from_register", "args": { "register": "m" } }, + { "keys": ["super+ctrl+shift+v", "n"], "command": "clipboard_manager_paste_from_register", "args": { "register": "n" } }, + { "keys": ["super+ctrl+shift+v", "o"], "command": "clipboard_manager_paste_from_register", "args": { "register": "o" } }, + { "keys": ["super+ctrl+shift+v", "p"], "command": "clipboard_manager_paste_from_register", "args": { "register": "p" } }, + { "keys": ["super+ctrl+shift+v", "q"], "command": "clipboard_manager_paste_from_register", "args": { "register": "q" } }, + { "keys": ["super+ctrl+shift+v", "r"], "command": "clipboard_manager_paste_from_register", "args": { "register": "r" } }, + { "keys": ["super+ctrl+shift+v", "s"], "command": "clipboard_manager_paste_from_register", "args": { "register": "s" } }, + { "keys": ["super+ctrl+shift+v", "t"], "command": "clipboard_manager_paste_from_register", "args": { "register": "t" } }, + { "keys": ["super+ctrl+shift+v", "u"], "command": "clipboard_manager_paste_from_register", "args": { "register": "u" } }, + { "keys": ["super+ctrl+shift+v", "v"], "command": "clipboard_manager_paste_from_register", "args": { "register": "v" } }, + { "keys": ["super+ctrl+shift+v", "w"], "command": "clipboard_manager_paste_from_register", "args": { "register": "w" } }, + { "keys": ["super+ctrl+shift+v", "x"], "command": "clipboard_manager_paste_from_register", "args": { "register": "x" } }, + { "keys": ["super+ctrl+shift+v", "y"], "command": "clipboard_manager_paste_from_register", "args": { "register": "y" } }, + { "keys": ["super+ctrl+shift+v", "z"], "command": "clipboard_manager_paste_from_register", "args": { "register": "z" } } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/README.md b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/README.md new file mode 100644 index 0000000..2878e59 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/README.md @@ -0,0 +1,123 @@ +Clipboard Manager plugin for Sublime Text 2 +=========================================== + +A version of the Sublime Text 2 plugin at +that makes for TextMate-like clipboard history. + +Originally written by AJ Palkovic ([ajpalkovic](https://github.com/ajpalkovic/SublimePlugins)), +modified by Martin Aspeli ([optilude](https://gist.github.com/1132507)), and +further modified and packaged for `Package Control` by Colin Thomas-Arnold +([colinta](https://github.com/colinta/SublimeClipboardManager)) + +My version of this plugin *does not use* `clipboard_history` as the prefix. See +the full command-list below. + +Installation +------------ + +1. Using Package Control, install "Clipboard Manager" + +Or: + +1. Open the Sublime Text 2 Packages folder + + - OS X: ~/Library/Application Support/Sublime Text 2/Packages/ + - Windows: %APPDATA%/Sublime Text 2/Packages/ + - Linux: ~/.Sublime Text 2/Packages/ + +2. clone this repo +3. Install keymaps for the commands (see Example.sublime-keymap for my preferred keys) + +Commands +-------- + +**The basics** + +`clipboard_manager_cut`: Self Explanatory + +`clipboard_manager_copy`: Self Explanatory + +`clipboard_manager_paste`: Self Explanatory. + +*Options*: indent (default: False): Determines whether to use the `paste` or +`paste_and_indent` built-in command. + +- - - - - - + +**Navigating clipboard history** + +`clipboard_manager_next_and_paste` (`super+alt+v`) + +Goes to the next entry in the history and pastes it. +*Options*: indent (default: `False`) + +`clipboard_manager_previous_and_paste` (`super+shift+v`) + +Goes to the previous entry in the history and pastes it. +*Options*: indent (default: `False`) + +`clipboard_manager_next` (`super+pageup` aka `super+fn+up`) + +Goes to the next entry in the history, but doesn't paste. (the content will +appear as a status message) + +`clipboard_manager_previous` (`super+pagedown` aka `super+fn+down`) + +Goes to the previous entry in the history, but doesn't paste. (the content will +appear as a status message) + +`clipboard_manager_choose_and_paste` (`super+ctrl+alt+v`) + +Shows the clipboard history in a "quick panel". + +`clipboard_manager_show` (`super+ctrl+shift+v, /`) + +Shows the clipboard history in an "output panel", and points to the current +clipboard item. This was mostly useful for development, but you might find it +beneficial as well. + +- - - - - - + +**Registers** + +Right now registers do not add/remove from the clipboard history. *This may +change!!* I would appreciate feedback about this feature. + +`clipboard_manager_copy_to_register` (there are a ton, e.g. `super+ctrl+shift+c, 1`, `super+ctrl+shift+c, a`) + +Puts the selection into a `register`. The example keymap includes a register +binding for every number and letter. Register keys should be single characters. + +`clipboard_manager_paste_from_register` (`super+ctrl+shift+v, 1`, `super+ctrl+shift+v, a`) + +Pastes the contents of a `register`. Again, there are lots of example key +bindings. + +`clipboard_manager_show_registers` (`super+ctrl+shift+v, ?`) + +Shows the clipboard registers in an "output panel", similar to +`clipboard_manager_show`. + +- - - - - - + +**Helpful Tips** + +There are two ways to find out what you've got hanging out in your clipboard +history, you should use both. The `clipboard_manager_choose_and_paste` command +is your goto. It uses the fuzzy finder input panel, so you can quickly find and +paste the entry you want. + +The other useful trick is to use `clipboard_manager_show` to show an output +panel at the bottom of the screen. As you scroll through history using +`clipboard_manager_next` and `clipboard_manager_previous`, it will update that +panel, with an arrow pointing the current entry. Then you can +`clipboard_manager_next_and_paste`, and it will get updated then, too. Keeps +you sane if you're doing something crazy. + +If you've got a repetive task to do, with lots of copy/pastes, use registers. +They do not get affected by usual copy/pasting, so you can rest assured that +your work flow will not get affected. The keyboard shortcuts are unfortunately +quite verbose (`super+ctrl+shift+c, letter/digit`), but look at +Example.sublime-keymap and you'll see that it is easy to assign a quicker +shortcut for registers you like to use. Registers do not have to be one letter, +any string can be used as the key. diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/clipboard_manager.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/clipboard_manager.py new file mode 100644 index 0000000..272d0ca --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/clipboard_manager.py @@ -0,0 +1,230 @@ +import sublime +import sublime_plugin + + +class HistoryList(list): + """ + List type for storing the history. + Maintains a "pointer" to the current clipboard item + """ + registers = {} + SIZE = 256 + __index = 0 + + def show(self): + ret = "" + ret += " CLIPBOARD HISTORY (%d)\n" % len(self) + ret += "====================%s==\n" % ("=" * len(str(len(self)))) + for i, item in enumerate(self): + if i == self.__index: + ret += '--> ' + else: + ret += ' ' + item = item.replace("\t", '\\t') + item = item.replace("\r\n", "\n") + item = item.replace("\r", "\n") + item = item.replace("\n", "\n" + ' > ') + ret += u'{i:>3}. {item}\n'.format(i=str(i + 1)[-3:], item=item) + return ret + + def show_registers(self): + ret = "" + ret += " CLIPBOARD REGISTERS (%d)\n" % len(self.registers.items()) + ret += "=====================%s==\n" % ("=" * len(str(len(self.registers.items())))) + for key, item in self.registers.iteritems(): + item = item.replace("\t", '\\t') + item = item.replace("\r\n", "\n") + item = item.replace("\r", "\n") + item = item.replace("\n", "\n" + ' > ') + ret += u'{key:<1}: {item}\n'.format(key=key, item=item) + return ret + + def register(self, register, *args): + if args: + if len(args) == 1: + copy = args[0] + else: + copy = "\n".join(args) + self.registers[register] = copy + copy = copy.replace("\t", "\\t") + copy = copy.replace("\n", "\\n") + copy = copy.replace("\r", "\\r") + sublime.status_message('Set Clipboard Register "{0}" to "{1}"'.format(register, copy)) + else: + return self.registers[register] + + def append(self, item): + """ + Appends to the history only if it isn't the current item. + """ + if not self or self[self.__index] != item: + self.insert(0, item) + self.__index = 0 + if len(self) > self.SIZE: + del self[self.SIZE:] + + def current(self): + if len(self) == 0: + return None + return self[self.__index] + + def at(self, idx): + self.__index = (idx if idx < len(self) else 0) + self.status() + + def next(self): + if self.__index > 0: + self.__index -= 1 + self.status() + + def previous(self): + if self.__index < len(self) - 1: + self.__index += 1 + self.status() + + def first(self): + """"first" actually kind of means "last", since this is a FIFO stack""" + self.__index = len(self) - 1 + self.status() + + def last(self): + """"last" actually kind of means "first", since this is a FIFO stack""" + self.__index = 0 + self.status() + + def status(self): + copy = self.current() + copy = copy.replace("\t", "\\t") + copy = copy.replace("\n", "\\n") + copy = copy.replace("\r", "\\r") + sublime.status_message(u'Set Clipboard to "{copy}"'.format(copy=copy)) + sublime.set_clipboard(self.current()) + + +HISTORY = HistoryList([sublime.get_clipboard()]) + + +def append_clipboard(): + # append the contents of the clipboard to the history + HISTORY.append(sublime.get_clipboard()) + + +class ClipboardManagerPaste(sublime_plugin.TextCommand): + def run(self, edit, indent=False): + if indent: + self.view.run_command('paste_and_indent') + else: + self.view.run_command('paste') + + +class ClipboardManagerCut(sublime_plugin.TextCommand): + def run(self, edit): + # First run sublime's command to extract the selected text. + # This will set the cut/copy'd data on the clipboard which we can easily steal without recreating the cut/copy logic. + self.view.run_command('cut') + append_clipboard() + self.view.window().run_command('clipboard_manager_show', {'show': False}) + + +class ClipboardManagerCopy(sublime_plugin.TextCommand): + def run(self, edit): + self.view.run_command('copy') + append_clipboard() + self.view.window().run_command('clipboard_manager_show', {'show': False}) + + +class ClipboardManagerCopyToRegister(sublime_plugin.TextCommand): + def run(self, edit, register): + self.view.run_command('copy') + HISTORY.register(register, sublime.get_clipboard()) + self.view.window().run_command('clipboard_manager_show_registers', {'show': False}) + + +class ClipboardManagerPasteFromRegister(sublime_plugin.TextCommand): + def run(self, edit, register): + sublime.set_clipboard(HISTORY.register(register)) + self.view.run_command('paste') + + +class ClipboardManagerNext(sublime_plugin.TextCommand): + def run(self, edit): + HISTORY.next() + self.view.window().run_command('clipboard_manager_show', {'show': False}) + + +class ClipboardManagerNextAndPaste(sublime_plugin.TextCommand): + def run(self, edit, indent=False): + HISTORY.next() + if indent: + self.view.run_command('paste_and_indent') + else: + self.view.run_command('paste') + self.view.window().run_command('clipboard_manager_show', {'show': False}) + + +class ClipboardManagerPrevious(sublime_plugin.TextCommand): + def run(self, edit): + HISTORY.previous() + self.view.window().run_command('clipboard_manager_show', {'show': False}) + + +class ClipboardManagerPreviousAndPaste(sublime_plugin.TextCommand): + def run(self, edit, indent=False): + HISTORY.previous() + if indent: + self.view.run_command('paste_and_indent') + else: + self.view.run_command('paste') + self.view.window().run_command('clipboard_manager_show', {'show': False}) + + +class ClipboardManagerShow(sublime_plugin.WindowCommand): + def run(self, show=True): + v = self.window.get_output_panel('clipboard_manager') + e = v.begin_edit('clipboard_manager') + v.replace(e, sublime.Region(0, v.size()), '') + v.insert(e, 0, HISTORY.show()) + v.end_edit(e) + if show: + self.window.run_command('show_panel', {'panel': 'output.clipboard_manager'}) + + +class ClipboardManagerShowRegisters(sublime_plugin.WindowCommand): + def run(self, show=True): + v = self.window.get_output_panel('clipboard_manager') + e = v.begin_edit('clipboard_manager') + v.replace(e, sublime.Region(0, v.size()), '') + v.insert(e, 0, HISTORY.show_registers()) + v.end_edit(e) + if show: + self.window.run_command('show_panel', {'panel': 'output.clipboard_manager'}) + + +class ClipboardManagerChooseAndPaste(sublime_plugin.TextCommand): + def run(self, edit): + def format(line): + return line.replace('\n', '\\n')[:64] + + lines = [] + line_map = {} + # filter out duplicates, keeping the first instance, and format + for i, line in enumerate(HISTORY): + if i == HISTORY.index(line): + line_map[len(lines)] = i + lines.append(format(line)) + + def on_done(idx): + if idx >= 0: + idx = line_map[idx] + HISTORY.at(idx) + self.view.run_command('paste') + + if lines: + sublime.active_window().show_quick_panel(lines, on_done) + else: + sublime.status_message('Nothing in history') + + +class ClipboardManagerEventListener(sublime_plugin.EventListener): + def on_activated(self, view): + append_clipboard() diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/package-metadata.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/package-metadata.json new file mode 100644 index 0000000..11a74c2 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/package-metadata.json @@ -0,0 +1 @@ +{"url": "https://gist.github.com/1590661", "version": "1.2.3", "description": "A version of the Sublime Text 2 plugin at that makes for TextMate-like clipboard history."} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/package.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/package.json new file mode 100644 index 0000000..ea57497 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Clipboard Manager/package.json @@ -0,0 +1,7 @@ +{ + "repo": "SublimeClipboardManager", + "name": "Clipboard Manager", + "description": "A version of the Sublime Text 2 plugin at that makes for TextMate-like clipboard history.", + "author": "AJ Palkovic (ajpalkovic), Martin Aspeli (optilude) and Colin Thomas-Arnold (colinta)", + "homepage": "https://gist.github.com/1590661" +} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/.gitignore b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/.gitignore new file mode 100644 index 0000000..7e99e36 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/.gitignore @@ -0,0 +1 @@ +*.pyc \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Default (Linux).sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Default (Linux).sublime-keymap new file mode 100644 index 0000000..e86b53b --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Default (Linux).sublime-keymap @@ -0,0 +1,22 @@ +[ + { + "keys": ["ctrl+;", ""], + "command": "easy_motion", + "args": {"select_text": false} + }, + { + "keys": ["ctrl+;", "enter"], + "command": "easy_motion", + "args": {"select_text": false, "character": "enter"} + }, + { + "keys": ["ctrl+shift+;", ""], + "command": "easy_motion", + "args": {"select_text": true} + }, + { + "keys": ["ctrl+shift+;", "enter"], + "command": "easy_motion", + "args": {"select_text": true, "character": "enter"} + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Default (OSX).sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Default (OSX).sublime-keymap new file mode 100644 index 0000000..83db6b5 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Default (OSX).sublime-keymap @@ -0,0 +1,22 @@ +[ + { + "keys": ["super+;", ""], + "command": "easy_motion", + "args": {"select_text": false} + }, + { + "keys": ["super+;", "enter"], + "command": "easy_motion", + "args": {"select_text": false, "character": "enter"} + }, + { + "keys": ["super+shift+;", ""], + "command": "easy_motion", + "args": {"select_text": true} + }, + { + "keys": ["super+shift+;", "enter"], + "command": "easy_motion", + "args": {"select_text": true, "character": "enter"} + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Default (Windows).sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Default (Windows).sublime-keymap new file mode 100644 index 0000000..e86b53b --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Default (Windows).sublime-keymap @@ -0,0 +1,22 @@ +[ + { + "keys": ["ctrl+;", ""], + "command": "easy_motion", + "args": {"select_text": false} + }, + { + "keys": ["ctrl+;", "enter"], + "command": "easy_motion", + "args": {"select_text": false, "character": "enter"} + }, + { + "keys": ["ctrl+shift+;", ""], + "command": "easy_motion", + "args": {"select_text": true} + }, + { + "keys": ["ctrl+shift+;", "enter"], + "command": "easy_motion", + "args": {"select_text": true, "character": "enter"} + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Default.sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Default.sublime-keymap new file mode 100644 index 0000000..8d1e0de --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Default.sublime-keymap @@ -0,0 +1,23 @@ +[ + /* haven't figured out why yet, but for some reason you need to hit ctrl+c or escape twice to get it to deactivate jump targets */ + { + "keys": ["ctrl+c"], + "command": "deactivate_jump_targets", "context": [{"key": "setting.easy_motion_mode"}] + }, + { + "keys": ["escape"], + "command": "deactivate_jump_targets", "context": [{"key": "setting.easy_motion_mode"}] + }, + { + "keys": [""], + "command": "jump_to", "context": [{"key": "setting.easy_motion_mode"}] + }, + { + "keys": ["enter"], + "command": "show_jump_group", "context": [{"key": "setting.easy_motion_mode"}] + }, + { + "keys": ["shift+enter"], + "command": "show_jump_group", "context": [{"key": "setting.easy_motion_mode"}], "args": {"next": false} + } +] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/EasyMotion.sublime-settings b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/EasyMotion.sublime-settings new file mode 100644 index 0000000..164c331 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/EasyMotion.sublime-settings @@ -0,0 +1,11 @@ +{ + // defines syntax highlighting scope that will be used to highlight matched jump targets + // other examples include: keyword, string, number + "jump_target_scope" : "entity.name.class", + + // define the characters that we can jump to, in the order that they'll appear, they should be unique + "placeholder_chars" : "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ", + + // searches are case sensitive by default + "case_sensitive" : true +} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/LICENSE b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/LICENSE new file mode 100644 index 0000000..5aeb903 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/LICENSE @@ -0,0 +1,203 @@ +/* +* Apache License +* Version 2.0, January 2004 +* http://www.apache.org/licenses/ +* +* TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +* +* 1. Definitions. +* +* "License" shall mean the terms and conditions for use, reproduction, +* and distribution as defined by Sections 1 through 9 of this document. +* +* "Licensor" shall mean the copyright owner or entity authorized by +* the copyright owner that is granting the License. +* +* "Legal Entity" shall mean the union of the acting entity and all +* other entities that control, are controlled by, or are under common +* control with that entity. For the purposes of this definition, +* "control" means (i) the power, direct or indirect, to cause the +* direction or management of such entity, whether by contract or +* otherwise, or (ii) ownership of fifty percent (50%) or more of the +* outstanding shares, or (iii) beneficial ownership of such entity. +* +* "You" (or "Your") shall mean an individual or Legal Entity +* exercising permissions granted by this License. +* +* "Source" form shall mean the preferred form for making modifications, +* including but not limited to software source code, documentation +* source, and configuration files. +* +* "Object" form shall mean any form resulting from mechanical +* transformation or translation of a Source form, including but +* not limited to compiled object code, generated documentation, +* and conversions to other media types. +* +* "Work" shall mean the work of authorship, whether in Source or +* Object form, made available under the License, as indicated by a +* copyright notice that is included in or attached to the work +* (an example is provided in the Appendix below). +* +* "Derivative Works" shall mean any work, whether in Source or Object +* form, that is based on (or derived from) the Work and for which the +* editorial revisions, annotations, elaborations, or other modifications +* represent, as a whole, an original work of authorship. For the purposes +* of this License, Derivative Works shall not include works that remain +* separable from, or merely link (or bind by name) to the interfaces of, +* the Work and Derivative Works thereof. +* +* "Contribution" shall mean any work of authorship, including +* the original version of the Work and any modifications or additions +* to that Work or Derivative Works thereof, that is intentionally +* submitted to Licensor for inclusion in the Work by the copyright owner +* or by an individual or Legal Entity authorized to submit on behalf of +* the copyright owner. For the purposes of this definition, "submitted" +* means any form of electronic, verbal, or written communication sent +* to the Licensor or its representatives, including but not limited to +* communication on electronic mailing lists, source code control systems, +* and issue tracking systems that are managed by, or on behalf of, the +* Licensor for the purpose of discussing and improving the Work, but +* excluding communication that is conspicuously marked or otherwise +* designated in writing by the copyright owner as "Not a Contribution." +* +* "Contributor" shall mean Licensor and any individual or Legal Entity +* on behalf of whom a Contribution has been received by Licensor and +* subsequently incorporated within the Work. +* +* 2. Grant of Copyright License. Subject to the terms and conditions of +* this License, each Contributor hereby grants to You a perpetual, +* worldwide, non-exclusive, no-charge, royalty-free, irrevocable +* copyright license to reproduce, prepare Derivative Works of, +* publicly display, publicly perform, sublicense, and distribute the +* Work and such Derivative Works in Source or Object form. +* +* 3. Grant of Patent License. Subject to the terms and conditions of +* this License, each Contributor hereby grants to You a perpetual, +* worldwide, non-exclusive, no-charge, royalty-free, irrevocable +* (except as stated in this section) patent license to make, have made, +* use, offer to sell, sell, import, and otherwise transfer the Work, +* where such license applies only to those patent claims licensable +* by such Contributor that are necessarily infringed by their +* Contribution(s) alone or by combination of their Contribution(s) +* with the Work to which such Contribution(s) was submitted. If You +* institute patent litigation against any entity (including a +* cross-claim or counterclaim in a lawsuit) alleging that the Work +* or a Contribution incorporated within the Work constitutes direct +* or contributory patent infringement, then any patent licenses +* granted to You under this License for that Work shall terminate +* as of the date such litigation is filed. +* +* 4. Redistribution. You may reproduce and distribute copies of the +* Work or Derivative Works thereof in any medium, with or without +* modifications, and in Source or Object form, provided that You +* meet the following conditions: +* +* (a) You must give any other recipients of the Work or +* Derivative Works a copy of this License; and +* +* (b) You must cause any modified files to carry prominent notices +* stating that You changed the files; and +* +* (c) You must retain, in the Source form of any Derivative Works +* that You distribute, all copyright, patent, trademark, and +* attribution notices from the Source form of the Work, +* excluding those notices that do not pertain to any part of +* the Derivative Works; and +* +* (d) If the Work includes a "NOTICE" text file as part of its +* distribution, then any Derivative Works that You distribute must +* include a readable copy of the attribution notices contained +* within such NOTICE file, excluding those notices that do not +* pertain to any part of the Derivative Works, in at least one +* of the following places: within a NOTICE text file distributed +* as part of the Derivative Works; within the Source form or +* documentation, if provided along with the Derivative Works; or, +* within a display generated by the Derivative Works, if and +* wherever such third-party notices normally appear. The contents +* of the NOTICE file are for informational purposes only and +* do not modify the License. You may add Your own attribution +* notices within Derivative Works that You distribute, alongside +* or as an addendum to the NOTICE text from the Work, provided +* that such additional attribution notices cannot be construed +* as modifying the License. +* +* You may add Your own copyright statement to Your modifications and +* may provide additional or different license terms and conditions +* for use, reproduction, or distribution of Your modifications, or +* for any such Derivative Works as a whole, provided Your use, +* reproduction, and distribution of the Work otherwise complies with +* the conditions stated in this License. +* +* 5. Submission of Contributions. Unless You explicitly state otherwise, +* any Contribution intentionally submitted for inclusion in the Work +* by You to the Licensor shall be under the terms and conditions of +* this License, without any additional terms or conditions. +* Notwithstanding the above, nothing herein shall supersede or modify +* the terms of any separate license agreement you may have executed +* with Licensor regarding such Contributions. +* +* 6. Trademarks. This License does not grant permission to use the trade +* names, trademarks, service marks, or product names of the Licensor, +* except as required for reasonable and customary use in describing the +* origin of the Work and reproducing the content of the NOTICE file. +* +* 7. Disclaimer of Warranty. Unless required by applicable law or +* agreed to in writing, Licensor provides the Work (and each +* Contributor provides its Contributions) on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +* implied, including, without limitation, any warranties or conditions +* of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +* PARTICULAR PURPOSE. You are solely responsible for determining the +* appropriateness of using or redistributing the Work and assume any +* risks associated with Your exercise of permissions under this License. +* +* 8. Limitation of Liability. In no event and under no legal theory, +* whether in tort (including negligence), contract, or otherwise, +* unless required by applicable law (such as deliberate and grossly +* negligent acts) or agreed to in writing, shall any Contributor be +* liable to You for damages, including any direct, indirect, special, +* incidental, or consequential damages of any character arising as a +* result of this License or out of the use or inability to use the +* Work (including but not limited to damages for loss of goodwill, +* work stoppage, computer failure or malfunction, or any and all +* other commercial damages or losses), even if such Contributor +* has been advised of the possibility of such damages. +* +* 9. Accepting Warranty or Additional Liability. While redistributing +* the Work or Derivative Works thereof, You may choose to offer, +* and charge a fee for, acceptance of support, warranty, indemnity, +* or other liability obligations and/or rights consistent with this +* License. However, in accepting such obligations, You may act only +* on Your own behalf and on Your sole responsibility, not on behalf +* of any other Contributor, and only if You agree to indemnify, +* defend, and hold each Contributor harmless for any liability +* incurred by, or claims asserted against, such Contributor by reason +* of your accepting any such warranty or additional liability. +* +* END OF TERMS AND CONDITIONS +* +* APPENDIX: How to apply the Apache License to your work. +* +* To apply the Apache License to your work, attach the following +* boilerplate notice, with the fields enclosed by brackets "[]" +* replaced with your own identifying information. (Don't include +* the brackets!) The text should be enclosed in the appropriate +* comment syntax for the file format. We also recommend that a +* file or class name and description of purpose be included on the +* same "printed page" as the copyright notice for easier +* identification within third-party archives. +* +* Copyright [yyyy] [name of copyright owner] +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Main.sublime-menu b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Main.sublime-menu new file mode 100644 index 0000000..45795cf --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/Main.sublime-menu @@ -0,0 +1,90 @@ +[ + { + "id": "preferences", + "children": + [ + { + "id": "package-settings", + "children": + [ + { + "caption": "EasyMotion", + "children": + [ + // README + { + "caption": "View README", + "command": "open_file", "args": {"file": "${packages}/EasyMotion/README.md"} + }, + { "caption": "-" }, + + // Settings + { + "caption": "Settings – Default", + "command": "open_file", "args": {"file": "${packages}/EasyMotion/EasyMotion.sublime-settings"} + }, + { + "caption": "Settings – User", + "command": "open_file", "args": {"file": "${packages}/User/EasyMotion.sublime-settings"} + }, + { "caption": "-" }, + + // Keybindings - Default + { + "caption": "Key Bindings – Default", + "command": "open_file", + "args": { + "file": "${packages}/EasyMotion/Default (OSX).sublime-keymap", + "platform": "OSX" + } + }, + { + "caption": "Key Bindings – Default", + "command": "open_file", + "args": { + "file": "${packages}/EasyMotion/Default (Linux).sublime-keymap", + "platform": "Linux" + } + }, + { + "caption": "Key Bindings – Default", + "command": "open_file", + "args": { + "file": "${packages}/EasyMotion/Default (Windows).sublime-keymap", + "platform": "Windows" + } + }, + + // Keybindings - User + { + "caption": "Key Bindings – User", + "command": "open_file", + "args": { + "file": "${packages}/User/Default (OSX).sublime-keymap", + "platform": "OSX" + } + }, + { + "caption": "Key Bindings – User", + "command": "open_file", + "args": { + "file": "${packages}/User/Default (Linux).sublime-keymap", + "platform": "Linux" + } + }, + { + "caption": "Key Bindings – User", + "command": "open_file", + "args": { + "file": "${packages}/User/Default (Windows).sublime-keymap", + "platform": "Windows" + } + }, + { "caption": "-" } + ] + } + ] + } + ] + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/README.md b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/README.md new file mode 100644 index 0000000..345e0e7 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/README.md @@ -0,0 +1,134 @@ +# Sublime EasyMotion + +EasyMotion is a [Sublime Text 2](http://www.sublimetext.com/2) plugin that allows you to move the cursor to any character in your current view. + +It's heavily inspired by [Vim's EasyMotion](http://www.vim.org/scripts/script.php?script_id=3526), and [Emacs' AceJump](http://www.emacswiki.org/emacs/AceJump) plugins. + +After pressing the EasyMotion shortcut (default `cmd-;`/`ctrl-;`), you then press the character that you'd like to jump to. EasyMotion will then replace all currently visible instances of that character with one of `a-zA-Z0-9`. Press the key for the one you want and your cursor will be moved right to it. + +![Animated Gif](https://raw.github.com/tednaleid/sublime-EasyMotion/add_images/images/easymotion.gif) + + +## Installation + +### Install via PackageControl +If you have the [PackageControl](http://wbond.net/sublime_packages/package_control) plugin installed, you can use that to install `EasyMotion`. + +Just type `cmd-shift-p` (`ctrl-shift-p` on win/linux) to bring up the command pallate then type `install` and pick `Package Control: Install Package` from the dropdown. + +Then type `EasyMotion` and choose the EasyMotion plugin from the dropdown. Hit `enter` and it will install. + +### Manual Installation + +Manual installation should be as easy as cloning this git repository into your Sublime `Packages` directory. On OSX: + + cd ~/Application\ Support/Sublime\ Text\ 2/Packages + git clone git://github.com/tednaleid/sublime-EasyMotion.git EasyMotion + +(The directory name underneath packages __must__ be `EasyMotion` and not `sublime-EasyMotion` for some preferences to get picked up) + +If you're interested in trying the next release of the plugin, you can switch your branch to the development branch: + + cd EasyMotion + git checkout development + +This branch will have features that are marked as fixed in the issue, but haven't yet been merged to `master`. + +## Usage + +### Jump to any visible character + + cmd-; // OSX + ctrl-; // Linux/Windows + +it will label all instances of that character with a unique value in `a-zA-Z0-9`, type the label you want and it will jump you to it. + +#### Example + +The cursor is at the end of the file and we want to jump to the beginning of the `realpart` variable on line 3 + +![EasyMotion Begin](https://raw.github.com/tednaleid/sublime-EasyMotion/add_images/images/sublimejump_begin.png) + +Instead of hitting the up arrow twice and scrolling over to the r (or grabbing your mouse), you could press `cmd-;` followed by `r`. That will transform your file into this (notice that each instance of `r` has been turned into one of `a-zA-Z0-9`): + +![EasyMotion Middle](https://raw.github.com/tednaleid/sublime-EasyMotion/add_images/images/sublimejump_middle.png) + +Press `e` and your cursor will jump right there: + +![EasyMotion Middle](https://raw.github.com/tednaleid/sublime-EasyMotion/add_images/images/sublimejump_end.png) + +If your target character occurs more than 62 times in the visible area, it will decorate them in batches. + +So if we search this for the letter `l` using `cmd-;`+`l` + +![Many Matches Start](https://raw.github.com/tednaleid/sublime-EasyMotion/add_images/images/many_matches_start.png) + +The first batch of 62 targets will look like this: + +![Many Matches First](https://raw.github.com/tednaleid/sublime-EasyMotion/add_images/images/many_matches_first.png) + +**Just hit `enter` and it will highlight the next group of matches.** + +![Many Matches Second](https://raw.github.com/tednaleid/sublime-EasyMotion/add_images/images/many_matches_second.png) + +Keep hitting `enter` and it will continue to cycle through them in groups of 62. You can also hit `shift-enter` to cycle backwards through the target groups. Hitting the `spacebar` will exit, and so will `ctrl-c` and `escape` (but for some reason there's currently a bug that makes you hit those twice to exit). + +### Select all text between cursor and any visible character + + cmd-shift-; // OSX + ctrl-shift-; // Linux/Windows + +it will label all instances of that character with a unique value in `a-zA-Z0-9`, type it and it will select all text between your current cursor position and the chosen jump target. + +#### Example + +So in the same situation as above, if we had hit `cmd-shift-;` followed by `r` and picked the `e` target that occurs at the start of the `imagpart` variable on line 3, we would end up with this: + +![EasyMotion Select](https://raw.github.com/tednaleid/sublime-EasyMotion/add_images/images/sublimejump_select.png) + + +## User Modifiable Preferences + +### Remapping the Sublime EasyMotion keyboard shortcut + +You can remap your keys to be something other than the defaults by entering an override value into your "User - KeyBindings" (under Sublime Text 2 -> Preferences -> Package Settings -> Easy Motion on OSX), just make sure to copy the existing key bindings exactly and change only the first item in the `keys` stanza, otherwise it won't work. So if you wanted the jump command to be `ctrl-,`, you'd use: + + + [ + { + "keys": ["ctrl+,", ""], + "command": "easy_motion", + "args": {"select_text": false} + }, + { + "keys": ["ctrl+shift+,", ""], + "command": "easy_motion", + "args": {"select_text": true} + } + ] + + +### Overriding the placeholder characters used for jumping + +Add this to your "User Settings" file (found at "Sublime Text 2 -> Preferences -> Package Settings -> Easy Motion -> Settings - User" on OSX) and change the string to contain whatever characters you'd like to use: + + // define the characters that we can jump to, in the order that they'll appear, they should be unique + "placeholder_chars" : "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ" + +example using only QWERTY home-row replacements: + + "placeholder_chars" : "jkl;asdfHGJKL:ASDFHG" + +### Override the highlight color for jump targets + +If the highlight color used for jump targets isn't bold enough if your color scheme, you can override it by changing this "User Setting": + + // defines syntax highlighting scope that will be used to highlight matched jump targets + // other examples include: keyword, string, number + "jump_target_scope" : "entity.name.class" + + +# Versions + +- 0.8 - released 2/3/13 - updates location of preferences to EasyMotion specific file and includes plugin specific preferences file. You'll need to migrate preferences over into this file for them to stick. +- 0.9 - released 2/14/13 - removes need for input panel and implements an easy_motion_mode to accept keystrokes, also lets `shift-enter` cycle backwards diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/easy_motion.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/easy_motion.py new file mode 100644 index 0000000..0c7aa8b --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/easy_motion.py @@ -0,0 +1,293 @@ +import sublime +import sublime_plugin +import re +from itertools import izip_longest +from pprint import pprint + +REGEX_ESCAPE_CHARS = '\\+*()[]{}^$?|:].,' + +# not a fan of using globals like this, but not sure if there's a better way with the plugin +# API that ST2 provides. Tried attaching as fields to active_view, but didn't persiste, I'm guessing +# it's just a representation of something that gets regenerated on demand so dynamic fields are transient +JUMP_GROUP_GENERATOR = None +CURRENT_JUMP_GROUP = None +EASY_MOTION_EDIT = None +SELECT_TEXT = False +COMMAND_MODE_WAS = False +JUMP_TARGET_SCOPE = 'string' + + +class JumpGroupGenerator: + ''' + given a list of region jump targets matching the given character, can emit a series of + JumpGroup dictionaries going forwards with next and backwards with previous + ''' + def __init__(self, view, character, placeholder_chars, case_sensitive): + self.view = view + self.case_sensitive = case_sensitive + self.placeholder_chars = placeholder_chars + self.all_jump_targets = self.find_all_jump_targets_in_visible_region(character) + self.interleaved_jump_targets = self.interleave_jump_targets_from_cursor() + self.jump_target_index = 0 + self.jump_target_groups = self.create_jump_target_groups() + self.jump_target_group_index = -1 + + def determine_re_flags(self, character): + if character == 'enter': + return '(?m)' + elif self.case_sensitive: + return '(?i)' + else: + return '' + + def interleave_jump_targets_from_cursor(self): + sel = self.view.sel()[0] # multi select not supported, doesn't really make sense + sel_begin = sel.begin() + sel_end = sel.end() + before = [] + after = [] + + # split them into two lists radiating out from the cursor position + for target in self.all_jump_targets: + if target.begin() < sel_begin: + # add to beginning of list so closest targets to cursor are first + before.insert(0, target) + elif target.begin() > sel_end: + after.append(target) + + # now interleave the two lists together into one list + return [target for targets in izip_longest(before, after) for target in targets if target is not None] + + def create_jump_target_groups(self): + jump_target_groups = [] + + while self.has_next_jump_target(): + jump_group = dict() + + for placeholder_char in self.placeholder_chars: + if self.has_next_jump_target(): + jump_group[placeholder_char] = self.interleaved_jump_targets[self.jump_target_index] + self.jump_target_index += 1 + else: + break + + jump_target_groups.append(jump_group) + + return jump_target_groups + + def has_next_jump_target(self): + return self.jump_target_index < len(self.interleaved_jump_targets) + + def __len__(self): + return len(self.jump_target_groups) + + def next(self): + self.jump_target_group_index += 1 + + if self.jump_target_group_index >= len(self.jump_target_groups) or self.jump_target_group_index < 0: + self.jump_target_group_index = 0 + + return self.jump_target_groups[self.jump_target_group_index] + + def previous(self): + self.jump_target_group_index -= 1 + + if self.jump_target_group_index < 0 or self.jump_target_group_index >= len(self.jump_target_groups): + self.jump_target_group_index = len(self.jump_target_groups) - 1 + + return self.jump_target_groups[self.jump_target_group_index] + + def find_all_jump_targets_in_visible_region(self, character): + visible_region_begin = self.visible_region_begin() + visible_text = self.visible_text() + folded_regions = self.get_folded_regions(self.view) + matching_regions = [] + target_regexp = self.target_regexp(character) + + for char_at in (match.start() for match in re.finditer(target_regexp, visible_text)): + char_point = char_at + visible_region_begin + char_region = sublime.Region(char_point, char_point + 1) + if not self.region_list_contains_region(folded_regions, char_region): + matching_regions.append(char_region) + + return matching_regions + + def region_list_contains_region(self, region_list, region): + + for element_region in region_list: + if element_region.contains(region): + return True + return False + + def visible_region_begin(self): + return self.view.visible_region().begin() + + def visible_text(self): + visible_region = self.view.visible_region() + return self.view.substr(visible_region) + + def target_regexp(self, character): + re_flags = self.determine_re_flags(character) + if (REGEX_ESCAPE_CHARS.find(character) >= 0): + return re_flags + '\\' + character + elif character == "enter": + return re_flags + "(?=^).|.(?=$)" + else: + return re_flags + character + + def get_folded_regions(self, view): + ''' + No way in the API to get the folded regions without unfolding them first + seems to be quick enough that you can't actually see them fold/unfold + ''' + folded_regions = view.unfold(view.visible_region()) + view.fold(folded_regions) + return folded_regions + + +class EasyMotionCommand(sublime_plugin.WindowCommand): + winning_selection = None + + def run(self, character=None, select_text=False): + global JUMP_GROUP_GENERATOR, SELECT_TEXT, JUMP_TARGET_SCOPE + sublime.status_message("EasyMotion: Jump to " + character) + + SELECT_TEXT = select_text + + active_view = self.window.active_view() + + settings = sublime.load_settings("EasyMotion.sublime-settings") + placeholder_chars = settings.get('placeholder_chars', 'abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ') + JUMP_TARGET_SCOPE = settings.get('jump_target_scope', 'string') + case_sensitive = settings.get('case_sensitive', True) + + JUMP_GROUP_GENERATOR = JumpGroupGenerator(active_view, character, placeholder_chars, case_sensitive) + + if len(JUMP_GROUP_GENERATOR) > 0: + self.activate_mode(active_view) + self.window.run_command("show_jump_group") + else: + sublime.status_message("EasyMotion: unable to find any instances of " + character + " in visible region") + + def activate_mode(self, active_view): + global COMMAND_MODE_WAS + active_view.settings().set('easy_motion_mode', True) + # yes, this feels a little dirty to mess with the Vintage plugin, but there + # doesn't appear to be any other way to tell it to not intercept keys, so turn it + # off (if it's on) while we're running EasyMotion + COMMAND_MODE_WAS = active_view.settings().get('command_mode') + if (COMMAND_MODE_WAS): + active_view.settings().set('command_mode', False) + + +class ShowJumpGroup(sublime_plugin.WindowCommand): + active_view = None + + def run(self, next=True): + self.active_view = self.window.active_view() + + self.show_jump_group(next) + + def show_jump_group(self, next=True): + global JUMP_GROUP_GENERATOR, CURRENT_JUMP_GROUP + + if next: + CURRENT_JUMP_GROUP = JUMP_GROUP_GENERATOR.next() + else: + CURRENT_JUMP_GROUP = JUMP_GROUP_GENERATOR.previous() + + self.activate_current_jump_group() + + def activate_current_jump_group(self): + global CURRENT_JUMP_GROUP, EASY_MOTION_EDIT, JUMP_TARGET_SCOPE + ''' + Start up an edit object if we don't have one already, then create all of the jump targets + ''' + if (EASY_MOTION_EDIT is not None): + # normally would call deactivate_current_jump_group here, but apparent ST2 bug prevents it from calling undo correctly + # instead just decorate the new character and keep the same edit object so all changes get undone properly + self.active_view.erase_regions("jump_match_regions") + else: + EASY_MOTION_EDIT = self.active_view.begin_edit() + + for placeholder_char in CURRENT_JUMP_GROUP.keys(): + self.active_view.replace(EASY_MOTION_EDIT, CURRENT_JUMP_GROUP[placeholder_char], placeholder_char) + + self.active_view.add_regions("jump_match_regions", CURRENT_JUMP_GROUP.values(), JUMP_TARGET_SCOPE, "dot") + + +class JumpTo(sublime_plugin.WindowCommand): + def run(self, character=None): + global COMMAND_MODE_WAS + + self.active_view = self.window.active_view() + self.winning_selection = self.winning_selection_from(character) + self.finish_easy_motion() + self.active_view.settings().set('easy_motion_mode', False) + if (COMMAND_MODE_WAS): + self.active_view.settings().set('command_mode', True) + + def winning_selection_from(self, selection): + global CURRENT_JUMP_GROUP, SELECT_TEXT + winning_region = None + if selection in CURRENT_JUMP_GROUP: + winning_region = CURRENT_JUMP_GROUP[selection] + + if winning_region is not None: + if SELECT_TEXT: + for current_selection in self.active_view.sel(): + if winning_region.begin() < current_selection.begin(): + return sublime.Region(current_selection.end(), winning_region.begin()) + else: + return sublime.Region(current_selection.begin(), winning_region.end()) + else: + return sublime.Region(winning_region.begin(), winning_region.begin()) + + def finish_easy_motion(self): + ''' + We need to clean up after ourselves by restoring the view to it's original state, if the user did + press a jump target that we've got saved, jump to it as the last action + ''' + self.deactivate_current_jump_group() + self.jump_to_winning_selection() + + def deactivate_current_jump_group(self): + ''' + Close out the edit that we've been messing with and then undo it right away to return the buffer to + the pristine state that we found it in. Other methods ended up leaving the window in a dirty save state + and this seems to be the cleanest way to get back to the original state + ''' + global EASY_MOTION_EDIT + if (EASY_MOTION_EDIT is not None): + self.active_view.end_edit(EASY_MOTION_EDIT) + self.window.run_command("undo") + EASY_MOTION_EDIT = None + + self.active_view.erase_regions("jump_match_regions") + + def jump_to_winning_selection(self): + if self.winning_selection is not None: + self.active_view.run_command("jump_to_winning_selection", {"begin": self.winning_selection.begin(), "end": self.winning_selection.end()}) + + +class DeactivateJumpTargets(sublime_plugin.WindowCommand): + def run(self): + pprint("DeactivateJumpTargets called") + global EASY_MOTION_EDIT + + active_view = self.window.active_view() + if (EASY_MOTION_EDIT is not None): + active_view.end_edit(EASY_MOTION_EDIT) + self.window.run_command("undo") + EASY_MOTION_EDIT = None + + active_view.erase_regions("jump_match_regions") + + +class JumpToWinningSelection(sublime_plugin.TextCommand): + def run(self, edit, begin, end): + winning_region = sublime.Region(long(begin), long(end)) + sel = self.view.sel() + sel.clear() + sel.add(winning_region) + self.view.show(winning_region) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/package-metadata.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/package-metadata.json new file mode 100644 index 0000000..5e98f3f --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/EasyMotion/package-metadata.json @@ -0,0 +1 @@ +{"url": "https://github.com/tednaleid/sublime-EasyMotion", "version": "2013.03.26.20.46.50", "description": "Sublime Text 2 plugin to quickly jump to any character in the visible area of the active view."} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/.gitignore b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/.gitignore new file mode 100644 index 0000000..2500b0f --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/.gitignore @@ -0,0 +1,2 @@ +*.pyc +*.cache \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ColorSchemes/Print-Color.tmTheme b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ColorSchemes/Print-Color.tmTheme new file mode 100644 index 0000000..aadedbc --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ColorSchemes/Print-Color.tmTheme @@ -0,0 +1,362 @@ + + + + + name + Print Color + settings + + + settings + + background + #FFFFFF + caret + #000000 + foreground + #000000 + invisibles + #3B3A32 + lineHighlight + #2E2E2E22 + selection + #34A7BD + selectionForeground + #FFFFFF + inactiveSelection + #9D550FAA + inactiveSelectionForeground + #6ac4d6 + findHighlight + #FFE792 + findHighlightForeground + #000000 + activeGuide + #34A7BD + gutterForeground + #858585 + gutter + #E5E5E5 + + bracketsForeground + #F8F8F2A5 + bracketsOptions + underline + + bracketContentsForeground + #F8F8F2A5 + bracketContentsOptions + underline + + tagsOptions + stippled_underline + + + + name + Comment + scope + comment + settings + + foreground + #A5A5A5 + + + + name + String + scope + string + settings + + foreground + #8F8634 + + + + name + Number + scope + constant.numeric + settings + + foreground + #7C4FCD + + + + name + Built-in constant + scope + constant.language + settings + + foreground + #7C4FCD + + + + name + User-defined constant + scope + constant.character, constant.other + settings + + foreground + #7C4FCD + + + + name + Variable + scope + variable + settings + + fontStyle + + + + + name + Keyword + scope + keyword + settings + + foreground + #C70040 + + + + name + Storage + scope + storage + settings + + fontStyle + + foreground + #C70040 + + + + name + Storage type + scope + storage.type + settings + + fontStyle + italic + foreground + #34A7BD + + + + name + Class name + scope + entity.name.class + settings + + fontStyle + underline + foreground + #427E00 + + + + name + Inherited class + scope + entity.other.inherited-class + settings + + fontStyle + italic underline + foreground + #427E00 + + + + name + Function name + scope + entity.name.function + settings + + fontStyle + + foreground + #427E00 + + + + name + Function argument + scope + variable.parameter + settings + + fontStyle + italic + foreground + #CB6500 + + + + name + Tag name + scope + entity.name.tag + settings + + fontStyle + + foreground + #C70040 + + + + name + Tag attribute + scope + entity.other.attribute-name + settings + + fontStyle + + foreground + #427E00 + + + + name + Library function + scope + support.function + settings + + fontStyle + + foreground + #34A7BD + + + + name + Library constant + scope + support.constant + settings + + fontStyle + + foreground + #34A7BD + + + + name + Library class/type + scope + support.type, support.class + settings + + fontStyle + italic + foreground + #34A7BD + + + + name + Library variable + scope + support.other.variable + settings + + fontStyle + + + + + name + Invalid + scope + invalid + settings + + background + #C70040 + fontStyle + + foreground + #F8F8F0 + + + + name + Invalid deprecated + scope + invalid.deprecated + settings + + background + #7C4FCD + foreground + #F8F8F0 + + + + name + JSON String + scope + meta.structure.dictionary.json string.quoted.double.json + settings + + foreground + #8F8634 + + + + name + diff.deleted + scope + markup.deleted + settings + + foreground + #C70040 + + + + name + diff.inserted + scope + markup.inserted + settings + + foreground + #427E00 + + + + name + diff.changed + scope + markup.changed + settings + + foreground + #8F8634 + + + + uuid + 22808317-0a5a-4b87-baea-5aeee17bf295 + + diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ColorSchemes/Print-Grayscale.tmTheme b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ColorSchemes/Print-Grayscale.tmTheme new file mode 100644 index 0000000..c1b2a58 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ColorSchemes/Print-Grayscale.tmTheme @@ -0,0 +1,133 @@ + + + + + name + Print Grayscale + settings + + + settings + + background + #FFFFFF + caret + #000000 + foreground + #000000 + invisibles + #323232 + lineHighlight + #2E2E2E22 + selection + #666666 + selectionForeground + #FFFFFF + inactiveSelection + #888888 + findHighlight + #666666 + findHighlightForeground + #000000 + activeGuide + #888888 + gutterForeground + #000000 + gutter + #E5E5E5 + + bracketsForeground + #000000 + bracketsOptions + underline + + bracketContentsForeground + #000000 + bracketContentsOptions + underline --> + + tagsOptions + stippled_underline + + + + name + Comment + scope + comment + settings + + foreground + #A5A5A5 + + + + name + Storage type + scope + storage.type + settings + + fontStyle + italic + foreground + #000000 + + + + name + Class name + scope + entity.name.class + settings + + fontStyle + underline + foreground + #000000 + + + + name + Inherited class + scope + entity.other.inherited-class + settings + + fontStyle + italic underline + foreground + #000000 + + + + name + Function argument + scope + variable.parameter + settings + + fontStyle + italic + foreground + #000000 + + + + name + Library class/type + scope + support.type, support.class + settings + + fontStyle + italic + foreground + #000000 + + + + uuid + 07379361-ce49-4e6c-a03f-26378a7a2131 + + diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/Context.sublime-menu b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/Context.sublime-menu new file mode 100644 index 0000000..d0320be --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/Context.sublime-menu @@ -0,0 +1,26 @@ +[ + { "caption": "-" }, + { + "caption": "Export", + "children": + [ + { "command": "export_html_panel", "caption": "HTML" }, + {"command": "export_bbcode_panel", "caption": "BBCode"} + ] + }, + { + "caption": "Annotations", + "children": + [ + { "command": "enable_annotation_mode", "caption": "Enable Annotation Mode" }, + { "command": "disable_annotation_mode", "caption": "Disable Annotation Mode" }, + { "caption": "-" }, + { "command": "add_annotation", "caption": "Add Annotation" }, + { "command": "edit_annotation", "caption": "Edit Annotation" }, + { "command": "delete_annotations", "caption": "Delete Annotation(s)" }, + { "command": "clear_annotations", "caption": "Delete All Annotations" }, + { "command": "show_annotation_comment", "caption": "Show Annotation Comment" } + ] + }, + { "caption": "-"} +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/Default.sublime-commands b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/Default.sublime-commands new file mode 100644 index 0000000..4e3776a --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/Default.sublime-commands @@ -0,0 +1,35 @@ +[ + // Export to HTML + { + "caption": "Export to HTML: Show Export Menu", + "command": "export_html_panel" + }, + { + "caption": "Export to BBCode: Show Export Menu", + "command": "export_bbcode_panel" + }, + { + "caption": "Export to HTML: Toggle Annotation Mode", + "command": "toggle_annotation_html_mode" + }, + { + "caption": "Export to HTML: Add Annotation", + "command": "add_annotation" + }, + { + "caption": "Export to HTML: Edit Annotation", + "command": "edit_annotation" + }, + { + "caption": "Export to HTML: Delete Annotation(s)", + "command": "delete_annotations" + }, + { + "caption": "Export to HTML: Delete All Annotations", + "command": "clear_annotations" + }, + { + "caption": "Export to HTML: Show Annotation Comment", + "command": "show_annotation_comment" + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportBbcode.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportBbcode.py new file mode 100644 index 0000000..2be2d0b --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportBbcode.py @@ -0,0 +1,352 @@ +import sublime +import sublime_plugin +from os import path +import tempfile +import sys +import re + +PACKAGE_SETTINGS = "ExportHtml.sublime-settings" + +if sublime.platform() == "linux": + # Try and load Linux Python2.6 lib. Default path is for Ubuntu. + linux_lib = sublime.load_settings(PACKAGE_SETTINGS).get("linux_python2.6_lib", "/usr/lib/python2.6/lib-dynload") + if not linux_lib in sys.path and path.exists(linux_lib): + sys.path.append(linux_lib) +from plistlib import readPlist +from ExportHtmlLib.rgba.rgba import RGBA + +NUMBERED_BBCODE_LINE = '[color=%(color)s]%(line)s [/color]%(code)s\n' + +BBCODE_LINE = '%(code)s\n' + +BBCODE_CODE = '[color=%(color)s]%(content)s[/color]' + +BBCODE_ESCAPE = '[/color][color=%(color_open)s]%(content)s[/color][color=%(color_close)s]' + +BBCODE_BOLD = '[b]%(content)s[/b]' + +BBCODE_ITALIC = '[i]%(content)s[/i]' + +POST_START = '[pre=%(bg_color)s]' + +POST_END = '[/pre]\n' + +BBCODE_MATCH = re.compile(r"""(\[/?)((?:code|pre|table|tr|td|th|b|i|u|sup|color|url|img|list|trac|center|quote|size|li|ul|ol|youtube|gvideo)(?:=[^\]]+)?)(\])""") + +FILTER_MATCH = re.compile(r'^(?:(brightness|saturation|hue|colorize)\((-?[\d]+|[\d]*\.[\d]+)\)|(sepia|grayscale|invert))$') + + +class ExportBbcodePanelCommand(sublime_plugin.WindowCommand): + def execute(self, value): + if value >= 0: + view = self.window.active_view() + if view != None: + ExportBbcode(view).run(**self.args[value]) + + def run(self): + options = sublime.load_settings(PACKAGE_SETTINGS).get("bbcode_panel", {}) + menu = [] + self.args = [] + for opt in options: + k, v = opt.items()[0] + menu.append(k) + self.args.append(v) + + if len(menu): + self.window.show_quick_panel( + menu, + self.execute + ) + + +class ExportBbcodeCommand(sublime_plugin.WindowCommand): + def run(self, **kwargs): + view = self.window.active_view() + if view != None: + ExportBbcode(view).run(**kwargs) + + +class ExportBbcode(object): + def __init__(self, view): + self.view = view + + def process_inputs(self, **kwargs): + return { + "numbers": bool(kwargs.get("numbers", False)), + "color_scheme": kwargs.get("color_scheme", None), + "multi_select": bool(kwargs.get("multi_select", False)), + "clipboard_copy": bool(kwargs.get("clipboard_copy", True)), + "view_open": bool(kwargs.get("view_open", False)), + "filter": kwargs.get("filter", "") + } + + def setup(self, **kwargs): + path_packages = sublime.packages_path() + + # Get get general document preferences from sublime preferences + settings = sublime.load_settings('Preferences.sublime-settings') + eh_settings = sublime.load_settings(PACKAGE_SETTINGS) + self.tab_size = settings.get('tab_size', 4) + self.char_limit = int(eh_settings.get("valid_selection_size", 4)) + self.bground = '' + self.fground = '' + self.gbground = '' + self.gfground = '' + self.sbground = '' + self.sfground = '' + self.numbers = kwargs["numbers"] + self.hl_continue = None + self.curr_hl = None + self.sels = [] + self.multi_select = self.check_sel() if kwargs["multi_select"] else False + self.size = self.view.size() + self.pt = 0 + self.end = 0 + self.curr_row = 0 + self.empty_space = None + self.filter = [] + for f in kwargs["filter"].split(";"): + m = FILTER_MATCH.match(f) + if m: + if m.group(1): + self.filter.append((m.group(1), float(m.group(2)))) + else: + self.filter.append((m.group(3), 0.0)) + + # Get color scheme + if kwargs["color_scheme"] != None: + alt_scheme = kwargs["color_scheme"] + else: + alt_scheme = eh_settings.get("alternate_scheme", False) + scheme_file = settings.get('color_scheme') if alt_scheme == False else alt_scheme + colour_scheme = path.normpath(scheme_file) + self.plist_file = self.apply_filters(readPlist(path_packages + colour_scheme.replace('Packages', ''))) + colour_settings = self.plist_file["settings"][0]["settings"] + + # Get general theme colors from color scheme file + self.bground = self.strip_transparency(colour_settings.get("background", '#FFFFFF'), simple_strip=True) + self.fground = self.strip_transparency(colour_settings.get("foreground", '#000000')) + self.gbground = self.bground + self.gfground = self.fground + + # Create scope colors mapping from color scheme file + self.colours = {self.view.scope_name(self.end).split(' ')[0]: {"color": self.fground, "style": []}} + for item in self.plist_file["settings"]: + scope = item.get('scope', None) + colour = None + style = [] + if 'scope' in item: + scope = item['scope'] + if 'settings' in item: + colour = item['settings'].get('foreground', None) + if 'fontStyle' in item['settings']: + for s in item['settings']['fontStyle'].split(' '): + if s == "bold" or s == "italic": # or s == "underline": + style.append(s) + + if scope != None and colour != None: + self.colours[scope] = {"color": self.strip_transparency(colour), "style": style} + + def apply_filters(self, tmtheme): + def filter_color(color): + rgba = RGBA(color) + for f in self.filter: + name = f[0] + value = f[1] + if name == "grayscale": + rgba.grayscale() + elif name == "sepia": + rgba.sepia() + elif name == "saturation": + rgba.saturation(value) + elif name == "invert": + rgba.invert() + elif name == "brightness": + rgba.brightness(value) + elif name == "hue": + rgba.hue(value) + elif name == "colorize": + rgba.colorize(value) + return rgba.get_rgba() + + if len(self.filter): + general_settings_read = False + for settings in tmtheme["settings"]: + if not general_settings_read: + for k, v in settings["settings"].items(): + try: + settings["settings"][k] = filter_color(v) + except: + pass + general_settings_read = True + continue + + try: + settings["settings"]["foreground"] = filter_color(settings["settings"]["foreground"]) + except: + pass + try: + settings["settings"]["background"] = filter_color(settings["settings"]["background"]) + except: + pass + return tmtheme + + def strip_transparency(self, color, track_darkness=False, simple_strip=False): + if color is None: + return color + rgba = RGBA(color.replace(" ", "")) + if not simple_strip: + rgba.apply_alpha(self.bground if self.bground != "" else "#FFFFFF") + return rgba.get_rgb() + + def setup_print_block(self, curr_sel, multi=False): + # Determine start and end points and whether to parse whole file or selection + if not multi and (curr_sel.empty() or curr_sel.size() <= self.char_limit): + self.size = self.view.size() + self.pt = 0 + self.end = 1 + self.curr_row = 1 + else: + self.size = curr_sel.end() + self.pt = curr_sel.begin() + self.end = self.pt + 1 + self.curr_row = self.view.rowcol(self.pt)[0] + 1 + self.start_line = self.curr_row + + self.gutter_pad = len(str(self.view.rowcol(self.size)[0])) + 1 + + def check_sel(self): + multi = False + for sel in self.view.sel(): + if not sel.empty() and sel.size() >= self.char_limit: + multi = True + self.sels.append(sel) + return multi + + def guess_colour(self, the_key): + the_colour = None + the_style = None + if the_key in self.colours: + the_colour = self.colours[the_key]["color"] + the_style = self.colours[the_key]["style"] + else: + best_match = 0 + for key in self.colours: + if self.view.score_selector(self.pt, key) > best_match: + best_match = self.view.score_selector(self.pt, key) + the_colour = self.colours[key]["color"] + the_style = self.colours[key]["style"] + self.colours[the_key] = {"color": the_colour, "style": the_style} + return the_colour, the_style + + def print_line(self, line, num): + if self.numbers: + bbcode_line = NUMBERED_BBCODE_LINE % { + "color": self.gfground, + "line": str(num).rjust(self.gutter_pad), + "code": line + } + else: + bbcode_line = BBCODE_LINE % {"code": line} + + return bbcode_line + + def convert_view_to_bbcode(self, the_bbcode): + for line in self.view.split_by_newlines(sublime.Region(self.end, self.size)): + self.empty_space = None + self.size = line.end() + line = self.convert_line_to_bbcode() + the_bbcode.write(self.print_line(line, self.curr_row)) + self.curr_row += 1 + + def repl(self, m, the_colour): + return m.group(1) + ( + BBCODE_ESCAPE % { + "color_open": the_colour, + "color_close": the_colour, + "content": m.group(2) + } + ) + m.group(3) + + def format_text(self, line, text, the_colour, the_style): + text = text.replace('\t', ' ' * self.tab_size).replace('\n', '') + if self.empty_space != None: + text = self.empty_space + text + self.empty_space = None + if text.strip(' ') == '': + self.empty_space = text + else: + code = "" + text = BBCODE_MATCH.sub(lambda m: self.repl(m, the_colour), text) + bold = False + italic = False + for s in the_style: + if s == "bold": + bold = True + if s == "italic": + italic = True + code += (BBCODE_CODE % {"color": the_colour, "content": text}) + if italic: + code = (BBCODE_ITALIC % {"color": the_colour, "content": code}) + if bold: + code = (BBCODE_BOLD % {"color": the_colour, "content": code}) + line.append(code) + + def convert_line_to_bbcode(self): + line = [] + + while self.end <= self.size: + # Get text of like scope up to a highlight + scope_name = self.view.scope_name(self.pt) + while self.view.scope_name(self.end) == scope_name and self.end < self.size: + self.end += 1 + the_colour, the_style = self.guess_colour(scope_name) + + region = sublime.Region(self.pt, self.end) + # Normal text formatting + text = self.view.substr(region) + self.format_text(line, text, the_colour, the_style) + + # Continue walking through line + self.pt = self.end + self.end = self.pt + 1 + + # Join line segments + return ''.join(line) + + def write_body(self, the_bbcode): + the_bbcode.write(POST_START % {"bg_color": self.bground}) + + # Convert view to HTML + if self.multi_select: + count = 0 + total = len(self.sels) + for sel in self.sels: + self.setup_print_block(sel, multi=True) + self.convert_view_to_bbcode(the_bbcode) + count += 1 + + if count < total: + the_bbcode.write("\n" + (BBCODE_CODE % {"color": self.fground, "content": "..."}) + "\n\n") + + else: + self.setup_print_block(self.view.sel()[0]) + self.convert_view_to_bbcode(the_bbcode) + + the_bbcode.write(POST_END) + + def run(self, **kwargs): + inputs = self.process_inputs(**kwargs) + self.setup(**inputs) + + delete = False if inputs["view_open"] else True + + with tempfile.NamedTemporaryFile(delete=delete, suffix='.txt') as the_bbcode: + self.write_body(the_bbcode) + if inputs["clipboard_copy"]: + the_bbcode.seek(0) + sublime.set_clipboard(the_bbcode.read()) + sublime.status_message("Export to BBCode: copied to clipboard") + + if inputs["view_open"]: + self.view.window().open_file(the_bbcode.name) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtml.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtml.py new file mode 100644 index 0000000..93fa85b --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtml.py @@ -0,0 +1,907 @@ +import sublime +import sublime_plugin +from os import path +import tempfile +import sys +import time +import webbrowser +import re +from HtmlAnnotations import get_annotations +import ExportHtmlLib.desktop as desktop +import json + +PACKAGE_SETTINGS = "ExportHtml.sublime-settings" +JS_DIR = path.join(sublime.packages_path(), 'ExportHtml', "js") +CSS_DIR = path.join(sublime.packages_path(), 'ExportHtml', "css") + +if sublime.platform() == "linux": + # Try and load Linux Python2.6 lib. Default path is for Ubuntu. + linux_lib = sublime.load_settings(PACKAGE_SETTINGS).get("linux_python2.6_lib", "/usr/lib/python2.6/lib-dynload") + if not linux_lib in sys.path and path.exists(linux_lib): + sys.path.append(linux_lib) +from plistlib import readPlist +from ExportHtmlLib.rgba.rgba import RGBA + +FILTER_MATCH = re.compile(r'^(?:(brightness|saturation|hue|colorize)\((-?[\d]+|[\d]*\.[\d]+)\)|(sepia|grayscale|invert))$') + +# HTML Code +HTML_HEADER = \ +''' + + +%(title)s + + + + +%(js)s + +''' + +TOOL_GUTTER = '''''' + +TOOL_PLAIN_TEXT = '''''' + +TOOL_PRINT = '''''' + +TOOL_ANNOTATION = '''''' + +TOOL_DUMP_THEME = '''''' + +TOOL_WRAPPING = '''''' + +TOOLBAR = '''
%(options)s
''' + +ANNOTATE_OPEN = '''%(code)s''' + +ANNOTATE_CLOSE = '''''' + +BODY_START = '''
'''
+
+FILE_INFO = '''
%(date_time)s %(file)s\n\n
''' + +TABLE_START = '''''' + +LINE = ( + '' + + '' + + '' + + '' +) + +CODE = '''%(content)s''' +ANNOTATION_CODE = '''%(content)s''' + +TABLE_END = '''
' + + '%(line)s ' + + '' + + '
%(code)s\n
' + + '
''' + +ROW_START = '''''' + +ROW_END = '''''' + +DIVIDER = '''\n...\n\n''' + +ANNOTATION_TBL_START = ( + '''' + +ANNOTATION_ROW = ( + '' + + '' + + '%(link)s' + + '' + + '
%(comment)s
' + + '' +) + +ANNOTATION_FOOTER = ( + '' + + '' + + '' +) + +BODY_END = '''
%(toolbar)s\n%(js)s\n\n\n''' + +INCLUDE_THEME = \ +''' + +''' + +TOGGLE_LINE_OPTIONS = \ +''' + +''' + +AUTO_PRINT = \ +''' + +''' + +WRAP = \ +''' + +''' + +HTML_JS_WRAP = \ +''' + +''' + + +def getjs(file_name): + code = "" + try: + with open(path.join(JS_DIR, file_name), "r") as f: + code = f.read() + except: + pass + return code + + +def getcss(file_name, options): + code = "" + final_code = "" + last_pt = 0 + keys = '|'.join(options.keys()) + replace = re.compile("/\\* *%(" + keys + ")% * \\*/") + + try: + with open(path.join(CSS_DIR, file_name), "r") as f: + code = f.read() + for m in replace.finditer(code): + final_code += code[last_pt:m.start()] + options[m.group(1)] + last_pt = m.end() + final_code += code[last_pt:] + except: + pass + + return final_code + + +class ExportHtmlPanelCommand(sublime_plugin.WindowCommand): + def execute(self, value): + if value >= 0: + view = self.window.active_view() + if view != None: + ExportHtml(view).run(**self.args[value]) + + def run(self): + options = sublime.load_settings(PACKAGE_SETTINGS).get("html_panel", {}) + menu = [] + self.args = [] + for opt in options: + k, v = opt.items()[0] + menu.append(k) + self.args.append(v) + + if len(menu): + self.window.show_quick_panel( + menu, + self.execute + ) + + +class ExportHtmlCommand(sublime_plugin.WindowCommand): + def run(self, **kwargs): + view = self.window.active_view() + if view != None: + ExportHtml(view).run(**kwargs) + + +class ExportHtml(object): + def __init__(self, view): + self.view = view + + def process_inputs(self, **kwargs): + return { + "numbers": bool(kwargs.get("numbers", False)), + "highlight_selections": bool(kwargs.get("highlight_selections", False)), + "browser_print": bool(kwargs.get("browser_print", False)), + "color_scheme": kwargs.get("color_scheme", None), + "wrap": kwargs.get("wrap", None), + "multi_select": bool(kwargs.get("multi_select", False)), + "style_gutter": bool(kwargs.get("style_gutter", True)), + "no_header": bool(kwargs.get("no_header", False)), + "date_time_format": kwargs.get("date_time_format", "%m/%d/%y %I:%M:%S"), + "show_full_path": bool(kwargs.get("show_full_path", True)), + "toolbar": kwargs.get("toolbar", ["plain_text", "gutter", "wrapping", "print", "annotation", "theme"]), + "save_location": kwargs.get("save_location", None), + "time_stamp": kwargs.get("time_stamp", "_%m%d%y%H%M%S"), + "clipboard_copy": bool(kwargs.get("clipboard_copy", False)), + "view_open": bool(kwargs.get("view_open", False)), + "shift_brightness": bool(kwargs.get("shift_brightness", False)), + "filter": kwargs.get("filter", "") + } + + def setup(self, **kwargs): + path_packages = sublime.packages_path() + + # Get get general document preferences from sublime preferences + eh_settings = sublime.load_settings(PACKAGE_SETTINGS) + settings = sublime.load_settings('Preferences.sublime-settings') + alternate_font_size = eh_settings.get("alternate_font_size", False) + alternate_font_face = eh_settings.get("alternate_font_face", False) + self.font_size = settings.get('font_size', 10) if alternate_font_size == False else alternate_font_size + self.font_face = settings.get('font_face', 'Consolas') if alternate_font_face == False else alternate_font_face + self.tab_size = settings.get('tab_size', 4) + self.padd_top = settings.get('line_padding_top', 0) + self.padd_bottom = settings.get('line_padding_bottom', 0) + self.char_limit = int(eh_settings.get("valid_selection_size", 4)) + self.bground = '' + self.fground = '' + self.gbground = '' + self.gfground = '' + self.sbground = '' + self.sfground = '' + self.numbers = kwargs["numbers"] + self.date_time_format = kwargs["date_time_format"] + self.time = time.localtime() + self.show_full_path = kwargs["show_full_path"] + self.highlight_selections = kwargs["highlight_selections"] + self.browser_print = kwargs["browser_print"] + self.auto_wrap = kwargs["wrap"] != None and int(kwargs["wrap"]) > 0 + self.wrap = 900 if not self.auto_wrap else int(kwargs["wrap"]) + self.hl_continue = None + self.curr_hl = None + self.sels = [] + self.multi_select = self.check_sel() if kwargs["multi_select"] and not kwargs["highlight_selections"] else False + self.size = self.view.size() + self.pt = 0 + self.end = 0 + self.curr_row = 0 + self.tables = 0 + self.curr_annot = None + self.curr_comment = None + self.annotations = self.get_annotations() + self.annot_num = -1 + self.new_annot = False + self.open_annot = False + self.no_header = kwargs["no_header"] + self.annot_tbl = [] + self.toolbar = kwargs["toolbar"] + self.toolbar_orientation = "block" if eh_settings.get("toolbar_orientation", "horizontal") == "vertical" else "inline-block" + self.matched = {} + self.ebground = self.bground + self.dark_lumens = None + self.lumens_limit = float(eh_settings.get("bg_min_lumen_threshold", 62)) + self.filter = [] + for f in kwargs["filter"].split(";"): + m = FILTER_MATCH.match(f) + if m: + if m.group(1): + self.filter.append((m.group(1), float(m.group(2)))) + else: + self.filter.append((m.group(3), 0.0)) + + fname = self.view.file_name() + if fname == None or not path.exists(fname): + fname = "Untitled" + self.file_name = fname + + # Get color scheme + if kwargs["color_scheme"] != None: + alt_scheme = kwargs["color_scheme"] + else: + alt_scheme = eh_settings.get("alternate_scheme", False) + scheme_file = settings.get('color_scheme') if alt_scheme == False else alt_scheme + colour_scheme = path.normpath(scheme_file) + self.scheme_file = path.basename(colour_scheme) + self.plist_file = self.apply_filters(readPlist(path_packages + colour_scheme.replace('Packages', ''))) + colour_settings = self.plist_file["settings"][0]["settings"] + + # Get general theme colors from color scheme file + self.bground = self.strip_transparency(colour_settings.get("background", '#FFFFFF'), True, True) + self.fground = self.strip_transparency(colour_settings.get("foreground", '#000000')) + self.sbground = self.strip_transparency(colour_settings.get("selection", self.fground), True) + self.sfground = self.strip_transparency(colour_settings.get("selectionForeground", None)) + self.gbground = self.strip_transparency(colour_settings.get("gutter", self.bground)) if kwargs["style_gutter"] else self.bground + self.gfground = self.strip_transparency(colour_settings.get("gutterForeground", self.fground), True) if kwargs["style_gutter"] else self.fground + + self.highlights = [] + if self.highlight_selections: + for sel in self.view.sel(): + if not sel.empty(): + self.highlights.append(sel) + + # Create scope colors mapping from color scheme file + self.colours = {self.view.scope_name(self.end).split(' ')[0]: {"color": self.fground, "bgcolor": None, "style": None}} + for item in self.plist_file["settings"]: + scope = item.get('scope', None) + colour = None + style = [] + if 'scope' in item: + scope = item['scope'] + if 'settings' in item: + colour = item['settings'].get('foreground', None) + bgcolour = item['settings'].get('background', None) + if 'fontStyle' in item['settings']: + for s in item['settings']['fontStyle'].split(' '): + if s == "bold" or s == "italic": # or s == "underline": + style.append(s) + + if scope != None and (colour != None or bgcolour != None): + self.colours[scope] = { + "color": self.strip_transparency(colour), + "bgcolor": self.strip_transparency(bgcolour, True), + "style": style + } + + self.shift_brightness = kwargs["shift_brightness"] and self.dark_lumens is not None and self.dark_lumens < self.lumens_limit + if self.shift_brightness: + self.color_adjust() + + def apply_filters(self, tmtheme): + def filter_color(color): + rgba = RGBA(color) + for f in self.filter: + name = f[0] + value = f[1] + if name == "grayscale": + rgba.grayscale() + elif name == "sepia": + rgba.sepia() + elif name == "saturation": + rgba.saturation(value) + elif name == "invert": + rgba.invert() + elif name == "brightness": + rgba.brightness(value) + elif name == "hue": + rgba.hue(value) + elif name == "colorize": + rgba.colorize(value) + return rgba.get_rgba() + + if len(self.filter): + general_settings_read = False + for settings in tmtheme["settings"]: + if not general_settings_read: + for k, v in settings["settings"].items(): + try: + settings["settings"][k] = filter_color(v) + except: + pass + general_settings_read = True + continue + + try: + settings["settings"]["foreground"] = filter_color(settings["settings"]["foreground"]) + except: + pass + try: + settings["settings"]["background"] = filter_color(settings["settings"]["background"]) + except: + pass + return tmtheme + + def color_adjust(self): + factor = 1 + ((self.lumens_limit - self.dark_lumens) / 255.0) if self.shift_brightness else None + for k, v in self.colours.items(): + fg, bg = v["color"], v["bgcolor"] + if v["color"] is not None: + self.colours[k]["color"] = self.apply_color_change(v["color"], factor) + if v["bgcolor"] is not None: + self.colours[k]["bgcolor"] = self.apply_color_change(v["bgcolor"], factor) + self.bground = self.apply_color_change(self.bground, factor) + self.fground = self.apply_color_change(self.fground, factor) + self.sbground = self.apply_color_change(self.sbground, factor) + if self.sfground is not None: + self.sfground = self.apply_color_change(self.sfground, factor) + self.gbground = self.apply_color_change(self.gbground, factor) + self.gfground = self.apply_color_change(self.gfground, factor) + + def apply_color_change(self, color, shift_factor): + rgba = RGBA(color) + if shift_factor is not None: + rgba.brightness(shift_factor) + return rgba.get_rgb() + + def get_tools(self, tools, use_annotation, use_wrapping): + toolbar_options = { + "gutter": TOOL_GUTTER, + "print": TOOL_PRINT, + "plain_text": TOOL_PLAIN_TEXT, + "annotation": TOOL_ANNOTATION if use_annotation else "", + "theme": TOOL_DUMP_THEME, + "wrapping": TOOL_WRAPPING if use_wrapping else "" + } + t_opt = "" + toolbar_element = "" + + if len(tools): + for t in tools: + if t in toolbar_options: + t_opt += toolbar_options[t] + toolbar_element = TOOLBAR % {"options": t_opt} + return toolbar_element + + def strip_transparency(self, color, track_darkness=False, simple_strip=False): + if color is None: + return color + rgba = RGBA(color.replace(" ", "")) + if not simple_strip: + rgba.apply_alpha(self.bground if self.bground != "" else "#FFFFFF") + if track_darkness: + lumens = rgba.luminance() + if self.dark_lumens is None or lumens < self.dark_lumens: + self.dark_lumens = lumens + return rgba.get_rgb() + + def setup_print_block(self, curr_sel, multi=False): + # Determine start and end points and whether to parse whole file or selection + if not multi and (curr_sel.empty() or self.highlight_selections or curr_sel.size() <= self.char_limit): + self.size = self.view.size() + self.pt = 0 + self.end = 1 + self.curr_row = 1 + else: + self.size = curr_sel.end() + self.pt = curr_sel.begin() + self.end = self.pt + 1 + self.curr_row = self.view.rowcol(self.pt)[0] + 1 + self.start_line = self.curr_row + + self.gutter_pad = len(str(self.view.rowcol(self.size)[0])) + 1 + + def check_sel(self): + multi = False + for sel in self.view.sel(): + if not sel.empty() and sel.size() >= self.char_limit: + multi = True + self.sels.append(sel) + return multi + + def print_line(self, line, num): + html_line = LINE % { + "line_id": num, + "color": self.gfground, + "line": str(num).rjust(self.gutter_pad).replace(" ", ' '), + "code_id": num, + "code": line, + "table": self.tables, + "pad_color": self.ebground or self.bground + } + + return html_line + + def guess_colour(self, pt, the_key): + the_colour = self.fground + the_bgcolour = None + the_style = set([]) + if the_key in self.matched: + the_colour = self.matched[the_key]["color"] + the_style = self.matched[the_key]["style"] + the_bgcolour = self.matched[the_key]["bgcolor"] + else: + best_match_bg = 0 + best_match_fg = 0 + best_match_style = 0 + for key in self.colours: + match = self.view.score_selector(pt, key) + if self.colours[key]["color"] is not None and match > best_match_fg: + best_match_fg = match + the_colour = self.colours[key]["color"] + if self.colours[key]["style"] is not None and match > best_match_style: + best_match_style = match + for s in self.colours[key]["style"]: + the_style.add(s) + if self.colours[key]["bgcolor"] is not None and match > best_match_bg: + best_match_bg = match + the_bgcolour = self.colours[key]["bgcolor"] + self.matched[the_key] = {"color": the_colour, "bgcolor": the_bgcolour, "style": the_style} + if len(the_style) == 0: + the_style = "normal" + else: + the_style = ' '.join(the_style) + return the_colour, the_style, the_bgcolour + + def write_header(self, the_html): + header = HTML_HEADER % { + "title": path.basename(self.file_name), + "css": getcss( + 'export.css', + { + "font_size": str(self.font_size), + "font_face": '"' + self.font_face + '"', + "page_bg": self.bground, + "gutter_bg": self.gbground, + "body_fg": self.fground, + "display_mode": 'table-cell' if self.numbers else 'none', + "dot_color": self.fground, + "toolbar_orientation": self.toolbar_orientation + } + ), + "js": INCLUDE_THEME % { + "jscode": getjs('plist.js'), + "theme": json.dumps(self.plist_file, sort_keys=True, indent=4, separators=(',', ': ')).encode('raw_unicode_escape'), + "name": self.scheme_file, + } + } + the_html.write(header) + + def convert_view_to_html(self, the_html): + for line in self.view.split_by_newlines(sublime.Region(self.pt, self.size)): + self.size = line.end() + empty = not bool(line.size()) + line = self.convert_line_to_html(the_html, empty) + the_html.write(self.print_line(line, self.curr_row)) + self.curr_row += 1 + + def html_encode(self, text): + # Format text to HTML + encode_table = { + '&': '&', + '>': '>', + '<': '<', + '\t': ' ' * self.tab_size, + ' ': ' ', + '\n': '' + } + + return ''.join(encode_table.get(c, c) for c in text).encode('ascii', 'xmlcharrefreplace') + + def get_annotations(self): + annotations = get_annotations(self.view) + comments = [] + for x in range(0, int(annotations["count"])): + region = annotations["annotations"]["html_annotation_%d" % x]["region"] + comments.append((region, annotations["annotations"]["html_annotation_%d" % x]["comment"])) + comments.sort() + return comments + + def annotate_text(self, line, the_colour, the_bgcolour, the_style, empty): + pre_text = None + annot_text = None + post_text = None + start = None + + # Pretext Check + if self.pt >= self.curr_annot.begin(): + # Region starts with an annotation + start = self.pt + else: + # Region has text before annoation + pre_text = self.html_encode(self.view.substr(sublime.Region(self.pt, self.curr_annot.begin()))) + start = self.curr_annot.begin() + + if self.end == self.curr_annot.end(): + # Region ends annotation + annot_text = self.html_encode(self.view.substr(sublime.Region(start, self.end))) + self.curr_annot = None + elif self.end > self.curr_annot.end(): + # Region has text following annotation + annot_text = self.html_encode(self.view.substr(sublime.Region(start, self.curr_annot.end()))) + post_text = self.html_encode(self.view.substr(sublime.Region(self.curr_annot.end(), self.end))) + self.curr_annot = None + else: + # Region ends but annotation is not finished + annot_text = self.html_encode(self.view.substr(sublime.Region(start, self.end))) + self.curr_annot = sublime.Region(self.end, self.curr_annot.end()) + + # Print the separate parts pre text, annotation, post text + if pre_text != None: + self.format_text(line, pre_text, the_colour, the_bgcolour, the_style, empty) + if annot_text != None: + self.format_text(line, annot_text, the_colour, the_bgcolour, the_style, empty, annotate=True) + if self.curr_annot == None: + self.curr_comment = None + if post_text != None: + self.format_text(line, post_text, the_colour, the_bgcolour, the_style, empty) + + def add_annotation_table_entry(self): + row, col = self.view.rowcol(self.annot_pt) + self.annot_tbl.append( + ( + self.tables, self.curr_row, "Line %d Col %d" % (row + 1, col + 1), + self.curr_comment.encode('ascii', 'xmlcharrefreplace') + ) + ) + self.annot_pt = None + + def format_text(self, line, text, the_colour, the_bgcolour, the_style, empty, annotate=False): + if empty: + text = ' ' + else: + the_style += " real_text" + + if the_bgcolour is None: + the_bgcolour = self.bground + + if annotate: + code = ANNOTATION_CODE % {"highlight": the_bgcolour, "color": the_colour, "content": text, "class": the_style} + else: + code = CODE % {"highlight": the_bgcolour, "color": the_colour, "content": text, "class": the_style} + + if annotate: + if self.curr_annot != None and not self.open_annot: + # Open an annotation + if self.annot_pt != None: + self.add_annotation_table_entry() + if self.new_annot: + self.annot_num += 1 + self.new_annot = False + code = ANNOTATE_OPEN % {"code": code, "comment": str(self.annot_num)} + self.open_annot = True + elif self.curr_annot == None: + if self.open_annot: + # Close an annotation + code += ANNOTATE_CLOSE + self.open_annot = False + else: + # Do a complete annotation + if self.annot_pt != None: + self.add_annotation_table_entry() + if self.new_annot: + self.annot_num += 1 + self.new_annot = False + code = ( + ANNOTATE_OPEN % {"code": code, "comment": str(self.annot_num)} + + ANNOTATE_CLOSE + ) + line.append(code) + + def convert_line_to_html(self, the_html, empty): + line = [] + hl_done = False + + # Continue highlight form last line + if self.hl_continue != None: + self.curr_hl = self.hl_continue + self.hl_continue = None + + while self.end <= self.size: + # Get next highlight region + if self.highlight_selections and self.curr_hl == None and len(self.highlights) > 0: + self.curr_hl = self.highlights.pop(0) + + # See if we are starting a highlight region + if self.curr_hl != None and self.pt == self.curr_hl.begin(): + # Get text of like scope up to a highlight + scope_name = self.view.scope_name(self.pt) + while self.view.scope_name(self.end) == scope_name and self.end < self.size: + # Kick out if we hit a highlight region + if self.end == self.curr_hl.end(): + break + self.end += 1 + if self.end < self.curr_hl.end(): + if self.end >= self.size: + self.hl_continue = sublime.Region(self.end, self.curr_hl.end()) + else: + self.curr_hl = sublime.Region(self.end, self.curr_hl.end()) + else: + hl_done = True + if hl_done and empty: + the_colour, the_style, the_bgcolour = self.guess_colour(self.pt, scope_name) + elif self.sfground is None: + the_colour, the_style, _ = self.guess_colour(self.pt, scope_name) + the_bgcolour = self.sbground + else: + the_colour, the_style = self.sfground, "normal" + the_bgcolour = self.sbground + else: + # Get text of like scope up to a highlight + scope_name = self.view.scope_name(self.pt) + while self.view.scope_name(self.end) == scope_name and self.end < self.size: + # Kick out if we hit a highlight region + if self.curr_hl != None and self.end == self.curr_hl.begin(): + break + self.end += 1 + the_colour, the_style, the_bgcolour = self.guess_colour(self.pt, scope_name) + + # Get new annotation + if (self.curr_annot == None or self.curr_annot.end() < self.pt) and len(self.annotations): + self.curr_annot, self.curr_comment = self.annotations.pop(0) + self.annot_pt = self.curr_annot[0] + while self.pt > self.curr_annot[1]: + if len(self.annotations): + self.curr_annot, self.curr_comment = self.annotations.pop(0) + self.annot_pt = self.curr_annot[0] + else: + self.curr_annot = None + self.curr_comment = None + break + self.new_annot = True + self.curr_annot = sublime.Region(self.curr_annot[0], self.curr_annot[1]) + + region = sublime.Region(self.pt, self.end) + if self.curr_annot != None and region.intersects(self.curr_annot): + # Apply annotation within the text and format the text + self.annotate_text(line, the_colour, the_bgcolour, the_style, empty) + else: + # Normal text formatting + tidied_text = self.html_encode(self.view.substr(region)) + self.format_text(line, tidied_text, the_colour, the_bgcolour, the_style, empty) + + if hl_done: + # Clear highlight flags and variables + hl_done = False + self.curr_hl = None + + # Continue walking through line + self.pt = self.end + self.end = self.pt + 1 + + # Close annotation if open at end of line + if self.open_annot: + line.append(ANNOTATE_CLOSE % {"comment": self.curr_comment}) + self.open_annot = False + + # Get the color for the space at the end of a line + if self.end < self.view.size(): + end_key = self.view.scope_name(self.pt) + _, _, self.ebground = self.guess_colour(self.pt, end_key) + + # Join line segments + return ''.join(line) + + def write_body(self, the_html): + processed_rows = "" + the_html.write(BODY_START) + + the_html.write(TABLE_START) + if not self.no_header: + # Write file name + date_time = time.strftime(self.date_time_format, self.time) + the_html.write( + FILE_INFO % { + "color": self.fground, + "date_time": date_time, + "file": self.file_name if self.show_full_path else path.basename(self.file_name) + } + ) + + the_html.write(ROW_START) + the_html.write(TABLE_START) + # Convert view to HTML + if self.multi_select: + count = 0 + total = len(self.sels) + for sel in self.sels: + self.setup_print_block(sel, multi=True) + processed_rows += "[" + str(self.curr_row) + "," + self.convert_view_to_html(the_html) + count += 1 + self.tables = count + processed_rows += str(self.curr_row) + "]," + + if count < total: + the_html.write(TABLE_END) + the_html.write(ROW_END) + the_html.write(ROW_START) + the_html.write(DIVIDER % {"color": self.fground}) + the_html.write(ROW_END) + the_html.write(ROW_START) + the_html.write(TABLE_START) + else: + self.setup_print_block(self.view.sel()[0]) + processed_rows += "[" + str(self.curr_row) + "," + self.convert_view_to_html(the_html) + processed_rows += str(self.curr_row) + "]," + self.tables += 1 + + the_html.write(TABLE_END) + the_html.write(ROW_END) + the_html.write(TABLE_END) + + js_options = [] + if len(self.annot_tbl): + self.add_comments_table(the_html) + js_options.append(HTML_JS_WRAP % {"jscode": getjs('annotation.js')}) + + # Write javascript snippets + js_options.append(HTML_JS_WRAP % {"jscode": getjs('print.js')}) + js_options.append(HTML_JS_WRAP % {"jscode": getjs('plaintext.js')}) + js_options.append(TOGGLE_LINE_OPTIONS % { + "jscode": getjs('lines.js'), + "wrap_size": self.wrap, + "ranges": processed_rows.rstrip(','), + "tables": self.tables, + "header": ("false" if self.no_header else "true"), + "gutter": ('true' if self.numbers else 'false') + } + ) + if self.auto_wrap: + js_options.append(WRAP) + + if self.browser_print: + js_options.append(AUTO_PRINT) + + # Write empty line to allow copying of last line and line number without issue + the_html.write(BODY_END % {"js": ''.join(js_options), "toolbar": self.get_tools(self.toolbar, len(self.annot_tbl), self.auto_wrap)}) + + def add_comments_table(self, the_html): + the_html.write(ANNOTATION_TBL_START) + the_html.write(''.join([ANNOTATION_ROW % {"table": t, "row": r, "link": l, "comment": c} for t, r, l, c in self.annot_tbl])) + the_html.write(ANNOTATION_FOOTER) + the_html.write(ANNOTATION_TBL_END) + + def run(self, **kwargs): + inputs = self.process_inputs(**kwargs) + self.setup(**inputs) + + save_location = inputs["save_location"] + time_stamp = inputs["time_stamp"] + + if save_location is not None: + fname = self.view.file_name() + if ( + ((fname == None or not path.exists(fname)) and save_location == ".") or + not path.exists(save_location) + or not path.isdir(save_location) + ): + html_file = ".html" + save_location = None + elif save_location == ".": + html_file = "%s%s.html" % (fname, time.strftime(time_stamp, self.time)) + elif fname is None or not path.exists(fname): + html_file = path.join(save_location, "Untitled%s.html" % time.strftime(time_stamp, self.time)) + else: + html_file = path.join(save_location, "%s%s.html" % (path.basename(fname), time.strftime(time_stamp, self.time))) + else: + html_file = ".html" + + if save_location is not None: + open_html = lambda x: open(x, "w") + else: + open_html = lambda x: tempfile.NamedTemporaryFile(delete=False, suffix=x) + + with open_html(html_file) as the_html: + self.write_header(the_html) + self.write_body(the_html) + if inputs["clipboard_copy"]: + the_html.seek(0) + sublime.set_clipboard(the_html.read()) + sublime.status_message("Export to HTML: copied to clipboard") + + if inputs["view_open"]: + self.view.window().open_file(the_html.name) + else: + # Open in web browser; check return code, if failed try webbrowser + status = desktop.open(the_html.name, status=True) + if not status: + webbrowser.open(the_html.name, new=2) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtml.sublime-settings b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtml.sublime-settings new file mode 100644 index 0000000..81ce6ae --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtml.sublime-settings @@ -0,0 +1,180 @@ +{ + // By default, the current color scheme is used + // You can define a string path to an alternate default + // Color scheme here, it must be relative to the + // Example: + // "alternate_scheme": "Packages/ExportHtml/themes/Print-Color.tmTheme" + "alternate_scheme": false, + + // By default, ExportHtml uses your current font_face and font_size. + // You can change this setting to always use this value. By default, + // the setting is a literal false, but you can change it to an actual + // font_face to enable this setting. + "alternate_font_face": false, + + // By default, ExportHtml uses your current font_face and font_size. + // You can change this setting to always use this value. By default, + // the setting is a literal false, but you can change it to an actual + // font_size to enable this setting. + "alternate_font_size": false, + + //Path to linux Python 2.6 lib + "linux_python2.6_lib": "/usr/lib/python2.6/lib-dynload", + + // Minimum allowable size for a selection to be accepted for only the selection to be exproted + // Multi-select will also ignore selections whose size is less than this. + "valid_selection_size": 4, + + // Scope to use for color for annotations in a Sublime Text view + "annotation_highlight_scope": "comment", + + // Style to use for for annotations in a Sublime Text view + // (outline|solid) + "annotation_highlight_style": "outline", + + // Orientation that the toolbar will be rendered with. + // (horizontal|vertical) + "toolbar_orientation": "horizontal", + + // Define configurations for the drop down export menu + "html_panel": [ + // Browser print color (selections and multi-selections allowed) + { + "Browser Print - Color": { + "numbers": true, + "wrap": 900, + "browser_print": true, + "multi_select": true, + "color_scheme": "Packages/ExportHtml/ColorSchemes/Print-Color.tmTheme", + "style_gutter": false + } + }, + + // Browser print black and white (selections and multi-selections allowed) + { + "Browser Print - Grayscale": { + "numbers": true, + "wrap": 900, + "browser_print": true, + "multi_select": true, + "color_scheme": "Packages/ExportHtml/ColorSchemes/Print-Grayscale.tmTheme", + "style_gutter": false + } + }, + + // Browser print color; highlight selections(selections and multi-selections allowed) + // Background colors are disabled in browser printing by default, so they will not + // print until background color printing is enabled + { + "Browser Print - Color (Selection Highlights)": { + "numbers": true, + "wrap": 900, + "browser_print": true, + "highlight_selections": true, + "color_scheme": "Packages/ExportHtml/ColorSchemes/Print-Color.tmTheme", + "style_gutter": false + } + }, + + // Browser print black and white; highlight selections(selections and multi-selections allowed) + // Background colors are disabled in browser printing by default, so they will not + // print until background color printing is enabled + { + "Browser Print - Grayscale (Selection Highlights)": { + "numbers": true, + "wrap": 900, + "browser_print": true, + "highlight_selections": true, + "color_scheme": "Packages/ExportHtml/ColorSchemes/Print-Grayscale.tmTheme", + "style_gutter": false + } + }, + // Browser view color (selections and multi-selections allowed) + { + "Browser View - Color": { + "numbers": true, + "multi_select": true, + "color_scheme": "Packages/ExportHtml/ColorSchemes/Print-Color.tmTheme" + } + }, + + // Browser view black and white (selections and multi-selections allowed) + { + "Browser View - Grayscale": { + "numbers": true, + "multi_select": true, + "color_scheme": "Packages/ExportHtml/ColorSchemes/Print-Grayscale.tmTheme" + } + }, + + // Browser view color; highlight selections(selections and multi-selections allowed) + { + "Browser View - Color (Selection Highlights)": { + "numbers": true, + "highlight_selections": true, + "color_scheme": "Packages/ExportHtml/ColorSchemes/Print-Color.tmTheme" + } + }, + + // Browser view black and white; highlight selections(selections and multi-selections allowed) + { + "Browser View - Grayscale (Selection Highlights)": { + "numbers": true, + "highlight_selections": true, + "color_scheme": "Packages/ExportHtml/ColorSchemes/Print-Grayscale.tmTheme" + } + }, + + // Sublime view grayscale HTML source; (selections and multi-selections allowed) + { + "Sublime View - Color": { + "view_open": true, + "numbers": true, + "wrap": 900, + "multi_select": true, + "color_scheme": "Packages/ExportHtml/ColorSchemes/Print-Color.tmTheme" + } + }, + + // Sublime view grayscale HTML source; (selections and multi-selections allowed) + { + "Sublime View - Grayscale": { + "view_open": true, + "numbers": true, + "wrap": 900, + "multi_select": true, + "color_scheme": "Packages/ExportHtml/ColorSchemes/Print-Grayscale.tmTheme" + } + } + ], + + "bbcode_panel": [ + { + "To Clipboard - Format as BBCode": { + "numbers": false, + "multi_select": true + } + }, + + { + "To Clipboard - Format as BBCode (show gutter)": { + "numbers": true, + "multi_select": true + } + }, + { + "Sublime View - Show BBCode in View": { + "numbers": false, + "multi_select": true, + "view_open": true + } + }, + { + "Sublime View - Show BBCode in View (show gutter)": { + "numbers": false, + "multi_select": true, + "view_open": true + } + } + ] +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/__init__.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/desktop/__init__.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/desktop/__init__.py new file mode 100644 index 0000000..9be2bbc --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/desktop/__init__.py @@ -0,0 +1,285 @@ +#!/usr/bin/env python + +""" +Simple desktop integration for Python. This module provides desktop environment +detection and resource opening support for a selection of common and +standardised desktop environments. + +Copyright (C) 2005, 2006, 2007, 2008, 2009 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along +with this program. If not, see . + +-------- + +Desktop Detection +----------------- + +To detect a specific desktop environment, use the get_desktop function. +To detect whether the desktop environment is standardised (according to the +proposed DESKTOP_LAUNCH standard), use the is_standard function. + +Opening URLs +------------ + +To open a URL in the current desktop environment, relying on the automatic +detection of that environment, use the desktop.open function as follows: + +desktop.open("http://www.python.org") + +To override the detected desktop, specify the desktop parameter to the open +function as follows: + +desktop.open("http://www.python.org", "KDE") # Insists on KDE +desktop.open("http://www.python.org", "GNOME") # Insists on GNOME + +Without overriding using the desktop parameter, the open function will attempt +to use the "standard" desktop opening mechanism which is controlled by the +DESKTOP_LAUNCH environment variable as described below. + +The DESKTOP_LAUNCH Environment Variable +--------------------------------------- + +The DESKTOP_LAUNCH environment variable must be shell-quoted where appropriate, +as shown in some of the following examples: + +DESKTOP_LAUNCH="kdialog --msgbox" Should present any opened URLs in + their entirety in a KDE message box. + (Command "kdialog" plus parameter.) +DESKTOP_LAUNCH="my\ opener" Should run the "my opener" program to + open URLs. + (Command "my opener", no parameters.) +DESKTOP_LAUNCH="my\ opener --url" Should run the "my opener" program to + open URLs. + (Command "my opener" plus parameter.) + +Details of the DESKTOP_LAUNCH environment variable convention can be found here: +http://lists.freedesktop.org/archives/xdg/2004-August/004489.html + +Other Modules +------------- + +The desktop.dialog module provides support for opening dialogue boxes. +The desktop.windows module permits the inspection of desktop windows. +""" + +__version__ = "0.4" + +import os +import sys + +# Provide suitable process creation functions. + +try: + import subprocess + def _run(cmd, shell, wait): + opener = subprocess.Popen(cmd, shell=shell) + if wait: opener.wait() + return opener.pid + + def _readfrom(cmd, shell): + opener = subprocess.Popen(cmd, shell=shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE) + opener.stdin.close() + return opener.stdout.read() + + def _status(cmd, shell): + opener = subprocess.Popen(cmd, shell=shell) + opener.wait() + return opener.returncode == 0 + +except ImportError: + import popen2 + def _run(cmd, shell, wait): + opener = popen2.Popen3(cmd) + if wait: opener.wait() + return opener.pid + + def _readfrom(cmd, shell): + opener = popen2.Popen3(cmd) + opener.tochild.close() + opener.childerr.close() + return opener.fromchild.read() + + def _status(cmd, shell): + opener = popen2.Popen3(cmd) + opener.wait() + return opener.poll() == 0 + +import commands + +# Private functions. + +def _get_x11_vars(): + + "Return suitable environment definitions for X11." + + if not os.environ.get("DISPLAY", "").strip(): + return "DISPLAY=:0.0 " + else: + return "" + +def _is_xfce(): + + "Return whether XFCE is in use." + + # XFCE detection involves testing the output of a program. + + try: + return _readfrom(_get_x11_vars() + "xprop -root _DT_SAVE_MODE", shell=1).strip().endswith(' = "xfce4"') + except OSError: + return 0 + +def _is_x11(): + + "Return whether the X Window System is in use." + + return os.environ.has_key("DISPLAY") + +# Introspection functions. + +def get_desktop(): + + """ + Detect the current desktop environment, returning the name of the + environment. If no environment could be detected, None is returned. + """ + + if os.environ.has_key("KDE_FULL_SESSION") or \ + os.environ.has_key("KDE_MULTIHEAD"): + return "KDE" + elif os.environ.has_key("GNOME_DESKTOP_SESSION_ID") or \ + os.environ.has_key("GNOME_KEYRING_SOCKET"): + return "GNOME" + elif sys.platform == "darwin": + return "Mac OS X" + elif hasattr(os, "startfile"): + return "Windows" + elif _is_xfce(): + return "XFCE" + + # KDE, GNOME and XFCE run on X11, so we have to test for X11 last. + + if _is_x11(): + return "X11" + else: + return None + +def use_desktop(desktop): + + """ + Decide which desktop should be used, based on the detected desktop and a + supplied 'desktop' argument (which may be None). Return an identifier + indicating the desktop type as being either "standard" or one of the results + from the 'get_desktop' function. + """ + + # Attempt to detect a desktop environment. + + detected = get_desktop() + + # Start with desktops whose existence can be easily tested. + + if (desktop is None or desktop == "standard") and is_standard(): + return "standard" + elif (desktop is None or desktop == "Windows") and detected == "Windows": + return "Windows" + + # Test for desktops where the overriding is not verified. + + elif (desktop or detected) == "KDE": + return "KDE" + elif (desktop or detected) == "GNOME": + return "GNOME" + elif (desktop or detected) == "XFCE": + return "XFCE" + elif (desktop or detected) == "Mac OS X": + return "Mac OS X" + elif (desktop or detected) == "X11": + return "X11" + else: + return None + +def is_standard(): + + """ + Return whether the current desktop supports standardised application + launching. + """ + + return os.environ.has_key("DESKTOP_LAUNCH") + +# Activity functions. + +def open(url, desktop=None, wait=0, status=False): + + """ + Open the 'url' in the current desktop's preferred file browser. If the + optional 'desktop' parameter is specified then attempt to use that + particular desktop environment's mechanisms to open the 'url' instead of + guessing or detecting which environment is being used. + + Suggested values for 'desktop' are "standard", "KDE", "GNOME", "XFCE", + "Mac OS X", "Windows" where "standard" employs a DESKTOP_LAUNCH environment + variable to open the specified 'url'. DESKTOP_LAUNCH should be a command, + possibly followed by arguments, and must have any special characters + shell-escaped. + + The process identifier of the "opener" (ie. viewer, editor, browser or + program) associated with the 'url' is returned by this function. If the + process identifier cannot be determined, None is returned. + + An optional 'wait' parameter is also available for advanced usage and, if + 'wait' is set to a true value, this function will wait for the launching + mechanism to complete before returning (as opposed to immediately returning + as is the default behaviour). + """ + + # Decide on the desktop environment in use. + + desktop_in_use = use_desktop(desktop) + + if desktop_in_use == "standard": + arg = "".join([os.environ["DESKTOP_LAUNCH"], commands.mkarg(url)]) + return _run(arg, 1, wait) + + elif desktop_in_use == "Windows": + # NOTE: This returns None in current implementations. + os.startfile(url) + return True if status else None + + elif desktop_in_use == "KDE": + cmd = ["kfmclient", "exec", url] + + elif desktop_in_use == "GNOME": + cmd = ["gnome-open", url] + + elif desktop_in_use == "XFCE": + cmd = ["exo-open", url] + + elif desktop_in_use == "Mac OS X": + cmd = ["open", url] + + elif desktop_in_use == "X11" and os.environ.has_key("BROWSER"): + cmd = [os.environ["BROWSER"], url] + + # Finish with an error where no suitable desktop was identified. + + else: + raise OSError, "Desktop '%s' not supported (neither DESKTOP_LAUNCH nor os.startfile could be used)" % desktop_in_use + + if status: + return _status(cmd, 0) + else: + return _run(cmd, 0, wait) + +# vim: tabstop=4 expandtab shiftwidth=4 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/desktop/dialog.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/desktop/dialog.py new file mode 100644 index 0000000..9525004 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/desktop/dialog.py @@ -0,0 +1,549 @@ +#!/usr/bin/env python + +""" +Simple desktop dialogue box support for Python. + +Copyright (C) 2007, 2009 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along +with this program. If not, see . + +-------- + +Opening Dialogue Boxes (Dialogs) +-------------------------------- + +To open a dialogue box (dialog) in the current desktop environment, relying on +the automatic detection of that environment, use the appropriate dialogue box +class: + +question = desktop.dialog.Question("Are you sure?") +result = question.open() + +To override the detected desktop, specify the desktop parameter to the open +function as follows: + +question.open("KDE") # Insists on KDE +question.open("GNOME") # Insists on GNOME + +The dialogue box options are documented in each class's docstring. + +Available dialogue box classes are listed in the desktop.dialog.available +attribute. + +Supported desktop environments are listed in the desktop.dialog.supported +attribute. +""" + +from desktop import use_desktop, _run, _readfrom, _status + +class _wrapper: + def __init__(self, handler): + self.handler = handler + +class _readvalue(_wrapper): + def __call__(self, cmd, shell): + return self.handler(cmd, shell).strip() + +class _readinput(_wrapper): + def __call__(self, cmd, shell): + return self.handler(cmd, shell)[:-1] + +class _readvalues_kdialog(_wrapper): + def __call__(self, cmd, shell): + result = self.handler(cmd, shell).strip().strip('"') + if result: + return result.split('" "') + else: + return [] + +class _readvalues_zenity(_wrapper): + def __call__(self, cmd, shell): + result = self.handler(cmd, shell).strip() + if result: + return result.split("|") + else: + return [] + +class _readvalues_Xdialog(_wrapper): + def __call__(self, cmd, shell): + result = self.handler(cmd, shell).strip() + if result: + return result.split("/") + else: + return [] + +# Dialogue parameter classes. + +class String: + + "A generic parameter." + + def __init__(self, name): + self.name = name + + def convert(self, value, program): + return [value or ""] + +class Strings(String): + + "Multiple string parameters." + + def convert(self, value, program): + return value or [] + +class StringPairs(String): + + "Multiple string parameters duplicated to make identifiers." + + def convert(self, value, program): + l = [] + for v in value: + l.append(v) + l.append(v) + return l + +class StringKeyword: + + "A keyword parameter." + + def __init__(self, keyword, name): + self.keyword = keyword + self.name = name + + def convert(self, value, program): + return [self.keyword + "=" + (value or "")] + +class StringKeywords: + + "Multiple keyword parameters." + + def __init__(self, keyword, name): + self.keyword = keyword + self.name = name + + def convert(self, value, program): + l = [] + for v in value or []: + l.append(self.keyword + "=" + v) + return l + +class Integer(String): + + "An integer parameter." + + defaults = { + "width" : 40, + "height" : 15, + "list_height" : 10 + } + scale = 8 + + def __init__(self, name, pixels=0): + String.__init__(self, name) + if pixels: + self.factor = self.scale + else: + self.factor = 1 + + def convert(self, value, program): + if value is None: + value = self.defaults[self.name] + return [str(int(value) * self.factor)] + +class IntegerKeyword(Integer): + + "An integer keyword parameter." + + def __init__(self, keyword, name, pixels=0): + Integer.__init__(self, name, pixels) + self.keyword = keyword + + def convert(self, value, program): + if value is None: + value = self.defaults[self.name] + return [self.keyword + "=" + str(int(value) * self.factor)] + +class Boolean(String): + + "A boolean parameter." + + values = { + "kdialog" : ["off", "on"], + "zenity" : ["FALSE", "TRUE"], + "Xdialog" : ["off", "on"] + } + + def convert(self, value, program): + values = self.values[program] + if value: + return [values[1]] + else: + return [values[0]] + +class MenuItemList(String): + + "A menu item list parameter." + + def convert(self, value, program): + l = [] + for v in value: + l.append(v.value) + l.append(v.text) + return l + +class ListItemList(String): + + "A radiolist/checklist item list parameter." + + def __init__(self, name, status_first=0): + String.__init__(self, name) + self.status_first = status_first + + def convert(self, value, program): + l = [] + for v in value: + boolean = Boolean(None) + status = boolean.convert(v.status, program) + if self.status_first: + l += status + l.append(v.value) + l.append(v.text) + if not self.status_first: + l += status + return l + +# Dialogue argument values. + +class MenuItem: + + "A menu item which can also be used with radiolists and checklists." + + def __init__(self, value, text, status=0): + self.value = value + self.text = text + self.status = status + +# Dialogue classes. + +class Dialogue: + + commands = { + "KDE" : "kdialog", + "GNOME" : "zenity", + "XFCE" : "zenity", # NOTE: Based on observations with Xubuntu. + "X11" : "Xdialog" + } + + def open(self, desktop=None): + + """ + Open a dialogue box (dialog) using a program appropriate to the desktop + environment in use. + + If the optional 'desktop' parameter is specified then attempt to use + that particular desktop environment's mechanisms to open the dialog + instead of guessing or detecting which environment is being used. + + Suggested values for 'desktop' are "standard", "KDE", "GNOME", + "Mac OS X", "Windows". + + The result of the dialogue interaction may be a string indicating user + input (for Input, Password, Menu, Pulldown), a list of strings + indicating selections of one or more items (for RadioList, CheckList), + or a value indicating true or false (for Question, Warning, Message, + Error). + + Where a string value may be expected but no choice is made, an empty + string may be returned. Similarly, where a list of values is expected + but no choice is made, an empty list may be returned. + """ + + # Decide on the desktop environment in use. + + desktop_in_use = use_desktop(desktop) + + # Get the program. + + try: + program = self.commands[desktop_in_use] + except KeyError: + raise OSError, "Desktop '%s' not supported (no known dialogue box command could be suggested)" % desktop_in_use + + # The handler is one of the functions communicating with the subprocess. + # Some handlers return boolean values, others strings. + + handler, options = self.info[program] + + cmd = [program] + for option in options: + if isinstance(option, str): + cmd.append(option) + else: + value = getattr(self, option.name, None) + cmd += option.convert(value, program) + + return handler(cmd, 0) + +class Simple(Dialogue): + def __init__(self, text, width=None, height=None): + self.text = text + self.width = width + self.height = height + +class Question(Simple): + + """ + A dialogue asking a question and showing response buttons. + Options: text, width (in characters), height (in characters) + Response: a boolean value indicating an affirmative response (true) or a + negative response + """ + + name = "question" + info = { + "kdialog" : (_status, ["--yesno", String("text")]), + "zenity" : (_status, ["--question", StringKeyword("--text", "text")]), + "Xdialog" : (_status, ["--stdout", "--yesno", String("text"), Integer("height"), Integer("width")]), + } + +class Warning(Simple): + + """ + A dialogue asking a question and showing response buttons. + Options: text, width (in characters), height (in characters) + Response: a boolean value indicating an affirmative response (true) or a + negative response + """ + + name = "warning" + info = { + "kdialog" : (_status, ["--warningyesno", String("text")]), + "zenity" : (_status, ["--warning", StringKeyword("--text", "text")]), + "Xdialog" : (_status, ["--stdout", "--yesno", String("text"), Integer("height"), Integer("width")]), + } + +class Message(Simple): + + """ + A message dialogue. + Options: text, width (in characters), height (in characters) + Response: a boolean value indicating an affirmative response (true) or a + negative response + """ + + name = "message" + info = { + "kdialog" : (_status, ["--msgbox", String("text")]), + "zenity" : (_status, ["--info", StringKeyword("--text", "text")]), + "Xdialog" : (_status, ["--stdout", "--msgbox", String("text"), Integer("height"), Integer("width")]), + } + +class Error(Simple): + + """ + An error dialogue. + Options: text, width (in characters), height (in characters) + Response: a boolean value indicating an affirmative response (true) or a + negative response + """ + + name = "error" + info = { + "kdialog" : (_status, ["--error", String("text")]), + "zenity" : (_status, ["--error", StringKeyword("--text", "text")]), + "Xdialog" : (_status, ["--stdout", "--msgbox", String("text"), Integer("height"), Integer("width")]), + } + +class Menu(Simple): + + """ + A menu of options, one of which being selectable. + Options: text, width (in characters), height (in characters), + list_height (in items), items (MenuItem objects) + Response: a value corresponding to the chosen item + """ + + name = "menu" + info = { + "kdialog" : (_readvalue(_readfrom), ["--menu", String("text"), MenuItemList("items")]), + "zenity" : (_readvalue(_readfrom), ["--list", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), + MenuItemList("items")] + ), + "Xdialog" : (_readvalue(_readfrom), ["--stdout", "--menubox", + String("text"), Integer("height"), Integer("width"), Integer("list_height"), MenuItemList("items")] + ), + } + item = MenuItem + number_of_titles = 2 + + def __init__(self, text, titles, items=None, width=None, height=None, list_height=None): + + """ + Initialise a menu with the given heading 'text', column 'titles', and + optional 'items' (which may be added later), 'width' (in characters), + 'height' (in characters) and 'list_height' (in items). + """ + + Simple.__init__(self, text, width, height) + self.titles = ([""] * self.number_of_titles + titles)[-self.number_of_titles:] + self.items = items or [] + self.list_height = list_height + + def add(self, *args, **kw): + + """ + Add an item, passing the given arguments to the appropriate item class. + """ + + self.items.append(self.item(*args, **kw)) + +class RadioList(Menu): + + """ + A list of radio buttons, one of which being selectable. + Options: text, width (in characters), height (in characters), + list_height (in items), items (MenuItem objects), titles + Response: a list of values corresponding to chosen items (since some + programs, eg. zenity, appear to support multiple default + selections) + """ + + name = "radiolist" + info = { + "kdialog" : (_readvalues_kdialog(_readfrom), ["--radiolist", String("text"), ListItemList("items")]), + "zenity" : (_readvalues_zenity(_readfrom), + ["--list", "--radiolist", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), + ListItemList("items", 1)] + ), + "Xdialog" : (_readvalues_Xdialog(_readfrom), ["--stdout", "--radiolist", + String("text"), Integer("height"), Integer("width"), Integer("list_height"), ListItemList("items")] + ), + } + number_of_titles = 3 + +class CheckList(Menu): + + """ + A list of checkboxes, many being selectable. + Options: text, width (in characters), height (in characters), + list_height (in items), items (MenuItem objects), titles + Response: a list of values corresponding to chosen items + """ + + name = "checklist" + info = { + "kdialog" : (_readvalues_kdialog(_readfrom), ["--checklist", String("text"), ListItemList("items")]), + "zenity" : (_readvalues_zenity(_readfrom), + ["--list", "--checklist", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), + ListItemList("items", 1)] + ), + "Xdialog" : (_readvalues_Xdialog(_readfrom), ["--stdout", "--checklist", + String("text"), Integer("height"), Integer("width"), Integer("list_height"), ListItemList("items")] + ), + } + number_of_titles = 3 + +class Pulldown(Menu): + + """ + A pull-down menu of options, one of which being selectable. + Options: text, width (in characters), height (in characters), + items (list of values) + Response: a value corresponding to the chosen item + """ + + name = "pulldown" + info = { + "kdialog" : (_readvalue(_readfrom), ["--combobox", String("text"), Strings("items")]), + "zenity" : (_readvalue(_readfrom), + ["--list", "--radiolist", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), + StringPairs("items")] + ), + "Xdialog" : (_readvalue(_readfrom), + ["--stdout", "--combobox", String("text"), Integer("height"), Integer("width"), Strings("items")]), + } + item = unicode + number_of_titles = 2 + +class Input(Simple): + + """ + An input dialogue, consisting of an input field. + Options: text, input, width (in characters), height (in characters) + Response: the text entered into the dialogue by the user + """ + + name = "input" + info = { + "kdialog" : (_readinput(_readfrom), + ["--inputbox", String("text"), String("data")]), + "zenity" : (_readinput(_readfrom), + ["--entry", StringKeyword("--text", "text"), StringKeyword("--entry-text", "data")]), + "Xdialog" : (_readinput(_readfrom), + ["--stdout", "--inputbox", String("text"), Integer("height"), Integer("width"), String("data")]), + } + + def __init__(self, text, data="", width=None, height=None): + Simple.__init__(self, text, width, height) + self.data = data + +class Password(Input): + + """ + A password dialogue, consisting of a password entry field. + Options: text, width (in characters), height (in characters) + Response: the text entered into the dialogue by the user + """ + + name = "password" + info = { + "kdialog" : (_readinput(_readfrom), + ["--password", String("text")]), + "zenity" : (_readinput(_readfrom), + ["--entry", StringKeyword("--text", "text"), "--hide-text"]), + "Xdialog" : (_readinput(_readfrom), + ["--stdout", "--password", "--inputbox", String("text"), Integer("height"), Integer("width")]), + } + +class TextFile(Simple): + + """ + A text file input box. + Options: filename, text, width (in characters), height (in characters) + Response: any text returned by the dialogue program (typically an empty + string) + """ + + name = "textfile" + info = { + "kdialog" : (_readfrom, ["--textbox", String("filename"), Integer("width", pixels=1), Integer("height", pixels=1)]), + "zenity" : (_readfrom, ["--text-info", StringKeyword("--filename", "filename"), IntegerKeyword("--width", "width", pixels=1), + IntegerKeyword("--height", "height", pixels=1)] + ), + "Xdialog" : (_readfrom, ["--stdout", "--textbox", String("filename"), Integer("height"), Integer("width")]), + } + + def __init__(self, filename, text="", width=None, height=None): + Simple.__init__(self, text, width, height) + self.filename = filename + +# Available dialogues. + +available = [Question, Warning, Message, Error, Menu, CheckList, RadioList, Input, Password, Pulldown, TextFile] + +# Supported desktop environments. + +supported = Dialogue.commands.keys() + +# vim: tabstop=4 expandtab shiftwidth=4 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/desktop/windows.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/desktop/windows.py new file mode 100644 index 0000000..2b19e85 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/desktop/windows.py @@ -0,0 +1,273 @@ +#!/usr/bin/env python + +""" +Simple desktop window enumeration for Python. + +Copyright (C) 2007, 2008, 2009 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along +with this program. If not, see . + +-------- + +Finding Open Windows on the Desktop +----------------------------------- + +To obtain a list of windows, use the desktop.windows.list function as follows: + +windows = desktop.windows.list() + +To obtain the root window, typically the desktop background, use the +desktop.windows.root function as follows: + +root = desktop.windows.root() + +Each window object can be inspected through a number of methods. For example: + +name = window.name() +width, height = window.size() +x, y = window.position() +child_windows = window.children() + +See the desktop.windows.Window class for more information. +""" + +from desktop import _is_x11, _get_x11_vars, _readfrom, use_desktop +import re + +# System functions. + +def _xwininfo(identifier, action): + if identifier is None: + args = "-root" + else: + args = "-id " + identifier + + s = _readfrom(_get_x11_vars() + "xwininfo %s -%s" % (args, action), shell=1) + + # Return a mapping of keys to values for the "stats" action. + + if action == "stats": + d = {} + for line in s.split("\n"): + fields = line.split(":") + if len(fields) < 2: + continue + key, value = fields[0].strip(), ":".join(fields[1:]).strip() + d[key] = value + + return d + + # Otherwise, return the raw output. + + else: + return s + +def _get_int_properties(d, properties): + results = [] + for property in properties: + results.append(int(d[property])) + return results + +# Finder functions. + +def find_all(name): + return 1 + +def find_named(name): + return name is not None + +def find_by_name(name): + return lambda n, t=name: n == t + +# Window classes. +# NOTE: X11 is the only supported desktop so far. + +class Window: + + "A window on the desktop." + + _name_pattern = re.compile(r':\s+\(.*?\)\s+[-0-9x+]+\s+[-0-9+]+$') + _absent_names = "(has no name)", "(the root window) (has no name)" + + def __init__(self, identifier): + + "Initialise the window with the given 'identifier'." + + self.identifier = identifier + + # Finder methods (from above). + + self.find_all = find_all + self.find_named = find_named + self.find_by_name = find_by_name + + def __repr__(self): + return "Window(%r)" % self.identifier + + # Methods which deal with the underlying commands. + + def _get_handle_and_name(self, text): + fields = text.strip().split(" ") + handle = fields[0] + + # Get the "" part, stripping off the quotes. + + name = " ".join(fields[1:]) + if len(name) > 1 and name[0] == '"' and name[-1] == '"': + name = name[1:-1] + + if name in self._absent_names: + return handle, None + else: + return handle, name + + def _get_this_handle_and_name(self, line): + fields = line.split(":") + return self._get_handle_and_name(":".join(fields[1:])) + + def _get_descendant_handle_and_name(self, line): + match = self._name_pattern.search(line) + if match: + return self._get_handle_and_name(line[:match.start()].strip()) + else: + raise OSError, "Window information from %r did not contain window details." % line + + def _descendants(self, s, fn): + handles = [] + adding = 0 + for line in s.split("\n"): + if line.endswith("child:") or line.endswith("children:"): + if not adding: + adding = 1 + elif adding and line: + handle, name = self._get_descendant_handle_and_name(line) + if fn(name): + handles.append(handle) + return [Window(handle) for handle in handles] + + # Public methods. + + def children(self, all=0): + + """ + Return a list of windows which are children of this window. If the + optional 'all' parameter is set to a true value, all such windows will + be returned regardless of whether they have any name information. + """ + + s = _xwininfo(self.identifier, "children") + return self._descendants(s, all and self.find_all or self.find_named) + + def descendants(self, all=0): + + """ + Return a list of windows which are descendants of this window. If the + optional 'all' parameter is set to a true value, all such windows will + be returned regardless of whether they have any name information. + """ + + s = _xwininfo(self.identifier, "tree") + return self._descendants(s, all and self.find_all or self.find_named) + + def find(self, callable): + + """ + Return windows using the given 'callable' (returning a true or a false + value when invoked with a window name) for descendants of this window. + """ + + s = _xwininfo(self.identifier, "tree") + return self._descendants(s, callable) + + def name(self): + + "Return the name of the window." + + d = _xwininfo(self.identifier, "stats") + + # Format is 'xwininfo: Window id: "" + + return self._get_this_handle_and_name(d["xwininfo"])[1] + + def size(self): + + "Return a tuple containing the width and height of this window." + + d = _xwininfo(self.identifier, "stats") + return _get_int_properties(d, ["Width", "Height"]) + + def position(self): + + "Return a tuple containing the upper left co-ordinates of this window." + + d = _xwininfo(self.identifier, "stats") + return _get_int_properties(d, ["Absolute upper-left X", "Absolute upper-left Y"]) + + def displayed(self): + + """ + Return whether the window is displayed in some way (but not necessarily + visible on the current screen). + """ + + d = _xwininfo(self.identifier, "stats") + return d["Map State"] != "IsUnviewable" + + def visible(self): + + "Return whether the window is displayed and visible." + + d = _xwininfo(self.identifier, "stats") + return d["Map State"] == "IsViewable" + +def list(desktop=None): + + """ + Return a list of windows for the current desktop. If the optional 'desktop' + parameter is specified then attempt to use that particular desktop + environment's mechanisms to look for windows. + """ + + root_window = root(desktop) + window_list = [window for window in root_window.descendants() if window.displayed()] + window_list.insert(0, root_window) + return window_list + +def root(desktop=None): + + """ + Return the root window for the current desktop. If the optional 'desktop' + parameter is specified then attempt to use that particular desktop + environment's mechanisms to look for windows. + """ + + # NOTE: The desktop parameter is currently ignored and X11 is tested for + # NOTE: directly. + + if _is_x11(): + return Window(None) + else: + raise OSError, "Desktop '%s' not supported" % use_desktop(desktop) + +def find(callable, desktop=None): + + """ + Find and return windows using the given 'callable' for the current desktop. + If the optional 'desktop' parameter is specified then attempt to use that + particular desktop environment's mechanisms to look for windows. + """ + + return root(desktop).find(callable) + +# vim: tabstop=4 expandtab shiftwidth=4 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/rgba/__init__.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/rgba/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/rgba/rgba.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/rgba/rgba.py new file mode 100644 index 0000000..b21f885 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/ExportHtmlLib/rgba/rgba.py @@ -0,0 +1,173 @@ +''' +RGBA +Licensed under MIT +Copyright (c) 2012 Isaac Muse +''' + +import re +from colorsys import rgb_to_hls, hls_to_rgb, rgb_to_hsv, hsv_to_rgb + +RGB_CHANNEL_SCALE = 1.0 / 255.0 +HUE_SCALE = 1.0 / 360.0 + + +def clamp(value, mn, mx): + return max(min(value, mx), mn) + + +class RGBA(object): + r = None + g = None + b = None + a = None + color_pattern = re.compile(r"^#(?:([A-Fa-f\d]{6})([A-Fa-f\d]{2})?|([A-Fa-f\d]{3}))") + + def __init__(self, s=None): + if s is None: + s = "#000000FF" + self.r, self.g, self.b, self.a = self._split_channels(s) + + def _split_channels(self, s): + def alpha_channel(alpha): + return int(alpha, 16) if alpha else 0xFF + + m = self.color_pattern.match(s) + assert(m is not None) + if m.group(1): + return int(s[1:3], 16), int(s[3:5], 16), int(s[5:7], 16), alpha_channel(m.group(2)) + else: + return int(s[1] * 2, 16), int(s[2] * 2, 16), int(s[3] * 2, 16), 0xFF + + def get_rgba(self): + return "#%02X%02X%02X%02X" % (self.r, self.g, self.b, self.a) + + def get_rgb(self): + return "#%02X%02X%02X" % (self.r, self.g, self.b) + + def apply_alpha(self, background="#000000AA"): + def tx_alpha(cf, af, cb, ab): + return int(abs(cf * (af / 255.0) + cb * (ab / 255.0) * (1 - (af / 255.0)))) & 0xFF + + if self.a < 0xFF: + r, g, b, a = self._split_channels(background) + + self.r, self.g, self.b = (tx_alpha(self.r, self.a, r, a), tx_alpha(self.g, self.a, g, a), tx_alpha(self.b, self.a, b, a)) + + return self.get_rgb() + + def luminance(self): + return clamp(int(round(0.299 * self.r + 0.587 * self.g + 0.114 * self.b)), 0, 255) + + def tohsv(self): + return rgb_to_hsv(self.r * RGB_CHANNEL_SCALE, self.g * RGB_CHANNEL_SCALE, self.b * RGB_CHANNEL_SCALE) + + def fromhsv(self, h, s, v): + r, g, b = hsv_to_rgb(h, s, v) + self.r = int(round(r * 255)) & 0xFF + self.g = int(round(g * 255)) & 0xFF + self.b = int(round(b * 255)) & 0xFF + + def tohls(self): + return rgb_to_hls(self.r * RGB_CHANNEL_SCALE, self.g * RGB_CHANNEL_SCALE, self.b * RGB_CHANNEL_SCALE) + + def fromhls(self, h, l, s): + r, g, b = hls_to_rgb(h, l, s) + self.r = int(round(r * 255)) & 0xFF + self.g = int(round(g * 255)) & 0xFF + self.b = int(round(b * 255)) & 0xFF + + def colorize(self, deg): + h, l, s = self.tohls() + h = clamp(deg * HUE_SCALE, 0.0, 1.0) + self.fromhls(h, l, s) + + def hue(self, deg): + d = deg * HUE_SCALE + h, l, s = self.tohls() + h = h + d + while h > 1.0: + h -= 1.0 + while h < 0.0: + h += 1.0 + self.fromhls(h, l, s) + + def invert(self): + self.r ^= 0xFF + self.g ^= 0xFF + self.b ^= 0xFF + + def saturation(self, factor): + h, l, s = self.tohls() + s = clamp(s * factor, 0.0, 1.0) + self.fromhls(h, l, s) + + def grayscale(self): + luminance = self.luminance() & 0xFF + self.r = luminance + self.g = luminance + self.b = luminance + + def sepia(self): + r = clamp(int((self.r * .393) + (self.g * .769) + (self.b * .189)), 0, 255) & 0xFF + g = clamp(int((self.r * .349) + (self.g * .686) + (self.b * .168)), 0, 255) & 0xFF + b = clamp(int((self.r * .272) + (self.g * .534) + (self.b * .131)), 0, 255) & 0xFF + self.r, self.g, self.b = r, g, b + + def brightness(self, factor): + # Caculate brightness based on RGB luminance. + # Maybe HLS or HSV brightness adjustment is better? + def get_overage(c): + if c < 0.0: + o = 0.0 + c + c = 0.0 + elif c > 255.0: + o = c - 255.0 + c = 255.0 + else: + o = 0.0 + return o, c + + def distribute_overage(c, o, s): + channels = len(s) + if channels == 0: + return c + parts = o / len(s) + if "r" in s and "g" in s: + c = c[0] + parts, c[1] + parts, c[2] + elif "r" in s and "b" in s: + c = c[0] + parts, c[1], c[2] + parts + elif "g" in s and "b" in s: + c = c[0], c[1] + parts, c[2] + parts + elif "r" in s: + c = c[0] + parts, c[1], c[2] + elif "g" in s: + c = c[0], c[1] + parts, c[2] + else: # "b" in s: + c = c[0], c[1], c[2] + parts + return c + + channels = ["r", "g", "b"] + total_lumes = clamp(self.luminance() + (255.0 * factor) - 255.0, 0.0, 255.0) + + if total_lumes == 255: + # white + self.r, self.g, self.b = 0xFF, 0xFF, 0xFF + elif total_lumes == 0: + # black + self.r, self.g, self.b = 0x00, 0x00, 0x00 + else: + # Adjust Brightness + pts = (total_lumes - 0.299 * self.r - 0.587 * self.g - 0.114 * self.b) + slots = set(channels) + components = [float(self.r) + pts, float(self.g) + pts, float(self.b) + pts] + count = 0 + for c in channels: + overage, components[count] = get_overage(components[count]) + if overage: + slots.remove(c) + components = list(distribute_overage(components, overage, slots)) + count += 1 + + self.r = int(round(components[0])) & 0xFF + self.g = int(round(components[1])) & 0xFF + self.b = int(round(components[2])) & 0xFF diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/HtmlAnnotations.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/HtmlAnnotations.py new file mode 100644 index 0000000..133dc88 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/HtmlAnnotations.py @@ -0,0 +1,262 @@ +import sublime +import sublime_plugin + +PACKAGE_SETTINGS = "ExportHtml.sublime-settings" + + +def get_highlight_style(): + style_flag = 0 + settings = sublime.load_settings(PACKAGE_SETTINGS) + scope = settings.get("annotation_highlight_scope", "comment") + style = settings.get("annotation_highlight_style", "outline") + if style == "outline": + style_flag |= sublime.DRAW_OUTLINED + return scope, style_flag + + +def clean_invalid_regions(view, annotations): + deletions = 0 + for x in range(0, int(annotations["count"])): + key_name = "html_annotation_%d" % x + regions = view.get_regions(key_name) + if len(regions) and not regions[0].empty(): + annotations["annotations"]["html_annotation_%d" % x]["region"] = [regions[0].begin(), regions[0].end()] + if deletions: + new_key = "html_annotation_%d" % (x - deletions) + annotations["annotations"][new_key] = annotations["annotations"][key_name] + del annotations["annotations"][key_name] + new_region = annotations["annotations"][new_key]["region"] + view.erase_regions(key_name) + scope, style = get_highlight_style() + view.add_regions( + new_key, + [sublime.Region(new_region[0], new_region[1])], + scope, + "", + style + ) + else: + del annotations["annotations"]["html_annotation_%d" % x] + annotations["count"] -= 1 + deletions += 1 + if len(regions): + view.erase_regions(key_name) + + view.settings().set("annotation_comments", annotations) + + +def get_annotations(view): + annotations = view.settings().get("annotation_comments", {"count": 0, "annotations": {}}) + clean_invalid_regions(view, annotations) + return annotations + + +def clear_annotations(view): + annotations = view.settings().get("annotation_comments", {"count": 0, "annotations": {}}) + for x in range(0, int(annotations["count"])): + view.erase_regions("html_annotation_%d" % x) + view.settings().set("annotation_comments", {"count": 0, "annotations": {}}) + + +def delete_annotations(view): + annotations = view.settings().get("annotation_comments", {"count": 0, "annotations": {}}) + for sel in view.sel(): + for x in range(0, int(annotations["count"])): + region = annotations["annotations"]["html_annotation_%d" % x]["region"] + annotation = sublime.Region(int(region[0]), int(region[1])) + if annotation.contains(sel): + view.erase_regions("html_annotation_%d" % x) + break + clean_invalid_regions(view, annotations) + + +def get_annotation_comment(view): + comment = None + annotations = view.settings().get("annotation_comments", {"count": 0, "annotations": {}}) + if len(view.sel()): + sel = view.sel()[0] + for x in range(0, int(annotations["count"])): + region = annotations["annotations"]["html_annotation_%d" % x]["region"] + annotation = sublime.Region(int(region[0]), int(region[1])) + if annotation.contains(sel): + comment = annotations["annotations"]["html_annotation_%d" % x]["comment"] + return comment + + +def is_selection_in_annotation(view, first_only=False): + mode = view.settings().get("annotation_mode", False) + selection = False + if mode: + annotations = view.settings().get("annotation_comments", {"count": 0, "annotations": {}}) + for sel in view.sel(): + for x in range(0, int(annotations["count"])): + region = annotations["annotations"]["html_annotation_%d" % x]["region"] + annotation = sublime.Region(int(region[0]), int(region[1])) + if annotation.contains(sel): + selection = True + break + if first_only: + break + return mode and selection + + +def annotations_exist(view): + mode = view.settings().get("annotation_mode", False) + found = False + if mode: + annotations = view.settings().get("annotation_comments", {"count": 0, "annotations": {}}) + if int(annotations["count"]): + found = True + return mode and found + + +def is_selected(view): + mode = view.settings().get("annotation_mode", False) + selected = not view.sel()[0].empty() + return mode and selected + + +class ShowAnnotationCommentCommand(sublime_plugin.TextCommand): + def is_visible(self): + return is_selection_in_annotation(self.view) + + def run(self, edit): + comment = get_annotation_comment(self.view) + if comment != None: + sublime.message_dialog("Annotation Comment:\n\n%s" % comment) + sublime.set_clipboard(comment) + + +class ClearAnnotationsCommand(sublime_plugin.TextCommand): + def is_visible(self): + return annotations_exist(self.view) + + def run(self, edit): + clear_annotations(self.view) + + +class DeleteAnnotationsCommand(sublime_plugin.TextCommand): + def is_visible(self): + return is_selection_in_annotation(self.view) + + def run(self, edit): + delete_annotations(self.view) + + +class EnableAnnotationModeCommand(sublime_plugin.TextCommand): + def is_visible(self): + return not self.view.settings().get("annotation_mode", False) + + def run(self, edit): + self.view.run_command("toggle_annotation_html_mode") + + +class DisableAnnotationModeCommand(sublime_plugin.TextCommand): + def is_visible(self): + return self.view.settings().get("annotation_mode", False) + + def run(self, edit): + self.view.run_command("toggle_annotation_html_mode") + + +class ToggleAnnotationHtmlModeCommand(sublime_plugin.TextCommand): + def is_enabled(self): + return not self.view.settings().get('is_widget') + + def run(self, edit): + mode = False if self.view.settings().get("annotation_mode", False) else True + self.view.settings().set("annotation_mode", mode) + if mode: + self.view.settings().set("annotation_read_mode", self.view.is_read_only()) + self.view.set_read_only(True) + self.view.set_status("html_annotation_mode", "Annotation Mode: ON") + else: + clear_annotations(self.view) + self.view.set_read_only(self.view.settings().get("annotation_read_mode", False)) + self.view.erase_status("html_annotation_mode") + + +class AddAnnotationCommand(sublime_plugin.TextCommand): + def is_visible(self): + return is_selected(self.view) + + def run(self, edit): + AnnotateHtml(self.view).run() + + +class EditAnnotationCommand(sublime_plugin.TextCommand): + def is_visible(self): + return is_selection_in_annotation(self.view, first_only=True) + + def run(self, edit): + AnnotateHtml(self.view).run() + + +class AnnotateHtml(object): + def __init__(self, view): + self.view = view + + def subset_annotation_adjust(self): + subset = None + comment = "" + parent = None + intersect = False + for k, v in self.annotations["annotations"].items(): + region = sublime.Region(int(v["region"][0]), int(v["region"][1])) + if region.contains(self.sel): + subset = region + comment = v["comment"] + parent = k + break + elif region.intersects(self.sel): + intersect = True + break + if subset != None: + self.sel = subset + return comment, parent, intersect + + def add_annotation(self, s, view_id, subset): + window = sublime.active_window() + view = window.active_view() if window != None else None + if s != "" and view != None and view_id == view.id(): + if subset == None: + idx = self.annotations["count"] + key_name = ("html_annotation_%d" % idx) + else: + key_name = subset + + self.annotations["annotations"][key_name] = { + "region": [self.sel.begin(), self.sel.end()], + "comment": s + } + if subset == None: + self.annotations["count"] += 1 + self.view.settings().set("annotation_comments", self.annotations) + + scope, style = get_highlight_style() + self.view.add_regions( + key_name, + [self.sel], + scope, + "", + style + ) + + def annotation_panel(self, default_comment, subset): + view_id = self.view.id() + self.view.window().show_input_panel( + ("Annotate region (%d, %d)" % (self.sel.begin(), self.sel.end())), + default_comment, + lambda x: self.add_annotation(x, view_id=view_id, subset=subset), + None, + None + ) + + def run(self): + self.sel = self.view.sel()[0] + self.annotations = get_annotations(self.view) + comment, subset, intersects = self.subset_annotation_adjust() + if not intersects: + self.annotation_panel(comment, subset) + else: + sublime.error_message("Cannot have intersecting annotation regions!") diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/Main.sublime-menu b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/Main.sublime-menu new file mode 100644 index 0000000..548aa4a --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/Main.sublime-menu @@ -0,0 +1,40 @@ +[ + { + "caption": "Preferences", + "mnemonic": "n", + "id": "preferences", + "children": + [ + { + "caption": "Package Settings", + "mnemonic": "P", + "id": "package-settings", + "children": + [ + { + "caption": "ExportHTML", + "children": + [ + { + "command": "open_file", + "args": {"file": "${packages}/ExportHtml/readme.md"}, + "caption": "README" + }, + { "caption": "-" }, + { + "command": "open_file", + "args": {"file": "${packages}/ExportHtml/ExportHtml.sublime-settings"}, + "caption": "Settings – Default" + }, + { + "command": "open_file", + "args": {"file": "${packages}/User/ExportHtml.sublime-settings"}, + "caption": "Settings – User" + } + ] + } + ] + } + ] + } +] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/css/export.css b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/css/export.css new file mode 100644 index 0000000..fcefd28 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/css/export.css @@ -0,0 +1,153 @@ +/* General */ +* html a:hover { background: transparent; } +body { color: /* %body_fg% */; } +pre { border: 0; margin: 0; padding: 0; } +td { padding: 0; } +table { border: 0; margin: 0; padding: 0; border-collapse: collapse; empty-cells: show; } +.code_text { font: /* %font_size% */pt /* %font_face% */, "Courier", Monospace; } +.code_page { background-color: /* %page_bg% */; } +.simple_code_page { background-color: white; color: black } +.code_gutter { display: /* %display_mode% */; background-color: /* %gutter_bg% */; padding-right: 10px; } +td.code_line div { width: 100%; } +span { display: inline-block; border: 0; margin: 0; } +span.bold { font-weight: bold; } +span.italic { font-style: italic; } +span.normal { font-style: normal; } +span.underline { text-decoration:underline; } + +/* Wrapping */ +div.wrap { + white-space: -moz-pre-wrap; /* Mozilla */ + white-space: -hp-pre-wrap; /* HP printers */ + white-space: -o-pre-wrap; /* Opera 7 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: pre-wrap; /* CSS 2.1 */ + white-space: pre-line; /* CSS 3 (and 2.1 as well, actually) */ + word-wrap: break-word; /* IE */ +} +div.wrap span { display: inline; } + +/* Toolbar */ +div#toolbarhide { + position: fixed; + top: 0px; + right: 0px; + padding-top: 5px; + padding-right: 10px; +} +div#toolbar { + visibility: hidden; + text-align: center; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + border-radius: 5px; + opacity: 0; + -webkit-transform: translateZ(0); /* webkit flicker fix */ + -webkit-font-smoothing: antialiased; /* webkit text rendering fix */ + -webkit-transition: all .25s ease-in; + -moz-transition: all .25s ease-in; + -ms-transition: all .25s ease-in; + -o-transition: all .25s ease-in; + transition: all .25s ease-in; + background-color:black; + color: white; +} +div#toolbarhide:hover div#toolbar { + visibility: visible; + opacity: .8; + -webkit-transition: all .25s ease-out; + -moz-transition: all .25s ease-out; + -ms-transition: all .25s ease-out; + -o-transition: all .25s ease-out; + transition: all .25s ease-out; +} + +div#toolbar img { + display: /* %toolbar_orientation% */ ; + vertical-align: middle; + border: 0; + cursor: pointer; + height: 16px; + width: 16px; + padding: 5px; +} + +/* tooltips */ +#tooltip { + border-radius: 5px 5px; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.1); + -webkit-box-shadow: 5px 5px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 5px 5px rgba(0, 0, 0, 0.1); + white-space: -moz-pre-wrap; /* Mozilla */ + white-space: -hp-pre-wrap; /* HP printers */ + white-space: -o-pre-wrap; /* Opera 7 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: pre-wrap; /* CSS 2.1 */ + white-space: pre-line; /* CSS 3 (and 2.1 as well, actually) */ + word-wrap: break-word; /* IE */ + position:absolute; display:block; + color: black; + padding: 0.8em 1em; + background: #FFFFE0; border: solid black; + font-family: Calibri, Tahoma, Geneva, sans-serif; + font-size: 10pt; + font-weight: bold; +} + +a.annotation { + color: /* %body_fg% */; + outline: none; + text-decoration: underline; +} + +span.annotation{ display: inline; } + +.tooltip_hotspot { cursor:pointer; } + +/* Annotation Table */ +div#comment_list { + visibility: hidden; + display: none; + margin: auto auto; + text-align: center; + position: fixed; + z-index: 99; + left:0px; + top: 0px; +} +div#comment_wrapper { + max-height: 200px; + overflow: auto; + border: solid black; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.1); + -webkit-box-shadow: 5px 5px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 5px 5px rgba(0, 0, 0, 0.1); +} +table#comment_table { + border: thin solid; border-collapse: collapse; + color: #000000; background-color: #FFFFE0; margin: auto auto; +} +div.table_footer { text-align:right; font-size: 8pt; font-weight: bold;} +a.table_close { color: black; float: right; } +a.table_close:link { text-decoration: none; } +a.table_close:active { text-decoration: none; } +a.table_close:visited { text-decoration: none; } +a.table_close:hover { text-decoration: none; } +table#comment_table th, table#comment_table td { border: thin solid; padding: 5px; } +td.annotation_link { width: 60px; text-align: right; padding-right: 20px; } +.annotation_comment { width: 500px; } +div.annotation_comment { + float: left; + text-align: left; + white-space: -moz-pre-wrap; /* Mozilla */ + white-space: -hp-pre-wrap; /* HP printers */ + white-space: -o-pre-wrap; /* Opera 7 */ + white-space: -pre-wrap; /* Opera 4-6 */ + white-space: pre-wrap; /* CSS 2.1 */ + white-space: pre-line; /* CSS 3 (and 2.1 as well, actually) */ + word-wrap: break-word; /* IE */ +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/icons/bubble.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/icons/bubble.png new file mode 100644 index 0000000000000000000000000000000000000000..ffbe903deada3e14e434b5aab6f2597fcc44d234 GIT binary patch literal 320 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE>1I5*%68Z-ksw?yMMPv)QnWbHx`A@BWmdh=U z)A|gPLqyX&UY+e(3fpB%;JQKQ}iuuLQ_tVA!*6(@Y>W z+tbA{#Nzbb$rpJKD+oBxU(F+6-4COqN%GrlK#b~5ceo-ylc gv&)<-`#!ERU*vW5@QQ^RPeIQ0boFyt=akR{0Mxl{RsaA1 literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/icons/gutter.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/icons/gutter.png new file mode 100644 index 0000000000000000000000000000000000000000..d28b14250c1cb7cae7a0d986333f8cd37afc3d6a GIT binary patch literal 345 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE=P4L=#V4lkM4O>2NY-&xI?$EcfeZtzHrJjNtM6Nkl zb>E$OHu?E%k;+C_of;PD`WZ;Pja(;X~$`Qz?03qQDd_1~rm8`$LI?XD8m%8(o2~TWS9pgh>!;|caSJ(qtZ0&nez4IdM%iQ2TA>0-E5}KmUGMZu zc!H(2Ft~UIPFnhGYIUBqnxXtE*$WXh{fr<6Ak^E90gj%*JM5sD&W?f(&EO)w02uHhh>wDU7u{*%=jVc!qg6*GjDbZ&ncFZ{_t*2@x9-28(2?R_V3Dx zmu3Ghtnr~hsLznEVaa|D55r>zSH7BN_9J9xPV#i!RU7_(J$q<&*sPf|f3E(%FMRd8 oV{G5Ho^$4?eUQMt?(YAK=9Zn`FK#cKR}S*0r>mdKI;Vst0CnAp?EnA( literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/icons/toolbar.xcf b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/icons/toolbar.xcf new file mode 100644 index 0000000000000000000000000000000000000000..74484473a2d097819ad54cd9f208cbdfa6bda7ce GIT binary patch literal 4284 zcmeHKO>7%Q6rS02Y{&WCH7yq;)^!o-At-4PTxlghYLVc~0hFYUF(givq_n+2gb?L` z09A0~3Wo{~2srWA6C6k@aj05h*G>gUsLC3YG>H@2^Z35?ZtJM2#DPi?;zhIXXWq=t zzV~I`bG|Tr!aF@N=}i`FHc?9=hTrhZ+oK3e#ix+;D!jaGW+wXf-r9e`YG@&CN^|jvq)3P39+NredGGE5m!6(GF*Z>s=DpGAT`->6$r##(PZkUF zUZFVgQf`jxXC^1-a`U$i$klh6+LrB08HbScR|symQt@|`>K<3B=NJO|rBVZzBM zO$=)HGh=6RvtITP=Pf=!aSWzNFp{AhIEONrdZ0s2b;vy(a&L#+*C7u;=DranAn<*p z2p%JCl~1b}%F)5$YrV1EW}TyMtAuo^9rSO%syJqsYB^Au`5DOduWnZ@yMsGok;6VSUt@8WO!?f|{*&^cnmiOJIxrI5OPLhfdOa%aucBTnj^2y?S2XOfLt~f5 zVWDe_b<8J~4d30d!)^SaHwqe{7FwuwpJYiZXvWwx{d3s{?i?5Ruw=ECb=g5--w203 z8#F>JF6)38xVg}SZQ0$l?!qjuW*-=c=E15#2z5ATkhhgL7jz&-LLI$~j;-*igl zk}hqPZ(Mp%1HG$md4Cpv*G9jGZb2{YTP!W^EAJn@oYljZ~B?+nH z5?ihTX$jtE0A0#&l;P{9f=}pYM5B-sAU#LMCSJ+U&YUbB=hSXMN%Sj~Cw-fT0!2p# Uzz08SnUnfnm?OO*4Vi zUr!gu5R21uCp+dHa^P{Dzc@#6W(>nqtuK3I7VdRiFK}g@x5qBGUpfUr>0Su2^ZQCT^;Wa5?I`=(y`k#+aF^|HxR(q8ddcv}6kvs~TbSyx_?qkS^ztNrt?)T36O zD?j{GJ9sSSY3rlyyD#>OCb06K(GKg65MB3Wjw$nXyP0#VNmbozAFAK5k0l1O maxw){ + tt.style.width = maxw + 'px'; + } + h = parseInt(tt.offsetHeight, 10) + top; + clearInterval(tt.timer); + tooltip.instantshow(true); + // tt.timer = setInterval(function(){tooltip.fade(1);}, timer); + }, + pos:function(e) { + var u = ie ? event.clientY + document.documentElement.scrollTop : e.pageY, + l = ie ? event.clientX + document.documentElement.scrollLeft : e.pageX; + tt.style.top = (u - h) + 'px'; + tt.style.left = (l + left) + 'px'; + }, + instantshow: function(show) { + if (show === true) { + tt.style.opacity = endalpha * 0.01; + tt.style.filter = 'alpha(opacity=' + endalpha + ')'; + } else { + tt.style.display = 'none'; + } + }, + fade:function(d) { + var a = alpha, i; + if((a != endalpha && d == 1) || (a !== 0 && d == -1)){ + i = speed; + if(endalpha - a < speed && d == 1){ + i = endalpha - a; + }else if(alpha < speed && d == -1){ + i = a; + } + alpha = a + (i * d); + tt.style.opacity = alpha * 0.01; + tt.style.filter = 'alpha(opacity=' + alpha + ')'; + }else{ + clearInterval(tt.timer); + if(d == -1){ + tt.style.display = 'none'; + } + } + }, + hide:function() { + clearInterval(tt.timer); + tooltip.instantshow(false); + // tt.timer = setInterval(function(){tooltip.fade(-1);},timer); + } + }; +}(); + +tooltip.init(); diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/js/lines.js b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/js/lines.js new file mode 100644 index 0000000..20e9d49 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/js/lines.js @@ -0,0 +1,87 @@ +/*jshint globalstrict: true*/ +"use strict"; + +var page_line_info = { + wrap: false, + ranges: null, + wrap_size: null, + tables: null, + header: null, + gutter: false + }; + +function wrap_code() { + var start, end, i, j, mode, idx, + width = 0, el; + if (page_line_info.header) { + document.getElementById("file_info").style.width = page_line_info.wrap_size + "px"; + } + for (i = 1; i <= page_line_info.tables; i++) { + idx = i - 1; + start = page_line_info.ranges[idx][0]; + end = page_line_info.ranges[idx][1]; + for(j = start; j < end; j++) { + if (mode == null) { + mode = true; + if (page_line_info.gutter) { + width = document.getElementById("L_" + idx + "_" + j).offsetWidth; + } + } + el = document.getElementById("C_" + idx + "_" + j); + el.style.width = (page_line_info.wrap_size - width) + "px"; + el.className = "wrap"; + } + } +} + +function toggle_gutter() { + var i, j, mode, rows, r, tbls, cells; + tbls = document.getElementsByTagName('table'); + for (i = 1; i <= page_line_info.tables; i++) { + rows = tbls[i].getElementsByTagName('tr'); + r = rows.length; + for (j = 0; j < r; j++) { + cells = rows[j].getElementsByTagName('td'); + if (mode == null) { + if (page_line_info.gutter) { + mode = 'none'; + page_line_info.gutter = false; + } else { + mode = 'table-cell'; + page_line_info.gutter = true; + } + } + cells[0].style.display = mode; + } + } + if (page_line_info.wrap && mode != null) { + setTimeout(function() {wrap_code();}, 500); + } +} + +function unwrap_code() { + var i, j, idx, start, end, el; + if (page_line_info.header) { + document.getElementById("file_info").style.width = "100%"; + } + for (i = 1; i <= page_line_info.tables; i++) { + idx = i - 1; + start = page_line_info.ranges[idx][0]; + end = page_line_info.ranges[idx][1]; + for(j = start; j < end; j++) { + el = document.getElementById("C_" + idx + "_" + j); + el.style.width = "100%"; + el.className = ""; + } + } +} + +function toggle_wrapping() { + if (page_line_info.wrap) { + page_line_info.wrap = false; + unwrap_code(); + } else { + page_line_info.wrap = true; + wrap_code(); + } +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/js/plaintext.js b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/js/plaintext.js new file mode 100644 index 0000000..183117d --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/js/plaintext.js @@ -0,0 +1,39 @@ +/*jshint globalstrict: true*/ +"use strict"; + +var plain_text_clone = null; + +function toggle_plain_text() { + var lines = document.querySelectorAll("td.code_line"), + line_len = lines.length, + text = "", + plain_pre = document.querySelectorAll("pre.simple_code_page"), + orig_pre, pre, i, j, spans, span_len, span; + if (plain_pre.length > 0) { + document.body.removeChild(plain_pre[0]); + document.body.appendChild(plain_text_clone); + document.body.className = "code_page"; + } else { + for (i = 0; i < line_len; i++) { + spans = lines[i].querySelectorAll("span.real_text"); + span_len = spans.length; + for (j = 0; j < span_len; j++) { + span = spans[j]; + if ("textContent" in span) { + text += span.textContent; + } else { + text += span.innerText; + } + } + text += "\n"; + } + orig_pre = document.querySelectorAll("pre.code_page")[0]; + plain_text_clone = orig_pre.cloneNode(true); + pre = document.createElement('pre'); + pre.className = "simple_code_page"; + pre.appendChild(document.createTextNode(text)); + document.body.removeChild(orig_pre); + document.body.appendChild(pre); + document.body.className = "simple_code_page"; + } +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/js/plist.js b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/js/plist.js new file mode 100644 index 0000000..c61be48 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/js/plist.js @@ -0,0 +1,129 @@ +/*jshint globalstrict: true*/ +"use strict"; + +var plist = { + color_scheme: {}, + content: "", + indentlevel: 0, + + get: function(file_name) { + this.content = '\n' + + '\n' + + '\n' + + '\n'; + this.parsedict(this.color_scheme); + this.content += '\n'; + return this.content; + }, + + isinstance: function(obj, s) { + return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase() === s; + }, + + sortkeys: function(obj) { + var sorted = {}, + keys = [], + key; + + for (key in obj) { + if (obj.hasOwnProperty(key)) { + keys.push(key); + } + } + keys.sort(); + return keys; + }, + + indent: function() { + var i; + for (i = 0; i < this.indentlevel; i++) { + this.content += " "; + } + }, + + parsekey: function(k) { + this.indent(); + this.content += '' + k + '\n'; + }, + + parseitem: function(obj) { + if (this.isinstance(obj, "string")) { + this.parsestring(obj); + } else if (this.isinstance(obj, "array")) { + this.parsearray(obj); + } else if (this.isinstance(obj, "object")) { + this.parsedict(obj); + } + }, + + parsearray: function(obj) { + var i, len = obj.length; + this.indent(); + this.content += '\n'; + this.indentlevel++; + for (i = 0; i < len; i++) { + this.parseitem(obj[i]); + } + this.indentlevel--; + this.indent(); + this.content += '\n'; + }, + + parsestring: function(s) { + this.indent(); + this.content += '' + s + '\n'; + }, + + parsedict: function(obj) { + var keys = this.sortkeys(obj), + len = keys.length, + k, i; + + this.indent(); + this.content += '\n'; + this.indentlevel++; + for (i = 0; i < len; i++) + { + k = keys[i]; + this.parsekey(k); + this.parseitem(obj[k]); + } + this.indentlevel--; + this.indent(); + this.content += '\n'; + } +}, + +escape_html = { + safe_chars: { + "&": "&", + "<": "<", + ">": ">", + '"': '"', + "'": ''', + "/": '/' + }, + escape: function (s) { + return String(s).replace(/[&<>"'\/]/g, function (c) {return escape_html.safe_chars[c];}); + } +}; + +function extract_theme(name) { + var text, wnd, doc, + a = document.createElement('a'); + window.URL = window.URL || window.webkitURL; + + if (window.Blob != null && a.download != null) { + text = new Blob([plist.get(name)], {'type':'application/octet-stream'}); + a.href = window.URL.createObjectURL(text); + a.download = name; + a.click(); + } else { + text = '
' + escape_html.escape(plist.get(name)) + '
', + wnd = window.open('', '_blank', "status=1,toolbar=0,scrollbars=1"), + doc = wnd.document; + doc.write(text); + doc.close(); + wnd.focus(); + } +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/js/print.js b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/js/print.js new file mode 100644 index 0000000..cf5594d --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/js/print.js @@ -0,0 +1,15 @@ +/*jshint globalstrict: true*/ +"use strict"; + +function page_print() { + var element = document.getElementById("toolbarhide"); + if (element != null) { + element.style.display = "none"; + } + if (window.print) { + window.print(); + } + if (element != null) { + element.style.display = "block"; + } +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/package-metadata.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/package-metadata.json new file mode 100644 index 0000000..9887e8e --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/package-metadata.json @@ -0,0 +1 @@ +{"url": "https://github.com/facelessuser/ExportHtml", "version": "2013.03.29.23.39.49", "description": "Sublime Text - Export code to HTML for copying/printing/saving. Also, export code to BBCode for forum posts."} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/readme.md b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/readme.md new file mode 100644 index 0000000..c9957c1 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ExportHtml/readme.md @@ -0,0 +1,220 @@ +# About +This is a fork of agibsonsw's [PrintHtml](https://github.com/agibsonsw/PrintHtml) plugin. This plugin allows the exporting of a document in ST2 to an HTML file or to BBCode. It duplicates ST2's theme colors and font styles. You can play with the demo page that has actual html pages generated with this plugin [here](http://facelessuser.github.com/ExportHtml). + + + +# Features +- Export to HTML using any tmTheme for syntax highlighting +- Can handle any language supported by ST2 +- Supports bold and italic theme font styles as well +- Configurable output +- Format suitable for copying and pasting in emails +- 2 included tmTheme files for color and grayscale printing (but any can be used) +- Export only selections (multi-select supported) +- Export and show highlights (multi-select supported) +- Toggle gutter on/off in browser view +- Automatically open browser print dialog (optional) +- Enable/disable configurable word wrapping +- Configurable toolbar to appear in the generated webpage + +# Usage: Exporting HTML +ExportHtml comes with a number of default commands available, but these can be overridden in the settings file. Or you can create commands directly outside of the settings file bound to the command palette, key bindings, or even the menu. + +If adding a command to the settings file, it goes under the ```html_panel``` setting. These configurations will appear under the ```Export to HTML: Show Export Menu``` command palette command. + +```javascript +// Define configurations for the drop down export menu +"html_panel": [ + // Browser print color (selections and multi-selections allowed) + { + "Browser Print - Color": { + "numbers": true, + "wrap": 900, + "browser_print": true, + "multi_select": true, + "color_scheme": "Packages/ExportHtml/ColorSchemes/Print-Color.tmTheme", + "style_gutter": false + } + } + ] +``` + +The name of the command is the key value, and then you add the parameters you wish to specify. You can use any combination of settings below. + +- numbers (boolean): Display line numbers in the gutter. +- style_gutter (boolean): Style gutter with theme backgrounds and foregrounds, or just use the default background/foreground. Default is ```true```. +- multi_select (boolean): If multiple regions are selected in a document, only export what is under those selections. By default only the first selection is recognized. Default is ```false``` +- highlight_selections (boolean): Highlights all selections in HTML output using the themes selection colors. Multi-select option will be ignored if this is set ```true```. Default is ```false``` +- wrap (integer): Define the allowable size in px to wrap lines at. By default wrapping is not used. +- color_scheme (string): The color scheme (tmTheme) file you would like to use. By default the current color scheme file is used, or the the alternate default color scheme if defined in the setting ```alternate_scheme```. +- clipboard_copy (boolean): Copy html to the clipboard after generation. Default is ```false```. +- browser_print (boolean): When opening in the web browser, also open the brower's print dialog. This will be ignored if ```view_open``` is ```true```. Default is ```false```. +- view_open (boolean): Open HTML in a Sublime Text tab instead of the web browser. Default is ```false```. +- no_header (boolean): Do not display file name, date, and time at the top of the HTML document. Default is ```false```. +- date_time_format (string): String denoting the format for date and time when displaying header. Please see Python's documentation on ```time.strftime``` for detailed info on formatting syntax. Default is ```"%m/%d/%y %I:%M:%S"``` +- show_full_path (boolean): Show full path for filename when displaying header. Default is ```true``` +- save_location (string): Path to save html file. If the file is wanted in the same file as the original, use ".". Otherwise, use the absolute path to where the file is desired. If there is an issue determining where to save the file, or the path does not exist, the OS temp folder will be used. Default is ```None``` (use temp folder). +- time_stamp (string): Configure the time stamp of saved html when using ```save_location```. To remove time stamps, just set to an empty string ```""```. Please see Python's documentation on ```time.strftime``` for detailed info on formatting syntax. Default is ```"_%m%d%y%H%M%S"``` +- toolbar (array of strings): Option to display a toolbar with to access features in a generated HTML. This setting is an array of keywords that represent the icons in the toolbar to show. Valid keywords include ```gutter```, ```print```, ```plain_text```, ```annotation```, ```theme```, and ```wrapping```. Toolbar will appear when you mouse over the uppert right corner of the window of the generated html. Default enables all. +- filter (string): Filters to use on the theme's colors. The string is a sequence of filters separated by ```;```. The accepted filters are ```grayscale```, ```invert```, ```sepia```, ```brightness```,, ```saturation```, ```hue```, and ```colorize```. ```brightness``` and ```saturation``` requires a float parameter to specify to what magnitude the filter should be applied at. ```hue``` and ```colorize``` take a float that represents a degree. ```hue``` shifts the hue via the degree given (can accept negative degrees); hues will wrap if they extend past 0 degrees or 360 degrees. Example: ```"filter": "sepia;invert;brightness(1.1);saturation(1.3);"```. Default is ```""```. +- shift_brightness (bool): This setting shifts the entire theme's brightness if a background color's luminace is below the global setting ```bg_min_lumen_threshold```. This was added to solve an issue that I had when copying dark themes into an outlook email; if a html span had a background that was too dark, the foreground would just be white. This allows me to not have to worry about how dark the theme is, and probably serves very little use besides that. + +If you wish to bind a command to a key combination etc., the same settings as above can be used. + +Example: + +```javascript +{ + "keys": ["ctrl+alt+n"], + "command": "export_html", + "args": { + "numbers": true, + "wrap": 900, + "browser_print": true, + "multi_select": true, + "color_scheme": "Packages/ExportHtml/ColorSchemes/Print-Color.tmTheme", + "style_gutter": false + } +} +``` + +When viewing the HTML in your web browser, regardless of the gutter settings, the gutter can be toggled to show or be hidden using the toolbar. + +# Usage: Exporting BBCode +ExportHtml can also export selected code as BBCode for posting in forums. Exporting BBCode is very similar to exporting HTML code. + +If adding a command to the settings file, it goes under the ```bbcode_panel``` setting. These configurations will appear under the ```Export to BBCode: Show Export Menu``` command palette command. + +```javascript +// Define configurations for the drop down export menu +"bbcode_panel": [ + { + "To Clipboard - Format as BBCode": { + "numbers": false, + "multi_select": true + } + } +] +``` + +The name of the command is the key value, and then you add the parameters you wish to specify. You can use any combination of settings below. + +- numbers (boolean): Display line numbers in the gutter. +- multi_select (boolean): If multiple regions are selected in a document, only export what is under those selections. By default only the first selection is recognized. Default is ```false``` +- color_scheme (string): The color scheme (tmTheme) file you would like to use. By default the current color scheme file is used, or the the alternate default color scheme if defined in the setting ```alternate_scheme```. +- clipboard_copy (boolean): Copy BBCode to the clipboard after generation. Default is ```true```. +- view_open (boolean): Open txt file of BBCode in a Sublime Text tab. Default is ```false```. +- no_header (boolean): Do not display file name, date, and time at the top of the HTML document. Default is ```false```. + +If you wish to bind a command to a key combination etc., the same settings as above can be used. + +Example: + +```javascript +{ + "keys": ["ctrl+alt+n"], + "command": "export_bbcode", + "args": { + "numbers": false, + "multi_select": true + } +} +``` + +# Usage: Annotations (HTML only) +Annotations are comments you can make on selected text. When the HTML is generated, the selected text will be underlined, and when the mouse hovers over them, a tooltip will appear with your comment. + + + +In order to use annotations, you must enter into an "Annotation Mode". This puts your file in a read only state. At this point, you can select text and create annotations using the annotation commands provided. When you leave the "Annotation Mode", all annotations will be lost. So you must print before leaving annotation mode. + +You can access the annotation commands from the command palette or from the context menu. + +The commands are as follows: + +- Enable Annotation Mode: Turn annotation mode on. +- Disable Annotation Mode: Turn annotation mode off. +- Annotate Selection: Annote the given selection (no multi-select support currently). +- Delete Annotation(s): Delete the annotation region the the cursor resides in (multi-select support). +- Delete All Annotations: Delete all annotation regions. +- Show Annotation Comment: Show the annotation comment of the region under the cursor. + +You can navigate the annotations in the generate HTML by using a jump table. You can show the jump table at any time by selecting the annotation button in the toolbar. You can also click any annotation to show the jump table as well. If it gets in the way, you can dock it in a different location. + + + + +# Settings File options +- alternate_scheme (string or false): Defines a default theme to be used if a theme is not specified in a command. When this is false, the current Sublime Text theme in use is used. +- alternate_font_size (int or false): Define an alternate font_size to use by default instead of the current one in use. Use the current one in use if set to a literal ```false```. Default is ```false```. +- alternate_font_face (string or false): Define an alternate font_face to use by default instead of the current one in use. Use the current one in use if set to a literal ```false```. Default is ```false```. +- valid_selection_size (integer): Minimum allowable size for a selection to be accepted for only the selection to be printed. +- linux_python2.6_lib (string): If you are on linux and Sublime Text is not including your Python 2.6 library folder, you can try and configure it here. +- html_panel (array of commands): Define export configurations to appear under the ```Export to HTML: Show Export Menu``` command palette command. +- bbcode_panel (array of commands): Define export configurations to appear under the ```Export to BBCode: Show Export Menu``` command palette command. + +#Credits +- agibsonsw: Original idea and algorithm for the plugin +- Paul Boddie: Desktop module for open files in web browser cross platform +- Print-Color and Print-Grayscale tmThemes were derived from Monokai Bright + +#Version 0.5.7 +- Better tooltips for annotations (they now follow the mouse) +- Remove workaround to fix gaps in background color (it is recommended to just use a reliable font like Courier) +- Change method of underlining annotations to work in wrap mode and non-wrap mode and with background colors +- Fix for CSS in annotation table not handling comment overflow + +#Version 0.5.6 +- Expose filters to ExportBbcode +- Port transparency simulation to ExportBbcode +- Add hue and colorize filters + +#Version 0.5.5 +- Various bug fixes +- Add color filters that can be applied to a theme +- Add shift_brightness to solve an issue I had with copying the html of very dark themes into Outlook at work + +#Version 0.5.0 +- Added ability to define path to save generated html to a specific folder with optional timestamp +- If selection foreground is not defined, use normal colors for text. +- Click annotations to show annotation jump table +- Removed shortcut actions +- Themes are now embedded in the html and can be extracted +- Added toggle plain text option and toggle wrapping (if enababled) +- Added toolbar to print, download theme, disable toggle wrapping (if enabled), toggle annotation jump table (if annotations available), toggle plain text, and toggle gutter +- Exposed toolbar options in configuration (can define any toolbar item to appear) +- Split out javascript into separate files +- Improved and fixed javascript issues + +# Version 0.4.1 +- Add date_time_format and show_full_path options +- Some internal adjustments + +# Version 0.4.0 +- Fix regression with option numbers = false +- Fix issue where if transparency was included in hex color, color would not render +- Fix regression where annotation table would not show + +# Version 0.3.2 +- Allow alternate font size and face via the settings file +- Tweak annotation jump table style and code + +# Version 0.3.1 +- Position annotation jump table in different locations via drop down list + +# Version 0.3.0 +- Add annotation jump table for the HTML. Show table with "alt+double_click" + +# Version 0.2.0 +- Fix issue where html is opened twice +- New annotation feature +- New export to BBCode +- Rename PrintHTML to ExportHTML +- Fix HTML Title (display actual file name of content) +- Update documentation + +# Version 0.1.1 +- Fix status returnd as None for Windows + +# Version 0.1.0 +- Initial release diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Context.sublime-menu b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Context.sublime-menu new file mode 100644 index 0000000..2cf4afd --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Context.sublime-menu @@ -0,0 +1,8 @@ +[ + { "caption": "-" }, + { "caption": "FileDiffs Menu", "command": "file_diff_menu" }, + { "caption": "Diff Selections", "command": "file_diff_selections" }, + { "caption": "Diff with Clipboard", "command": "file_diff_clipboard" }, + { "caption": "Diff with Saved", "command": "file_diff_saved" }, + { "caption": "-" } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Default.sublime-commands b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Default.sublime-commands new file mode 100644 index 0000000..596557d --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Default.sublime-commands @@ -0,0 +1,6 @@ +[ + { + "caption": "FileDiffs: Menu", + "command": "file_diff_menu" + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Example.sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Example.sublime-keymap new file mode 100644 index 0000000..33e5eb1 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Example.sublime-keymap @@ -0,0 +1,3 @@ +[ + { "keys": ["ctrl+shift+d"], "command": "file_diff_menu" } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/FileDiffs.sublime-settings b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/FileDiffs.sublime-settings new file mode 100644 index 0000000..c6f0b9d --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/FileDiffs.sublime-settings @@ -0,0 +1,31 @@ +// You can set a command like this +// "cmd": ["command", "$file1", "$file2"] + +// You can also include other command line parameters like this +// "cmd": ["command", "-parameter1", "-parameter2", "$file1", "$file2"] + +{ + // just uncomment one of the examples + // or write your own command + // NOTE: Copy/paste example below or write your own command in: Package Settings --> FileDiffs --> Settings - User + // This file will be overwritten if the package is updated. + + // opendiff (FileMerge) + // "cmd": ["opendiff", "$file1", "$file2"] + + // ksdiff (Kaleidoscope) + // "cmd": ["ksdiff", "$file1", "$file2"] + + // "open_in_sublime": false + // twdiff (Textwrangler) + // "cmd": ["twdiff", "$file1", "$file2"] + + // bbdiff (BBEdit) NOTE: Use example below if you receive error. + // "cmd": ["bbdiff", "$file1", "$file2"] + + // bbdiff (BBEdit) + // "cmd" ["/usr/local/bin/bbdiff", "$file1", "$file2"] + + // deltawalker (DeltaWalker) + // "cmd": ["deltawalker", "-nosplash", "$file1", "$file2"] +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Main.sublime-menu b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Main.sublime-menu new file mode 100644 index 0000000..edb3be8 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Main.sublime-menu @@ -0,0 +1,27 @@ +[ + { + "caption": "Preferences", + "mnemonic": "n", + "id": "preferences", + "children": + [ + { + "caption": "Package Settings", + "mnemonic": "P", + "id": "package-settings", + "children": + [ + { + "caption": "FileDiffs", + "children": + [ + { "command": "open_file", "args": {"file": "${packages}/FileDiffs/FileDiffs.sublime-settings"}, "caption": "Settings – Default" }, + { "command": "open_file", "args": {"file": "${packages}/User/FileDiffs.sublime-settings"}, "caption": "Settings – User" }, + { "caption": "-" } + ] + } + ] + } + ] + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/README.md b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/README.md new file mode 100644 index 0000000..ad0183a --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/README.md @@ -0,0 +1,55 @@ +FileDiffs Plugin for Sublime Text 2 +=================================== + +Shows diffs - also in an external diff tool - between the current file, or selection(s) in the current file, and clipboard, another file, or unsaved changes. + + +Installation +------------ + +1. Using Package Control, install "FileDiffs" + +Or: + +1. Open the Sublime Text 2 Packages folder + + - OS X: ~/Library/Application Support/Sublime Text 2/Packages/ + - Windows: %APPDATA%/Sublime Text 2/Packages/ + - Linux: ~/.Sublime Text 2/Packages/ or ~/.config/sublime-text-2/Packages + +2. clone this repo +3. Install keymaps for the commands (see Example.sublime-keymap for my preferred keys) + +Add External Diff Tool +-------- + +(IMPORTANT: Dont forget to make a correct symlink (e.g. in /usr/bin) pointing to the command line tool of your external diff tool) + +1. Preferences > Package Settings > FileDiffs > Settings - Default + +2. Uncomment one of the examples or write you own command to open external diff tool. + + +Commands +-------- + +`file_diff_menu`: Shows a menu to select one of the file_diff commands. Bound to `ctrl+shift+d`. + +The rest of the commands are not bound by default: + +`file_diff_clipboard`: Shows the diff of the current file or selection(s) and the clipboard (the clipboard is considered the "new" file unless `reverse` is True) + +`file_diff_selections`: Shows the diff of the first and second selected regions. The file_diff_menu command checks for exactly two regions selected, otherwise it doesn't display this command. + +`file_diff_saved`: Shows the diff of the current file or selection(s) and the saved file. + +`file_diff_file`: Shows the diff of the current file or selection(s) and a file that is in the current project. + +`file_diff_tab`: Shows the diff of the current file or selection(s) and an open file (aka a file that has a tab). + +Help! +----- + +Check the [wiki][] for more tips + +[wiki]: https://github.com/colinta/SublimeFileDiffs/wiki diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Side Bar.sublime-menu b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Side Bar.sublime-menu new file mode 100644 index 0000000..3200757 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Side Bar.sublime-menu @@ -0,0 +1,6 @@ +[ + { "caption": "-" }, + { "caption": "FileDiffs Menu", "command": "file_diff_menu" }, + { "caption": "Diff with File in Project…", "command": "file_diff_file" }, + { "caption": "-" } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Tab Context.sublime-menu b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Tab Context.sublime-menu new file mode 100644 index 0000000..2a7fc9e --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/Tab Context.sublime-menu @@ -0,0 +1,6 @@ +[ + { "caption": "-" }, + { "caption": "FileDiffs Menu", "command": "file_diff_menu" }, + { "caption": "Diff with Tab…", "command": "file_diff_tab" }, + { "caption": "-" } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/file_diffs.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/file_diffs.py new file mode 100644 index 0000000..16f79a8 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/file_diffs.py @@ -0,0 +1,292 @@ +# coding: utf8 +import os +import re + +import sublime +import sublime_plugin +import difflib +import tempfile + +from fnmatch import fnmatch +import codecs + +SETTINGS = sublime.load_settings('FileDiffs.sublime-settings') + +CLIPBOARD = u'Diff file with Clipboard' +SELECTIONS = u'Diff Selections' +SAVED = u'Diff file with Saved' +FILE = u'Diff file with File in Project…' +TAB = u'Diff file with Open Tab…' + +FILE_DIFFS = [CLIPBOARD, SAVED, FILE, TAB] + + +class FileDiffMenuCommand(sublime_plugin.TextCommand): + def run(self, edit): + menu_items = FILE_DIFFS[:] + saved = SAVED + non_empty_regions = [region for region in self.view.sel() if not region.empty()] + if len(non_empty_regions) == 2: + menu_items.insert(1, SELECTIONS) + elif len(non_empty_regions): + menu_items = [f.replace(u'Diff file', u'Diff selection') for f in menu_items] + saved = saved.replace(u'Diff file', u'Diff selection') + + if not (self.view.file_name() and self.view.is_dirty()): + menu_items.remove(saved) + + def on_done(index): + restored_menu_items = [f.replace(u'Diff selection', u'Diff file') for f in menu_items] + if index == -1: + return + elif restored_menu_items[index] == CLIPBOARD: + self.view.run_command('file_diff_clipboard') + elif restored_menu_items[index] == SELECTIONS: + self.view.run_command('file_diff_selections') + elif restored_menu_items[index] == SAVED: + self.view.run_command('file_diff_saved') + elif restored_menu_items[index] == FILE: + self.view.run_command('file_diff_file') + elif restored_menu_items[index] == TAB: + self.view.run_command('file_diff_tab') + self.view.window().show_quick_panel(menu_items, on_done) + + +class FileDiffCommand(sublime_plugin.TextCommand): + def diff_content(self): + content = '' + + regions = [region for region in self.view.sel()] + for region in regions: + if region.empty(): + continue + content += self.view.substr(region) + + if not content: + content = self.view.substr(sublime.Region(0, self.view.size())) + return content + + def run_diff(self, a, b, from_file=None, to_file=None): + from_content = a + to_content = b + + if os.path.exists(a): + if from_file is None: + from_file = a + with codecs.open(from_file, mode='U', encoding='utf-8') as f: + from_content = f.readlines() + else: + from_content = a.splitlines(True) + if from_file is None: + from_file = 'from_file' + + if os.path.exists(b): + if to_file is None: + to_file = b + with codecs.open(to_file, mode='U', encoding='utf-8') as f: + to_content = f.readlines() + else: + to_content = b.splitlines(True) + if to_file is None: + to_file = 'to_file' + + diffs = list(difflib.unified_diff(from_content, to_content, from_file, to_file)) + + open_in_sublime = SETTINGS.get('open_in_sublime', True) + external_command = SETTINGS.get('cmd') + if not diffs: + sublime.status_message('No Difference') + else: + if external_command: + self.diff_with_external(a, b, from_file, to_file) + + if open_in_sublime: + self.diff_in_sublime(diffs) + + def diff_with_external(self, a, b, from_file=None, to_file=None): + try: + if not os.path.exists(from_file): + tmp_file = tempfile.NamedTemporaryFile(delete=False) + from_file = tmp_file.name + tmp_file.close() + + with codecs.open(from_file, encoding='utf-8', mode='w+') as tmp_file: + tmp_file.write(a) + + if not os.path.exists(to_file): + tmp_file = tempfile.NamedTemporaryFile(delete=False) + to_file = tmp_file.name + tmp_file.close() + + with codecs.open(to_file, encoding='utf-8', mode='w+') as tmp_file: + tmp_file.write(b) + + if os.path.exists(from_file): + command = SETTINGS.get('cmd') + if command is not None: + command = [c.replace(u'$file1', from_file) for c in command] + command = [c.replace(u'$file2', to_file) for c in command] + self.view.window().run_command("exec", {"cmd": command}) + except Exception as e: + # some basic logging here, since we are cluttering the /tmp folder + print repr(e) + sublime.status_message(str(e)) + + def diff_in_sublime(self, diffs): + scratch = self.view.window().new_file() + scratch.set_scratch(True) + scratch.set_syntax_file('Packages/Diff/Diff.tmLanguage') + scratch_edit = scratch.begin_edit('file_diffs') + scratch.insert(scratch_edit, 0, ''.join(diffs)) + scratch.end_edit(scratch_edit) + + +class FileDiffClipboardCommand(FileDiffCommand): + def run(self, edit, **kwargs): + current = sublime.get_clipboard() + self.run_diff(self.diff_content(), current, + from_file=self.view.file_name(), + to_file='(clipboard)') + + +class FileDiffSelectionsCommand(FileDiffCommand): + def run(self, edit, **kwargs): + regions = self.view.sel() + current = self.view.substr(regions[0]) + diff = self.view.substr(regions[1]) + + # trim off indent + indent = None + for line in current.splitlines(): + new_indent = re.match('[ \t]*', line).group(0) + if new_indent == '': + continue + + if indent is None: + indent = new_indent + elif len(new_indent) < len(indent): + indent = new_indent + + if not indent: + break + + if indent: + current = u"\n".join(line[len(indent):] for line in current.splitlines()) + + # trim off indent + indent = None + for line in diff.splitlines(): + new_indent = re.match('[ \t]*', line).group(0) + if new_indent == '': + continue + + if indent is None: + indent = new_indent + elif len(new_indent) < len(indent): + indent = new_indent + + if indent: + diff = u"\n".join(line[len(indent):] for line in diff.splitlines()) + + self.run_diff(current, diff, + from_file='first selection', + to_file='second selection') + + +class FileDiffSavedCommand(FileDiffCommand): + def run(self, edit, **kwargs): + content = '' + regions = [region for region in self.view.sel()] + for region in regions: + if region.empty(): + continue + content += self.view.substr(region) + if not content: + content = self.view.substr(sublime.Region(0, self.view.size())) + + self.run_diff(self.view.file_name(), content, + from_file=self.view.file_name(), + to_file=self.view.file_name() + u' (Unsaved)') + + +class FileDiffFileCommand(FileDiffCommand): + def run(self, edit, **kwargs): + common = None + folders = self.view.window().folders() + files = self.find_files(folders) + for folder in folders: + if common == None: + common = folder + else: + common_len = len(common) + while folder[0:common_len] != common[0:common_len]: + common_len -= 1 + common = common[0:common_len] + + my_file = self.view.file_name() + # filter out my_file + files = [file for file in files if file != my_file] + # shorten names using common length + file_picker = [file[len(common):] for file in files] + + def on_done(index): + if index > -1: + self.run_diff(self.diff_content(), files[index], + from_file=self.view.file_name()) + self.view.window().show_quick_panel(file_picker, on_done) + + def find_files(self, folders): + # Cannot access these settings!! WHY!? + # folder_exclude_patterns = self.view.settings().get('folder_exclude_patterns') + # file_exclude_patterns = self.view.settings().get('file_exclude_patterns') + folder_exclude_patterns = [".svn", ".git", ".hg", "CVS"] + file_exclude_patterns = ["*.pyc", "*.pyo", "*.exe", "*.dll", "*.obj", "*.o", "*.a", "*.lib", "*.so", "*.dylib", "*.ncb", "*.sdf", "*.suo", "*.pdb", "*.idb", ".DS_Store", "*.class", "*.psd", "*.db"] + + ret = [] + for folder in folders: + if not os.path.isdir(folder): + continue + + for file in os.listdir(folder): + fullpath = os.path.join(folder, file) + if os.path.isdir(fullpath): + # excluded folder? + if not len([True for pattern in folder_exclude_patterns if fnmatch(file, pattern)]): + ret += self.find_files([fullpath]) + else: + # excluded file? + if not len([True for pattern in file_exclude_patterns if fnmatch(file, pattern)]): + ret.append(fullpath) + return ret + + +class FileDiffTabCommand(FileDiffCommand): + def run(self, edit, **kwargs): + my_id = self.view.id() + files = [] + contents = [] + untitled_count = 1 + for v in self.view.window().views(): + if v.id() != my_id: + this_content = v.substr(sublime.Region(0, v.size())) + if v.file_name(): + files.append(v.file_name()) + elif v.name(): + files.append(v.name()) + else: + files.append('untitled %d' % untitled_count) + untitled_count += 1 + + contents.append(this_content) + + def on_done(index): + if index > -1: + self.run_diff(self.diff_content(), contents[index], + from_file=self.view.file_name(), + to_file=files[index]) + + if len(files) == 1: + on_done(0) + else: + menu_items = [os.path.basename(f) for f in files] + self.view.window().show_quick_panel(menu_items, on_done) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/package-metadata.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/package-metadata.json new file mode 100644 index 0000000..7710cac --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/package-metadata.json @@ -0,0 +1 @@ +{"url": "https://github.com/spape/SublimeFileDiffs", "version": "1.5.0", "description": "Shows diffs - also in an external diff tool - between the current file, or selection(s) in the current file, and clipboard, another file, or unsaved changes."} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/package.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/package.json new file mode 100644 index 0000000..d78b90a --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/FileDiffs/package.json @@ -0,0 +1,7 @@ +{ + "repo": "SublimeFileDiffs", + "name": "FileDiffs", + "description": "Shows diffs - also in an external diff tool - between the current file, or selection(s) in the current file, and clipboard, another file, or unsaved changes.", + "author": "Colin Thomas-Arnold (colinta), Sebastian Pape (spape), Jiri Urban (jiriurban)", + "homepage": "https://github.com/spape/SublimeFileDiffs" +} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/LiveReload.sublime-settings b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/LiveReload.sublime-settings new file mode 100644 index 0000000..8dc3ca9 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/LiveReload.sublime-settings @@ -0,0 +1,8 @@ +{ + "version" : "1.6" + , "port" : 35729 + , "delay_ms" : 100 + , "apply_js_live" : false + , "apply_css_live" : true + , "apply_images_live" : true +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/Main.sublime-menu b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/Main.sublime-menu new file mode 100644 index 0000000..8622061 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/Main.sublime-menu @@ -0,0 +1,39 @@ +[ + { + "caption": "Preferences", + "mnemonic": "n", + "id": "preferences", + "children": + [ + { + "caption": "Package Settings", + "mnemonic": "P", + "id": "package-settings", + "children": + [ + { + "caption": "LiveReload", + "children": + [ + { + "command": "open_file", "args": + { + "file": "${packages}/LiveReload/LiveReload.sublime-settings" + }, + "caption": "Settings – Default" + }, + { + "command": "open_file", "args": + { + "file": "${packages}/User/LiveReload.sublime-settings" + }, + "caption": "Settings – User" + }, + { "caption": "-" } + ] + } + ] + } + ] + } +] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/README.md b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/README.md new file mode 100644 index 0000000..bf14a91 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/README.md @@ -0,0 +1,62 @@ +LiveReload for Sublime Text 2 +========= + +A web browser page reloading plugin for the [Sublime Text 2](http://sublimetext.com "Sublime Text 2") editor. + +Installing +----- + +Install with [Sublime Package Control](http://wbond.net/sublime_packages/package_control "Sublime Package Control"), search for LiveReload and install. + +Devel branch +----- +Have a look at [devel version](https://github.com/dz0ny/LiveReload-sublimetext2/tree/devel). Which is total rewrite of plugin, supporting SublimeText 3, plugins and much more. + +Browser extensions +----- +You can use both major LiveReload versions. For old one you can find instructions bellow, for new ones please visit [New browser extensions](http://help.livereload.com/kb/general-use/browser-extensions "New browser extensions") or try [self loading version](http://help.livereload.com/kb/general-use/using-livereload-without-browser-extensions "self loading version"). + + +### [Google Chrome extension](https://chrome.google.com/extensions/detail/jnihajbhpnppcggbcgedagnkighmdlei) + +![](https://github.com/mockko/livereload/raw/master/docs/images/chrome-install-prompt.png) + +Click “Install”. Actually, LiveReload does not access your browser history. The warning is misleading. + +![](https://github.com/mockko/livereload/raw/master/docs/images/chrome-button.png) + +If you want to use it with local files, be sure to enable “Allow access to file URLs” checkbox in Tools > Extensions > LiveReload after installation. + +### Safari extension + +For now it only works with self loading version: + + + + +### [Firefox 4 extension](http://feedback.livereload.com/knowledgebase/articles/86242-how-do-i-install-and-use-the-browser-extensions-) + +![](http://static-cdn.addons.mozilla.net/img/uploads/previews/full/63/63478.png?modified=1317506904) + + +## Usage + +Now, if you are using Safari, right-click the page you want to be livereload'ed and choose “Enable LiveReload”: + +![](https://github.com/mockko/livereload/raw/master/docs/images/safari-context-menu.png) + +If you are using Chrome, just click the toolbar button (it will turn green to indicate that LiveReload is active). + +---- + +You can also use the Preferences menu to change port, version and type of reloading(full, js,css). + +## Compass + +![](http://cdn.nmecdesign.com/wp/wp-content/uploads/2011/12/Compass-Logo.png) + +Want to use Livereload with compass ? Now you can ! + +.scss and .css have to be in the same directory , if no config.rb file is found one will automatically generated ! + +So if you want to start using compass, install compass gem, edit a xxx.scss file and voila ! A .css file would be automatically generated. diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/assets/config.rb b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/assets/config.rb new file mode 100644 index 0000000..10577cd --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/assets/config.rb @@ -0,0 +1,8 @@ +http_path = "/" +css_dir = "." +sass_dir = "." +images_dir = "img" +javascripts_dir = "js" +output_style = :compressed +relative_assets=true +line_comments = false \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/livereload.js b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/livereload.js new file mode 100644 index 0000000..3a14a06 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/LiveReload/livereload.js @@ -0,0 +1,942 @@ +(function() { +var __customevents = {}, __protocol = {}, __connector = {}, __timer = {}, __options = {}, __reloader = {}, __livereload = {}, __less = {}, __startup = {}; + +// customevents +var CustomEvents; +CustomEvents = { + bind: function(element, eventName, handler) { + if (element.addEventListener) { + return element.addEventListener(eventName, handler, false); + } else if (element.attachEvent) { + element[eventName] = 1; + return element.attachEvent('onpropertychange', function(event) { + if (event.propertyName === eventName) { + return handler(); + } + }); + } else { + throw new Error("Attempt to attach custom event " + eventName + " to something which isn't a DOMElement"); + } + }, + fire: function(element, eventName) { + var event; + if (element.addEventListener) { + event = document.createEvent('HTMLEvents'); + event.initEvent(eventName, true, true); + return document.dispatchEvent(event); + } else if (element.attachEvent) { + if (element[eventName]) { + return element[eventName]++; + } + } else { + throw new Error("Attempt to fire custom event " + eventName + " on something which isn't a DOMElement"); + } + } +}; +__customevents.bind = CustomEvents.bind; +__customevents.fire = CustomEvents.fire; + +// protocol +var PROTOCOL_6, PROTOCOL_7, Parser, ProtocolError; +var __indexOf = Array.prototype.indexOf || function(item) { + for (var i = 0, l = this.length; i < l; i++) { + if (this[i] === item) return i; + } + return -1; +}; +__protocol.PROTOCOL_6 = PROTOCOL_6 = 'http://livereload.com/protocols/official-6'; +__protocol.PROTOCOL_7 = PROTOCOL_7 = 'http://livereload.com/protocols/official-7'; +__protocol.ProtocolError = ProtocolError = (function() { + function ProtocolError(reason, data) { + this.message = "LiveReload protocol error (" + reason + ") after receiving data: \"" + data + "\"."; + } + return ProtocolError; +})(); +__protocol.Parser = Parser = (function() { + function Parser(handlers) { + this.handlers = handlers; + this.reset(); + } + Parser.prototype.reset = function() { + return this.protocol = null; + }; + Parser.prototype.process = function(data) { + var command, message, options, _ref; + try { + if (!(this.protocol != null)) { + if (data.match(/^!!ver:([\d.]+)$/)) { + this.protocol = 6; + } else if (message = this._parseMessage(data, ['hello'])) { + if (!message.protocols.length) { + throw new ProtocolError("no protocols specified in handshake message"); + } else if (__indexOf.call(message.protocols, PROTOCOL_7) >= 0) { + this.protocol = 7; + } else if (__indexOf.call(message.protocols, PROTOCOL_6) >= 0) { + this.protocol = 6; + } else { + throw new ProtocolError("no supported protocols found"); + } + } + return this.handlers.connected(this.protocol); + } else if (this.protocol === 6) { + message = JSON.parse(data); + if (!message.length) { + throw new ProtocolError("protocol 6 messages must be arrays"); + } + command = message[0], options = message[1]; + if (command !== 'refresh') { + throw new ProtocolError("unknown protocol 6 command"); + } + return this.handlers.message({ + command: 'reload', + path: options.path, + liveCSS: (_ref = options.apply_css_live) != null ? _ref : true + }); + } else { + message = this._parseMessage(data, ['reload', 'alert']); + return this.handlers.message(message); + } + } catch (e) { + if (e instanceof ProtocolError) { + return this.handlers.error(e); + } else { + throw e; + } + } + }; + Parser.prototype._parseMessage = function(data, validCommands) { + var message, _ref; + try { + message = JSON.parse(data); + } catch (e) { + throw new ProtocolError('unparsable JSON', data); + } + if (!message.command) { + throw new ProtocolError('missing "command" key', data); + } + if (_ref = message.command, __indexOf.call(validCommands, _ref) < 0) { + throw new ProtocolError("invalid command '" + message.command + "', only valid commands are: " + (validCommands.join(', ')) + ")", data); + } + return message; + }; + return Parser; +})(); + +// connector +var Connector, PROTOCOL_6, PROTOCOL_7, Parser, Version, _ref; + +_ref = __protocol, Parser = _ref.Parser, PROTOCOL_6 = _ref.PROTOCOL_6, PROTOCOL_7 = _ref.PROTOCOL_7; + +Version = '2.0.7'; + +__connector.Connector = Connector = (function() { + + function Connector(options, WebSocket, Timer, handlers) { + var _this = this; + this.options = options; + this.WebSocket = WebSocket; + this.Timer = Timer; + this.handlers = handlers; + this._uri = "ws://" + this.options.host + ":" + this.options.port + "/livereload"; + this._nextDelay = this.options.mindelay; + this._connectionDesired = false; + this.protocol = 0; + this.protocolParser = new Parser({ + connected: function(protocol) { + _this.protocol = protocol; + _this._handshakeTimeout.stop(); + _this._nextDelay = _this.options.mindelay; + _this._disconnectionReason = 'broken'; + return _this.handlers.connected(protocol); + }, + error: function(e) { + _this.handlers.error(e); + return _this._closeOnError(); + }, + message: function(message) { + return _this.handlers.message(message); + } + }); + this._handshakeTimeout = new Timer(function() { + if (!_this._isSocketConnected()) return; + _this._disconnectionReason = 'handshake-timeout'; + return _this.socket.close(); + }); + this._reconnectTimer = new Timer(function() { + if (!_this._connectionDesired) return; + return _this.connect(); + }); + this.connect(); + } + + Connector.prototype._isSocketConnected = function() { + return this.socket && this.socket.readyState === this.WebSocket.OPEN; + }; + + Connector.prototype.connect = function() { + var _this = this; + this._connectionDesired = true; + if (this._isSocketConnected()) return; + this._reconnectTimer.stop(); + this._disconnectionReason = 'cannot-connect'; + this.protocolParser.reset(); + this.handlers.connecting(); + this.socket = new this.WebSocket(this._uri); + this.socket.onopen = function(e) { + return _this._onopen(e); + }; + this.socket.onclose = function(e) { + return _this._onclose(e); + }; + this.socket.onmessage = function(e) { + return _this._onmessage(e); + }; + return this.socket.onerror = function(e) { + return _this._onerror(e); + }; + }; + + Connector.prototype.disconnect = function() { + this._connectionDesired = false; + this._reconnectTimer.stop(); + if (!this._isSocketConnected()) return; + this._disconnectionReason = 'manual'; + return this.socket.close(); + }; + + Connector.prototype._scheduleReconnection = function() { + if (!this._connectionDesired) return; + if (!this._reconnectTimer.running) { + this._reconnectTimer.start(this._nextDelay); + return this._nextDelay = Math.min(this.options.maxdelay, this._nextDelay * 2); + } + }; + + Connector.prototype.sendCommand = function(command) { + if (this.protocol == null) return; + return this._sendCommand(command); + }; + + Connector.prototype._sendCommand = function(command) { + return this.socket.send(JSON.stringify(command)); + }; + + Connector.prototype._closeOnError = function() { + this._handshakeTimeout.stop(); + this._disconnectionReason = 'error'; + return this.socket.close(); + }; + + Connector.prototype._onopen = function(e) { + var hello; + this.handlers.socketConnected(); + this._disconnectionReason = 'handshake-failed'; + hello = { + command: 'hello', + protocols: [PROTOCOL_6, PROTOCOL_7] + }; + hello.ver = Version; + if (this.options.ext) hello.ext = this.options.ext; + if (this.options.extver) hello.extver = this.options.extver; + if (this.options.snipver) hello.snipver = this.options.snipver; + this._sendCommand(hello); + return this._handshakeTimeout.start(this.options.handshake_timeout); + }; + + Connector.prototype._onclose = function(e) { + this.protocol = 0; + this.handlers.disconnected(this._disconnectionReason, this._nextDelay); + return this._scheduleReconnection(); + }; + + Connector.prototype._onerror = function(e) {}; + + Connector.prototype._onmessage = function(e) { + return this.protocolParser.process(e.data); + }; + + return Connector; + +})(); + +// timer +var Timer; +var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; +__timer.Timer = Timer = (function() { + function Timer(func) { + this.func = func; + this.running = false; + this.id = null; + this._handler = __bind(function() { + this.running = false; + this.id = null; + return this.func(); + }, this); + } + Timer.prototype.start = function(timeout) { + if (this.running) { + clearTimeout(this.id); + } + this.id = setTimeout(this._handler, timeout); + return this.running = true; + }; + Timer.prototype.stop = function() { + if (this.running) { + clearTimeout(this.id); + this.running = false; + return this.id = null; + } + }; + return Timer; +})(); +Timer.start = function(timeout, func) { + return setTimeout(func, timeout); +}; + +// options +var Options; +__options.Options = Options = (function() { + function Options() { + this.host = null; + this.port = 35729; + this.snipver = null; + this.ext = null; + this.extver = null; + this.mindelay = 1000; + this.maxdelay = 60000; + this.handshake_timeout = 5000; + } + Options.prototype.set = function(name, value) { + switch (typeof this[name]) { + case 'undefined': + break; + case 'number': + return this[name] = +value; + default: + return this[name] = value; + } + }; + return Options; +})(); +Options.extract = function(document) { + var element, keyAndValue, m, mm, options, pair, src, _i, _j, _len, _len2, _ref, _ref2; + _ref = document.getElementsByTagName('script'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + element = _ref[_i]; + if ((src = element.src) && (m = src.match(/^[^:]+:\/\/(.*)\/z?livereload\.js(?:\?(.*))?$/))) { + options = new Options(); + if (mm = m[1].match(/^([^\/:]+)(?::(\d+))?$/)) { + options.host = mm[1]; + if (mm[2]) { + options.port = parseInt(mm[2], 10); + } + } + if (m[2]) { + _ref2 = m[2].split('&'); + for (_j = 0, _len2 = _ref2.length; _j < _len2; _j++) { + pair = _ref2[_j]; + if ((keyAndValue = pair.split('=')).length > 1) { + options.set(keyAndValue[0].replace(/-/g, '_'), keyAndValue.slice(1).join('=')); + } + } + } + return options; + } + } + return null; +}; + +// reloader +var IMAGE_STYLES, Reloader, numberOfMatchingSegments, pathFromUrl, pathsMatch, pickBestMatch, splitUrl; + +splitUrl = function(url) { + var hash, index, params; + if ((index = url.indexOf('#')) >= 0) { + hash = url.slice(index); + url = url.slice(0, index); + } else { + hash = ''; + } + if ((index = url.indexOf('?')) >= 0) { + params = url.slice(index); + url = url.slice(0, index); + } else { + params = ''; + } + return { + url: url, + params: params, + hash: hash + }; +}; + +pathFromUrl = function(url) { + var path; + url = splitUrl(url).url; + if (url.indexOf('file://') === 0) { + path = url.replace(/^file:\/\/(localhost)?/, ''); + } else { + path = url.replace(/^([^:]+:)?\/\/([^:\/]+)(:\d*)?\//, '/'); + } + return decodeURIComponent(path); +}; + +pickBestMatch = function(path, objects, pathFunc) { + var bestMatch, object, score, _i, _len; + bestMatch = { + score: 0 + }; + for (_i = 0, _len = objects.length; _i < _len; _i++) { + object = objects[_i]; + score = numberOfMatchingSegments(path, pathFunc(object)); + if (score > bestMatch.score) { + bestMatch = { + object: object, + score: score + }; + } + } + if (bestMatch.score > 0) { + return bestMatch; + } else { + return null; + } +}; + +numberOfMatchingSegments = function(path1, path2) { + var comps1, comps2, eqCount, len; + path1 = path1.replace(/^\/+/, '').toLowerCase(); + path2 = path2.replace(/^\/+/, '').toLowerCase(); + if (path1 === path2) return 10000; + comps1 = path1.split('/').reverse(); + comps2 = path2.split('/').reverse(); + len = Math.min(comps1.length, comps2.length); + eqCount = 0; + while (eqCount < len && comps1[eqCount] === comps2[eqCount]) { + ++eqCount; + } + return eqCount; +}; + +pathsMatch = function(path1, path2) { + return numberOfMatchingSegments(path1, path2) > 0; +}; + +IMAGE_STYLES = [ + { + selector: 'background', + styleNames: ['backgroundImage'] + }, { + selector: 'border', + styleNames: ['borderImage', 'webkitBorderImage', 'MozBorderImage'] + } +]; + +__reloader.Reloader = Reloader = (function() { + + function Reloader(window, console, Timer) { + this.window = window; + this.console = console; + this.Timer = Timer; + this.document = this.window.document; + this.stylesheetGracePeriod = 200; + this.importCacheWaitPeriod = 200; + this.plugins = []; + } + + Reloader.prototype.addPlugin = function(plugin) { + return this.plugins.push(plugin); + }; + + Reloader.prototype.analyze = function(callback) { + return results; + }; + + Reloader.prototype.reload = function(path, options) { + var plugin, _i, _len, _ref; + this.options = options; + _ref = this.plugins; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + plugin = _ref[_i]; + if (plugin.reload && plugin.reload(path, options)) return; + } + if (options.liveCSS) { + if (path.match(/\.css$/i)) if (this.reloadStylesheet(path)) return; + } + if (options.liveImg) { + if (path.match(/\.(jpe?g|png|gif)$/i)) { + this.reloadImages(path); + return; + } + } + return this.reloadPage(); + }; + + Reloader.prototype.reloadPage = function() { + return this.window.document.location.reload(); + }; + + Reloader.prototype.reloadImages = function(path) { + var expando, img, selector, styleNames, styleSheet, _i, _j, _k, _l, _len, _len2, _len3, _len4, _ref, _ref2, _ref3, _ref4, _results; + expando = this.generateUniqueString(); + _ref = this.document.images; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + img = _ref[_i]; + if (pathsMatch(path, pathFromUrl(img.src))) { + img.src = this.generateCacheBustUrl(img.src, expando); + } + } + if (this.document.querySelectorAll) { + for (_j = 0, _len2 = IMAGE_STYLES.length; _j < _len2; _j++) { + _ref2 = IMAGE_STYLES[_j], selector = _ref2.selector, styleNames = _ref2.styleNames; + _ref3 = this.document.querySelectorAll("[style*=" + selector + "]"); + for (_k = 0, _len3 = _ref3.length; _k < _len3; _k++) { + img = _ref3[_k]; + this.reloadStyleImages(img.style, styleNames, path, expando); + } + } + } + if (this.document.styleSheets) { + _ref4 = this.document.styleSheets; + _results = []; + for (_l = 0, _len4 = _ref4.length; _l < _len4; _l++) { + styleSheet = _ref4[_l]; + _results.push(this.reloadStylesheetImages(styleSheet, path, expando)); + } + return _results; + } + }; + + Reloader.prototype.reloadStylesheetImages = function(styleSheet, path, expando) { + var rule, rules, styleNames, _i, _j, _len, _len2; + try { + rules = styleSheet != null ? styleSheet.cssRules : void 0; + } catch (e) { + + } + if (!rules) return; + for (_i = 0, _len = rules.length; _i < _len; _i++) { + rule = rules[_i]; + switch (rule.type) { + case CSSRule.IMPORT_RULE: + this.reloadStylesheetImages(rule.styleSheet, path, expando); + break; + case CSSRule.STYLE_RULE: + for (_j = 0, _len2 = IMAGE_STYLES.length; _j < _len2; _j++) { + styleNames = IMAGE_STYLES[_j].styleNames; + this.reloadStyleImages(rule.style, styleNames, path, expando); + } + break; + case CSSRule.MEDIA_RULE: + this.reloadStylesheetImages(rule, path, expando); + } + } + }; + + Reloader.prototype.reloadStyleImages = function(style, styleNames, path, expando) { + var newValue, styleName, value, _i, _len; + var _this = this; + for (_i = 0, _len = styleNames.length; _i < _len; _i++) { + styleName = styleNames[_i]; + value = style[styleName]; + if (typeof value === 'string') { + newValue = value.replace(/\burl\s*\(([^)]*)\)/, function(match, src) { + if (pathsMatch(path, pathFromUrl(src))) { + return "url(" + (_this.generateCacheBustUrl(src, expando)) + ")"; + } else { + return match; + } + }); + if (newValue !== value) style[styleName] = newValue; + } + } + }; + + Reloader.prototype.reloadStylesheet = function(path) { + var imported, link, links, match, style, _i, _j, _k, _len, _len2, _len3, _ref; + links = (function() { + var _i, _len, _ref, _results; + _ref = this.document.getElementsByTagName('link'); + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + link = _ref[_i]; + if (link.rel === 'stylesheet' && !link.__LiveReload_pendingRemoval) { + _results.push(link); + } + } + return _results; + }).call(this); + imported = []; + _ref = this.document.getElementsByTagName('style'); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + style = _ref[_i]; + if (style.sheet) { + this.collectImportedStylesheets(style, style.sheet, imported); + } + } + for (_j = 0, _len2 = links.length; _j < _len2; _j++) { + link = links[_j]; + this.collectImportedStylesheets(link, link.sheet, imported); + } + this.console.log("LiveReload found " + links.length + " LINKed stylesheets, " + imported.length + " @imported stylesheets"); + match = pickBestMatch(path, links.concat(imported), function(l) { + return pathFromUrl(l.href); + }); + if (match) { + if (match.object.rule) { + this.console.log("LiveReload is reloading imported stylesheet: " + match.object.href); + this.reattachImportedRule(match.object); + } else { + this.console.log("LiveReload is reloading stylesheet: " + match.object.href); + this.reattachStylesheetLink(match.object); + } + } else { + this.console.log("LiveReload will reload all stylesheets because path '" + path + "' did not match any specific one"); + for (_k = 0, _len3 = links.length; _k < _len3; _k++) { + link = links[_k]; + this.reattachStylesheetLink(link); + } + } + return true; + }; + + Reloader.prototype.collectImportedStylesheets = function(link, styleSheet, result) { + var index, rule, rules, _len; + try { + rules = styleSheet != null ? styleSheet.cssRules : void 0; + } catch (e) { + + } + if (rules && rules.length) { + for (index = 0, _len = rules.length; index < _len; index++) { + rule = rules[index]; + switch (rule.type) { + case CSSRule.CHARSET_RULE: + continue; + case CSSRule.IMPORT_RULE: + result.push({ + link: link, + rule: rule, + index: index, + href: rule.href + }); + this.collectImportedStylesheets(link, rule.styleSheet, result); + break; + default: + break; + } + } + } + }; + + Reloader.prototype.reattachStylesheetLink = function(link) { + var clone, parent, timer; + if (link.__LiveReload_pendingRemoval) return; + link.__LiveReload_pendingRemoval = true; + clone = link.cloneNode(false); + clone.href = this.generateCacheBustUrl(link.href); + parent = link.parentNode; + if (parent.lastChild === link) { + parent.appendChild(clone); + } else { + parent.insertBefore(clone, link.nextSibling); + } + timer = new this.Timer(function() { + if (link.parentNode) return link.parentNode.removeChild(link); + }); + return timer.start(this.stylesheetGracePeriod); + }; + + Reloader.prototype.reattachImportedRule = function(_arg) { + var href, index, link, media, newRule, parent, rule, tempLink; + var _this = this; + rule = _arg.rule, index = _arg.index, link = _arg.link; + parent = rule.parentStyleSheet; + href = this.generateCacheBustUrl(rule.href); + media = rule.media.length ? [].join.call(rule.media, ', ') : ''; + newRule = "@import url(\"" + href + "\") " + media + ";"; + rule.__LiveReload_newHref = href; + tempLink = this.document.createElement("link"); + tempLink.rel = 'stylesheet'; + tempLink.href = href; + tempLink.__LiveReload_pendingRemoval = true; + if (link.parentNode) link.parentNode.insertBefore(tempLink, link); + return this.Timer.start(this.importCacheWaitPeriod, function() { + if (tempLink.parentNode) tempLink.parentNode.removeChild(tempLink); + if (rule.__LiveReload_newHref !== href) return; + parent.insertRule(newRule, index); + parent.deleteRule(index + 1); + rule = parent.cssRules[index]; + rule.__LiveReload_newHref = href; + return _this.Timer.start(_this.importCacheWaitPeriod, function() { + if (rule.__LiveReload_newHref !== href) return; + parent.insertRule(newRule, index); + return parent.deleteRule(index + 1); + }); + }); + }; + + Reloader.prototype.generateUniqueString = function() { + return 'livereload=' + Date.now(); + }; + + Reloader.prototype.generateCacheBustUrl = function(url, expando) { + var hash, oldParams, params, _ref; + if (expando == null) expando = this.generateUniqueString(); + _ref = splitUrl(url), url = _ref.url, hash = _ref.hash, oldParams = _ref.params; + if (this.options.overrideURL) { + if (url.indexOf(this.options.serverURL) < 0) { + url = this.options.serverURL + this.options.overrideURL + "?url=" + encodeURIComponent(url); + } + } + params = oldParams.replace(/(\?|&)livereload=(\d+)/, function(match, sep) { + return "" + sep + expando; + }); + if (params === oldParams) { + if (oldParams.length === 0) { + params = "?" + expando; + } else { + params = "" + oldParams + "&" + expando; + } + } + return url + params + hash; + }; + + return Reloader; + +})(); + +// livereload +var Connector, LiveReload, Options, Reloader, Timer; + +Connector = __connector.Connector; + +Timer = __timer.Timer; + +Options = __options.Options; + +Reloader = __reloader.Reloader; + +__livereload.LiveReload = LiveReload = (function() { + + function LiveReload(window) { + var _this = this; + this.window = window; + this.listeners = {}; + this.plugins = []; + this.pluginIdentifiers = {}; + this.console = this.window.location.href.match(/LR-verbose/) && this.window.console && this.window.console.log && this.window.console.error ? this.window.console : { + log: function() {}, + error: function() {} + }; + if (!(this.WebSocket = this.window.WebSocket || this.window.MozWebSocket)) { + console.error("LiveReload disabled because the browser does not seem to support web sockets"); + return; + } + if (!(this.options = Options.extract(this.window.document))) { + console.error("LiveReload disabled because it could not find its own ' + # update output html file + tmp_fullpath = getTempMarkdownPreviewPath(self.view) + tmp_html = open(tmp_fullpath, 'w') + tmp_html.write(full_html.encode(encoding)) + tmp_html.close() + # now opens in browser if needed + if target == 'browser': + config_browser = settings.get('browser') + if config_browser and config_browser != 'default': + cmd = '"%s" %s' % (config_browser, tmp_fullpath) + if sys.platform == 'darwin': + cmd = "open -a %s" % cmd + elif sys.platform == 'linux2': + cmd += ' &' + result = os.system(cmd) + if result != 0: + sublime.error_message('cannot execute "%s" Please check your Markdown Preview settings' % config_browser) + else: + sublime.status_message('Markdown preview launched in %s' % config_browser) + else: + desktop.open(tmp_fullpath) + sublime.status_message('Markdown preview launched in default html viewer') + elif target == 'sublime': + # create a new buffer and paste the output HTML + new_view = self.view.window().new_file() + new_view.set_scratch(True) + new_edit = new_view.begin_edit() + new_view.insert(new_edit, 0, markdown_html) + new_view.end_edit(new_edit) + sublime.status_message('Markdown preview launched in sublime') + elif target == 'clipboard': + # clipboard copy the full HTML + sublime.set_clipboard(full_html) + sublime.status_message('Markdown export copied to clipboard') diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/MarkdownPreview.sublime-settings b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/MarkdownPreview.sublime-settings new file mode 100644 index 0000000..f815f52 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/MarkdownPreview.sublime-settings @@ -0,0 +1,60 @@ +/* + Markdown Preview default settings +*/ +{ + /* + Sets the default opener for html files + + default - Use the system default HTML viewer + other - Set a full path to any executable. ex: /Applications/Google Chrome Canary.app or /Applications/Firefox.app + */ + "browser": "default", + + /* + Sets the default parser for converting markdown to html. + Warning for github API : if you have a ST2 linux build, Python is not built with SSL o it may not work + + default - Use the builtin python-markdown2 parser + github - User github API to convert markdown, so you can use GitHub flavored Markdown, see http://github.github.com/github-flavored-markdown/ + */ + "parser": "default", + + /* + Default mode for the github Markdon parser : markdown (documents) or gfm (comments) + see http://developer.github.com/v3/markdown/#render-an-arbitrary-markdown-document + */ + "github_mode": "markdown", + + /* + Uses an OAuth token to when parsing markdown with GitHub API. To create one for Markdown Preview, see https://help.github.com/articles/creating-an-oauth-token-for-command-line-use. + */ + // "github_oauth_token": "secret" + + /* + Sets the default css file to embed in the HTML + + default - Use the builtin CSS or github CSS, depending on parser config (markdown.css or github.css) + other - Set an absolute path or url to any css file + */ + "css": "default", + + /* + Allow CSS overrides + + true - Any file with matching a .markdown_filetype extension with .css will be loaded as an override + false - Matching files ignored + */ + "allow_css_overrides": true, + + /* + Sets the supported filetypes for auto-reload on save + */ + "markdown_filetypes": [".md", ".markdown", ".mdown"], + + /* + Strips the YAML front matter header and converts title to a heading + */ + "strip_yaml_front_matter": false +} + + diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/README.md b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/README.md new file mode 100644 index 0000000..94c3e54 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/README.md @@ -0,0 +1,52 @@ +Sublime Text 2 MarkDown preview +=============================== + +A simple ST2 plugin to help you preview your markdown files quickly in you web browser. + +You can use builtin [python-markdown2][0] parser (default) or use the [github markdown API][5] for the conversion (edit your settings to select it). + +**NOTE:** If you choose the GitHub API for conversion (set parser: github in your settings), your code will be sent through https to github for live conversion. You'll have [Github flavored markdown][6], syntax highlighting and EMOJI support for free :heart: :octocat: :gift:. If you make more than 60 calls a day, be sure to set your GitHub API key in the settings :) + +**LINUX users:** If you want to use GitHub API for conversion, you'll need to have a custom Python install that includes python-ssl as its not built in the Sublime Text 2 Linux package. see [@dusteye comment][8]. If you use a custom window manager, also be sure to set a `BROWSER` environnement variable. see [@PPvG comments][9] + +## Features : + + - Markdown conversion via builtin Markdown Parser ([python-markdown2][0]) or via Github API : just choose in your settings. + - Browser preview auto reload on save if you have the [ST2 LiveReload plugin][7] installed. + - Builtin parser : Support TOC, footnotes markdown extensions + - CSS overriding if you need + - YAML support thanks to @tommi + - Clipboard selection and copy to clipboard thanks to @hexatrope + +## Installation : + + - you should use [sublime package manager][3] + - use `cmd+shift+P` then `Package Control: Install Package` + - look for `Markdown Preview` and install it. + +## Usage : + + - optionnaly select some of your markdown for conversion + - use `cmd+shift+P` then `Markdown Preview` to launch a preview + - or bind some key in your user key binding, using a line like this one: + `{ "keys": ["alt+m"], "command": "markdown_preview", "args": {"target": "browser"} },` + - once converted a first time, the output HTML will be updated on each file save (with LiveReload plugin) + +## Uses : + + - [python-markdown2][0] for markdown parsing **OR** the GitHub markdown API. + + +## Licence : + +The code is available at github [https://github.com/revolunet/sublimetext-markdown-preview][2] under MIT licence : [http://revolunet.mit-license.org][4] + + [0]: https://github.com/trentm/python-markdown2 + [2]: https://github.com/revolunet/sublimetext-markdown-preview + [3]: http://wbond.net/sublime_packages/package_control + [4]: http://revolunet.mit-license.org + [5]: http://developer.github.com/v3/markdown + [6]: http://github.github.com/github-flavored-markdown/ + [7]: https://github.com/dz0ny/LiveReload-sublimetext2 + [8]: https://github.com/revolunet/sublimetext-markdown-preview/issues/27#issuecomment-11772098 + [9]: https://github.com/revolunet/sublimetext-markdown-preview/issues/78#issuecomment-15644727 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/desktop/__init__.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/desktop/__init__.py new file mode 100644 index 0000000..d87486f --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/desktop/__init__.py @@ -0,0 +1,290 @@ +#!/usr/bin/env python + +""" +Simple desktop integration for Python. This module provides desktop environment +detection and resource opening support for a selection of common and +standardised desktop environments. + +Copyright (C) 2005, 2006, 2007, 2008, 2009 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along +with this program. If not, see . + +-------- + +Desktop Detection +----------------- + +To detect a specific desktop environment, use the get_desktop function. +To detect whether the desktop environment is standardised (according to the +proposed DESKTOP_LAUNCH standard), use the is_standard function. + +Opening URLs +------------ + +To open a URL in the current desktop environment, relying on the automatic +detection of that environment, use the desktop.open function as follows: + +desktop.open("http://www.python.org") + +To override the detected desktop, specify the desktop parameter to the open +function as follows: + +desktop.open("http://www.python.org", "KDE") # Insists on KDE +desktop.open("http://www.python.org", "GNOME") # Insists on GNOME +desktop.open("http://www.python.org", "MATE") # Insists on MATE + +Without overriding using the desktop parameter, the open function will attempt +to use the "standard" desktop opening mechanism which is controlled by the +DESKTOP_LAUNCH environment variable as described below. + +The DESKTOP_LAUNCH Environment Variable +--------------------------------------- + +The DESKTOP_LAUNCH environment variable must be shell-quoted where appropriate, +as shown in some of the following examples: + +DESKTOP_LAUNCH="kdialog --msgbox" Should present any opened URLs in + their entirety in a KDE message box. + (Command "kdialog" plus parameter.) +DESKTOP_LAUNCH="my\ opener" Should run the "my opener" program to + open URLs. + (Command "my opener", no parameters.) +DESKTOP_LAUNCH="my\ opener --url" Should run the "my opener" program to + open URLs. + (Command "my opener" plus parameter.) + +Details of the DESKTOP_LAUNCH environment variable convention can be found here: +http://lists.freedesktop.org/archives/xdg/2004-August/004489.html + +Other Modules +------------- + +The desktop.dialog module provides support for opening dialogue boxes. +The desktop.windows module permits the inspection of desktop windows. +""" + +__version__ = "0.4" + +import os +import sys + +# Provide suitable process creation functions. + +try: + import subprocess + def _run(cmd, shell, wait): + opener = subprocess.Popen(cmd, shell=shell) + if wait: opener.wait() + return opener.pid + + def _readfrom(cmd, shell): + opener = subprocess.Popen(cmd, shell=shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE) + opener.stdin.close() + return opener.stdout.read() + + def _status(cmd, shell): + opener = subprocess.Popen(cmd, shell=shell) + opener.wait() + return opener.returncode == 0 + +except ImportError: + import popen2 + def _run(cmd, shell, wait): + opener = popen2.Popen3(cmd) + if wait: opener.wait() + return opener.pid + + def _readfrom(cmd, shell): + opener = popen2.Popen3(cmd) + opener.tochild.close() + opener.childerr.close() + return opener.fromchild.read() + + def _status(cmd, shell): + opener = popen2.Popen3(cmd) + opener.wait() + return opener.poll() == 0 + +import commands + +# Private functions. + +def _get_x11_vars(): + + "Return suitable environment definitions for X11." + + if not os.environ.get("DISPLAY", "").strip(): + return "DISPLAY=:0.0 " + else: + return "" + +def _is_xfce(): + + "Return whether XFCE is in use." + + # XFCE detection involves testing the output of a program. + + try: + return _readfrom(_get_x11_vars() + "xprop -root _DT_SAVE_MODE", shell=1).strip().endswith(' = "xfce4"') + except OSError: + return 0 + +def _is_x11(): + + "Return whether the X Window System is in use." + + return os.environ.has_key("DISPLAY") + +# Introspection functions. + +def get_desktop(): + + """ + Detect the current desktop environment, returning the name of the + environment. If no environment could be detected, None is returned. + """ + + if os.environ.has_key("KDE_FULL_SESSION") or \ + os.environ.has_key("KDE_MULTIHEAD"): + return "KDE" + elif os.environ.has_key("GNOME_DESKTOP_SESSION_ID") or \ + os.environ.has_key("GNOME_KEYRING_SOCKET"): + return "GNOME" + elif os.environ.has_key("MATE_DESKTOP_SESSION_ID") or \ + os.environ.has_key("MATE_KEYRING_SOCKET"): + return "MATE" + elif sys.platform == "darwin": + return "Mac OS X" + elif hasattr(os, "startfile"): + return "Windows" + elif _is_xfce(): + return "XFCE" + + # KDE, GNOME, MATE and XFCE run on X11, so we have to test for X11 last. + + if _is_x11(): + return "X11" + else: + return None + +def use_desktop(desktop): + + """ + Decide which desktop should be used, based on the detected desktop and a + supplied 'desktop' argument (which may be None). Return an identifier + indicating the desktop type as being either "standard" or one of the results + from the 'get_desktop' function. + """ + + # Attempt to detect a desktop environment. + + detected = get_desktop() + + # Start with desktops whose existence can be easily tested. + + if (desktop is None or desktop == "standard") and is_standard(): + return "standard" + elif (desktop is None or desktop == "Windows") and detected == "Windows": + return "Windows" + + # Test for desktops where the overriding is not verified. + + elif (desktop or detected) == "KDE": + return "KDE" + elif (desktop or detected) == "GNOME": + return "GNOME" + elif (desktop or detected) == "MATE": + return "MATE" + elif (desktop or detected) == "XFCE": + return "XFCE" + elif (desktop or detected) == "Mac OS X": + return "Mac OS X" + elif (desktop or detected) == "X11": + return "X11" + else: + return None + +def is_standard(): + + """ + Return whether the current desktop supports standardised application + launching. + """ + + return os.environ.has_key("DESKTOP_LAUNCH") + +# Activity functions. + +def open(url, desktop=None, wait=0): + + """ + Open the 'url' in the current desktop's preferred file browser. If the + optional 'desktop' parameter is specified then attempt to use that + particular desktop environment's mechanisms to open the 'url' instead of + guessing or detecting which environment is being used. + + Suggested values for 'desktop' are "standard", "KDE", "GNOME", "GNOME", + "XFCE", "Mac OS X", "Windows" where "standard" employs a DESKTOP_LAUNCH + environment variable to open the specified 'url'. DESKTOP_LAUNCH should + be a command, possibly followed by arguments, and must have any special + characters shell-escaped. + + The process identifier of the "opener" (ie. viewer, editor, browser or + program) associated with the 'url' is returned by this function. If the + process identifier cannot be determined, None is returned. + + An optional 'wait' parameter is also available for advanced usage and, if + 'wait' is set to a true value, this function will wait for the launching + mechanism to complete before returning (as opposed to immediately returning + as is the default behaviour). + """ + + # Decide on the desktop environment in use. + + desktop_in_use = use_desktop(desktop) + + if desktop_in_use == "standard": + arg = "".join([os.environ["DESKTOP_LAUNCH"], commands.mkarg(url)]) + return _run(arg, 1, wait) + + elif desktop_in_use == "Windows": + # NOTE: This returns None in current implementations. + return os.startfile(url) + + elif desktop_in_use == "KDE": + cmd = ["xdg-open", url] + + elif desktop_in_use == "GNOME": + cmd = ["xdg-open", url] + + elif desktop_in_use == "MATE": + cmd = ["xdg-open", url] + + elif desktop_in_use == "XFCE": + cmd = ["xdg-open", url] + + elif desktop_in_use == "Mac OS X": + cmd = ["open", url] + + elif desktop_in_use == "X11" and os.environ.has_key("BROWSER"): + cmd = [os.environ["BROWSER"], url] + + # Finish with an error where no suitable desktop was identified. + + else: + raise OSError, "Desktop '%s' not supported (neither DESKTOP_LAUNCH nor os.startfile could be used)" % desktop_in_use + + return _run(cmd, 0, wait) + +# vim: tabstop=4 expandtab shiftwidth=4 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/desktop/dialog.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/desktop/dialog.py new file mode 100644 index 0000000..113af66 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/desktop/dialog.py @@ -0,0 +1,551 @@ +#!/usr/bin/env python + +""" +Simple desktop dialogue box support for Python. + +Copyright (C) 2007, 2009 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along +with this program. If not, see . + +-------- + +Opening Dialogue Boxes (Dialogs) +-------------------------------- + +To open a dialogue box (dialog) in the current desktop environment, relying on +the automatic detection of that environment, use the appropriate dialogue box +class: + +question = desktop.dialog.Question("Are you sure?") +result = question.open() + +To override the detected desktop, specify the desktop parameter to the open +function as follows: + +question.open("KDE") # Insists on KDE +question.open("GNOME") # Insists on GNOME +question.open("MATE") # Insists on MATE + +The dialogue box options are documented in each class's docstring. + +Available dialogue box classes are listed in the desktop.dialog.available +attribute. + +Supported desktop environments are listed in the desktop.dialog.supported +attribute. +""" + +from desktop import use_desktop, _run, _readfrom, _status + +class _wrapper: + def __init__(self, handler): + self.handler = handler + +class _readvalue(_wrapper): + def __call__(self, cmd, shell): + return self.handler(cmd, shell).strip() + +class _readinput(_wrapper): + def __call__(self, cmd, shell): + return self.handler(cmd, shell)[:-1] + +class _readvalues_kdialog(_wrapper): + def __call__(self, cmd, shell): + result = self.handler(cmd, shell).strip().strip('"') + if result: + return result.split('" "') + else: + return [] + +class _readvalues_zenity(_wrapper): + def __call__(self, cmd, shell): + result = self.handler(cmd, shell).strip() + if result: + return result.split("|") + else: + return [] + +class _readvalues_Xdialog(_wrapper): + def __call__(self, cmd, shell): + result = self.handler(cmd, shell).strip() + if result: + return result.split("/") + else: + return [] + +# Dialogue parameter classes. + +class String: + + "A generic parameter." + + def __init__(self, name): + self.name = name + + def convert(self, value, program): + return [value or ""] + +class Strings(String): + + "Multiple string parameters." + + def convert(self, value, program): + return value or [] + +class StringPairs(String): + + "Multiple string parameters duplicated to make identifiers." + + def convert(self, value, program): + l = [] + for v in value: + l.append(v) + l.append(v) + return l + +class StringKeyword: + + "A keyword parameter." + + def __init__(self, keyword, name): + self.keyword = keyword + self.name = name + + def convert(self, value, program): + return [self.keyword + "=" + (value or "")] + +class StringKeywords: + + "Multiple keyword parameters." + + def __init__(self, keyword, name): + self.keyword = keyword + self.name = name + + def convert(self, value, program): + l = [] + for v in value or []: + l.append(self.keyword + "=" + v) + return l + +class Integer(String): + + "An integer parameter." + + defaults = { + "width" : 40, + "height" : 15, + "list_height" : 10 + } + scale = 8 + + def __init__(self, name, pixels=0): + String.__init__(self, name) + if pixels: + self.factor = self.scale + else: + self.factor = 1 + + def convert(self, value, program): + if value is None: + value = self.defaults[self.name] + return [str(int(value) * self.factor)] + +class IntegerKeyword(Integer): + + "An integer keyword parameter." + + def __init__(self, keyword, name, pixels=0): + Integer.__init__(self, name, pixels) + self.keyword = keyword + + def convert(self, value, program): + if value is None: + value = self.defaults[self.name] + return [self.keyword + "=" + str(int(value) * self.factor)] + +class Boolean(String): + + "A boolean parameter." + + values = { + "kdialog" : ["off", "on"], + "zenity" : ["FALSE", "TRUE"], + "Xdialog" : ["off", "on"] + } + + def convert(self, value, program): + values = self.values[program] + if value: + return [values[1]] + else: + return [values[0]] + +class MenuItemList(String): + + "A menu item list parameter." + + def convert(self, value, program): + l = [] + for v in value: + l.append(v.value) + l.append(v.text) + return l + +class ListItemList(String): + + "A radiolist/checklist item list parameter." + + def __init__(self, name, status_first=0): + String.__init__(self, name) + self.status_first = status_first + + def convert(self, value, program): + l = [] + for v in value: + boolean = Boolean(None) + status = boolean.convert(v.status, program) + if self.status_first: + l += status + l.append(v.value) + l.append(v.text) + if not self.status_first: + l += status + return l + +# Dialogue argument values. + +class MenuItem: + + "A menu item which can also be used with radiolists and checklists." + + def __init__(self, value, text, status=0): + self.value = value + self.text = text + self.status = status + +# Dialogue classes. + +class Dialogue: + + commands = { + "KDE" : "kdialog", + "GNOME" : "zenity", + "MATE" : "zenity", + "XFCE" : "zenity", # NOTE: Based on observations with Xubuntu. + "X11" : "Xdialog" + } + + def open(self, desktop=None): + + """ + Open a dialogue box (dialog) using a program appropriate to the desktop + environment in use. + + If the optional 'desktop' parameter is specified then attempt to use + that particular desktop environment's mechanisms to open the dialog + instead of guessing or detecting which environment is being used. + + Suggested values for 'desktop' are "standard", "KDE", "GNOME", + "MATE", "Mac OS X", "Windows". + + The result of the dialogue interaction may be a string indicating user + input (for Input, Password, Menu, Pulldown), a list of strings + indicating selections of one or more items (for RadioList, CheckList), + or a value indicating true or false (for Question, Warning, Message, + Error). + + Where a string value may be expected but no choice is made, an empty + string may be returned. Similarly, where a list of values is expected + but no choice is made, an empty list may be returned. + """ + + # Decide on the desktop environment in use. + + desktop_in_use = use_desktop(desktop) + + # Get the program. + + try: + program = self.commands[desktop_in_use] + except KeyError: + raise OSError, "Desktop '%s' not supported (no known dialogue box command could be suggested)" % desktop_in_use + + # The handler is one of the functions communicating with the subprocess. + # Some handlers return boolean values, others strings. + + handler, options = self.info[program] + + cmd = [program] + for option in options: + if isinstance(option, str): + cmd.append(option) + else: + value = getattr(self, option.name, None) + cmd += option.convert(value, program) + + return handler(cmd, 0) + +class Simple(Dialogue): + def __init__(self, text, width=None, height=None): + self.text = text + self.width = width + self.height = height + +class Question(Simple): + + """ + A dialogue asking a question and showing response buttons. + Options: text, width (in characters), height (in characters) + Response: a boolean value indicating an affirmative response (true) or a + negative response + """ + + name = "question" + info = { + "kdialog" : (_status, ["--yesno", String("text")]), + "zenity" : (_status, ["--question", StringKeyword("--text", "text")]), + "Xdialog" : (_status, ["--stdout", "--yesno", String("text"), Integer("height"), Integer("width")]), + } + +class Warning(Simple): + + """ + A dialogue asking a question and showing response buttons. + Options: text, width (in characters), height (in characters) + Response: a boolean value indicating an affirmative response (true) or a + negative response + """ + + name = "warning" + info = { + "kdialog" : (_status, ["--warningyesno", String("text")]), + "zenity" : (_status, ["--warning", StringKeyword("--text", "text")]), + "Xdialog" : (_status, ["--stdout", "--yesno", String("text"), Integer("height"), Integer("width")]), + } + +class Message(Simple): + + """ + A message dialogue. + Options: text, width (in characters), height (in characters) + Response: a boolean value indicating an affirmative response (true) or a + negative response + """ + + name = "message" + info = { + "kdialog" : (_status, ["--msgbox", String("text")]), + "zenity" : (_status, ["--info", StringKeyword("--text", "text")]), + "Xdialog" : (_status, ["--stdout", "--msgbox", String("text"), Integer("height"), Integer("width")]), + } + +class Error(Simple): + + """ + An error dialogue. + Options: text, width (in characters), height (in characters) + Response: a boolean value indicating an affirmative response (true) or a + negative response + """ + + name = "error" + info = { + "kdialog" : (_status, ["--error", String("text")]), + "zenity" : (_status, ["--error", StringKeyword("--text", "text")]), + "Xdialog" : (_status, ["--stdout", "--msgbox", String("text"), Integer("height"), Integer("width")]), + } + +class Menu(Simple): + + """ + A menu of options, one of which being selectable. + Options: text, width (in characters), height (in characters), + list_height (in items), items (MenuItem objects) + Response: a value corresponding to the chosen item + """ + + name = "menu" + info = { + "kdialog" : (_readvalue(_readfrom), ["--menu", String("text"), MenuItemList("items")]), + "zenity" : (_readvalue(_readfrom), ["--list", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), + MenuItemList("items")] + ), + "Xdialog" : (_readvalue(_readfrom), ["--stdout", "--menubox", + String("text"), Integer("height"), Integer("width"), Integer("list_height"), MenuItemList("items")] + ), + } + item = MenuItem + number_of_titles = 2 + + def __init__(self, text, titles, items=None, width=None, height=None, list_height=None): + + """ + Initialise a menu with the given heading 'text', column 'titles', and + optional 'items' (which may be added later), 'width' (in characters), + 'height' (in characters) and 'list_height' (in items). + """ + + Simple.__init__(self, text, width, height) + self.titles = ([""] * self.number_of_titles + titles)[-self.number_of_titles:] + self.items = items or [] + self.list_height = list_height + + def add(self, *args, **kw): + + """ + Add an item, passing the given arguments to the appropriate item class. + """ + + self.items.append(self.item(*args, **kw)) + +class RadioList(Menu): + + """ + A list of radio buttons, one of which being selectable. + Options: text, width (in characters), height (in characters), + list_height (in items), items (MenuItem objects), titles + Response: a list of values corresponding to chosen items (since some + programs, eg. zenity, appear to support multiple default + selections) + """ + + name = "radiolist" + info = { + "kdialog" : (_readvalues_kdialog(_readfrom), ["--radiolist", String("text"), ListItemList("items")]), + "zenity" : (_readvalues_zenity(_readfrom), + ["--list", "--radiolist", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), + ListItemList("items", 1)] + ), + "Xdialog" : (_readvalues_Xdialog(_readfrom), ["--stdout", "--radiolist", + String("text"), Integer("height"), Integer("width"), Integer("list_height"), ListItemList("items")] + ), + } + number_of_titles = 3 + +class CheckList(Menu): + + """ + A list of checkboxes, many being selectable. + Options: text, width (in characters), height (in characters), + list_height (in items), items (MenuItem objects), titles + Response: a list of values corresponding to chosen items + """ + + name = "checklist" + info = { + "kdialog" : (_readvalues_kdialog(_readfrom), ["--checklist", String("text"), ListItemList("items")]), + "zenity" : (_readvalues_zenity(_readfrom), + ["--list", "--checklist", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), + ListItemList("items", 1)] + ), + "Xdialog" : (_readvalues_Xdialog(_readfrom), ["--stdout", "--checklist", + String("text"), Integer("height"), Integer("width"), Integer("list_height"), ListItemList("items")] + ), + } + number_of_titles = 3 + +class Pulldown(Menu): + + """ + A pull-down menu of options, one of which being selectable. + Options: text, width (in characters), height (in characters), + items (list of values) + Response: a value corresponding to the chosen item + """ + + name = "pulldown" + info = { + "kdialog" : (_readvalue(_readfrom), ["--combobox", String("text"), Strings("items")]), + "zenity" : (_readvalue(_readfrom), + ["--list", "--radiolist", StringKeyword("--text", "text"), StringKeywords("--column", "titles"), + StringPairs("items")] + ), + "Xdialog" : (_readvalue(_readfrom), + ["--stdout", "--combobox", String("text"), Integer("height"), Integer("width"), Strings("items")]), + } + item = unicode + number_of_titles = 2 + +class Input(Simple): + + """ + An input dialogue, consisting of an input field. + Options: text, input, width (in characters), height (in characters) + Response: the text entered into the dialogue by the user + """ + + name = "input" + info = { + "kdialog" : (_readinput(_readfrom), + ["--inputbox", String("text"), String("data")]), + "zenity" : (_readinput(_readfrom), + ["--entry", StringKeyword("--text", "text"), StringKeyword("--entry-text", "data")]), + "Xdialog" : (_readinput(_readfrom), + ["--stdout", "--inputbox", String("text"), Integer("height"), Integer("width"), String("data")]), + } + + def __init__(self, text, data="", width=None, height=None): + Simple.__init__(self, text, width, height) + self.data = data + +class Password(Input): + + """ + A password dialogue, consisting of a password entry field. + Options: text, width (in characters), height (in characters) + Response: the text entered into the dialogue by the user + """ + + name = "password" + info = { + "kdialog" : (_readinput(_readfrom), + ["--password", String("text")]), + "zenity" : (_readinput(_readfrom), + ["--entry", StringKeyword("--text", "text"), "--hide-text"]), + "Xdialog" : (_readinput(_readfrom), + ["--stdout", "--password", "--inputbox", String("text"), Integer("height"), Integer("width")]), + } + +class TextFile(Simple): + + """ + A text file input box. + Options: filename, text, width (in characters), height (in characters) + Response: any text returned by the dialogue program (typically an empty + string) + """ + + name = "textfile" + info = { + "kdialog" : (_readfrom, ["--textbox", String("filename"), Integer("width", pixels=1), Integer("height", pixels=1)]), + "zenity" : (_readfrom, ["--text-info", StringKeyword("--filename", "filename"), IntegerKeyword("--width", "width", pixels=1), + IntegerKeyword("--height", "height", pixels=1)] + ), + "Xdialog" : (_readfrom, ["--stdout", "--textbox", String("filename"), Integer("height"), Integer("width")]), + } + + def __init__(self, filename, text="", width=None, height=None): + Simple.__init__(self, text, width, height) + self.filename = filename + +# Available dialogues. + +available = [Question, Warning, Message, Error, Menu, CheckList, RadioList, Input, Password, Pulldown, TextFile] + +# Supported desktop environments. + +supported = Dialogue.commands.keys() + +# vim: tabstop=4 expandtab shiftwidth=4 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/desktop/windows.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/desktop/windows.py new file mode 100644 index 0000000..2b19e85 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/desktop/windows.py @@ -0,0 +1,273 @@ +#!/usr/bin/env python + +""" +Simple desktop window enumeration for Python. + +Copyright (C) 2007, 2008, 2009 Paul Boddie + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU Lesser General Public License as published by the Free +Software Foundation; either version 3 of the License, or (at your option) any +later version. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. + +You should have received a copy of the GNU Lesser General Public License along +with this program. If not, see . + +-------- + +Finding Open Windows on the Desktop +----------------------------------- + +To obtain a list of windows, use the desktop.windows.list function as follows: + +windows = desktop.windows.list() + +To obtain the root window, typically the desktop background, use the +desktop.windows.root function as follows: + +root = desktop.windows.root() + +Each window object can be inspected through a number of methods. For example: + +name = window.name() +width, height = window.size() +x, y = window.position() +child_windows = window.children() + +See the desktop.windows.Window class for more information. +""" + +from desktop import _is_x11, _get_x11_vars, _readfrom, use_desktop +import re + +# System functions. + +def _xwininfo(identifier, action): + if identifier is None: + args = "-root" + else: + args = "-id " + identifier + + s = _readfrom(_get_x11_vars() + "xwininfo %s -%s" % (args, action), shell=1) + + # Return a mapping of keys to values for the "stats" action. + + if action == "stats": + d = {} + for line in s.split("\n"): + fields = line.split(":") + if len(fields) < 2: + continue + key, value = fields[0].strip(), ":".join(fields[1:]).strip() + d[key] = value + + return d + + # Otherwise, return the raw output. + + else: + return s + +def _get_int_properties(d, properties): + results = [] + for property in properties: + results.append(int(d[property])) + return results + +# Finder functions. + +def find_all(name): + return 1 + +def find_named(name): + return name is not None + +def find_by_name(name): + return lambda n, t=name: n == t + +# Window classes. +# NOTE: X11 is the only supported desktop so far. + +class Window: + + "A window on the desktop." + + _name_pattern = re.compile(r':\s+\(.*?\)\s+[-0-9x+]+\s+[-0-9+]+$') + _absent_names = "(has no name)", "(the root window) (has no name)" + + def __init__(self, identifier): + + "Initialise the window with the given 'identifier'." + + self.identifier = identifier + + # Finder methods (from above). + + self.find_all = find_all + self.find_named = find_named + self.find_by_name = find_by_name + + def __repr__(self): + return "Window(%r)" % self.identifier + + # Methods which deal with the underlying commands. + + def _get_handle_and_name(self, text): + fields = text.strip().split(" ") + handle = fields[0] + + # Get the "" part, stripping off the quotes. + + name = " ".join(fields[1:]) + if len(name) > 1 and name[0] == '"' and name[-1] == '"': + name = name[1:-1] + + if name in self._absent_names: + return handle, None + else: + return handle, name + + def _get_this_handle_and_name(self, line): + fields = line.split(":") + return self._get_handle_and_name(":".join(fields[1:])) + + def _get_descendant_handle_and_name(self, line): + match = self._name_pattern.search(line) + if match: + return self._get_handle_and_name(line[:match.start()].strip()) + else: + raise OSError, "Window information from %r did not contain window details." % line + + def _descendants(self, s, fn): + handles = [] + adding = 0 + for line in s.split("\n"): + if line.endswith("child:") or line.endswith("children:"): + if not adding: + adding = 1 + elif adding and line: + handle, name = self._get_descendant_handle_and_name(line) + if fn(name): + handles.append(handle) + return [Window(handle) for handle in handles] + + # Public methods. + + def children(self, all=0): + + """ + Return a list of windows which are children of this window. If the + optional 'all' parameter is set to a true value, all such windows will + be returned regardless of whether they have any name information. + """ + + s = _xwininfo(self.identifier, "children") + return self._descendants(s, all and self.find_all or self.find_named) + + def descendants(self, all=0): + + """ + Return a list of windows which are descendants of this window. If the + optional 'all' parameter is set to a true value, all such windows will + be returned regardless of whether they have any name information. + """ + + s = _xwininfo(self.identifier, "tree") + return self._descendants(s, all and self.find_all or self.find_named) + + def find(self, callable): + + """ + Return windows using the given 'callable' (returning a true or a false + value when invoked with a window name) for descendants of this window. + """ + + s = _xwininfo(self.identifier, "tree") + return self._descendants(s, callable) + + def name(self): + + "Return the name of the window." + + d = _xwininfo(self.identifier, "stats") + + # Format is 'xwininfo: Window id: "" + + return self._get_this_handle_and_name(d["xwininfo"])[1] + + def size(self): + + "Return a tuple containing the width and height of this window." + + d = _xwininfo(self.identifier, "stats") + return _get_int_properties(d, ["Width", "Height"]) + + def position(self): + + "Return a tuple containing the upper left co-ordinates of this window." + + d = _xwininfo(self.identifier, "stats") + return _get_int_properties(d, ["Absolute upper-left X", "Absolute upper-left Y"]) + + def displayed(self): + + """ + Return whether the window is displayed in some way (but not necessarily + visible on the current screen). + """ + + d = _xwininfo(self.identifier, "stats") + return d["Map State"] != "IsUnviewable" + + def visible(self): + + "Return whether the window is displayed and visible." + + d = _xwininfo(self.identifier, "stats") + return d["Map State"] == "IsViewable" + +def list(desktop=None): + + """ + Return a list of windows for the current desktop. If the optional 'desktop' + parameter is specified then attempt to use that particular desktop + environment's mechanisms to look for windows. + """ + + root_window = root(desktop) + window_list = [window for window in root_window.descendants() if window.displayed()] + window_list.insert(0, root_window) + return window_list + +def root(desktop=None): + + """ + Return the root window for the current desktop. If the optional 'desktop' + parameter is specified then attempt to use that particular desktop + environment's mechanisms to look for windows. + """ + + # NOTE: The desktop parameter is currently ignored and X11 is tested for + # NOTE: directly. + + if _is_x11(): + return Window(None) + else: + raise OSError, "Desktop '%s' not supported" % use_desktop(desktop) + +def find(callable, desktop=None): + + """ + Find and return windows using the given 'callable' for the current desktop. + If the optional 'desktop' parameter is specified then attempt to use that + particular desktop environment's mechanisms to look for windows. + """ + + return root(desktop).find(callable) + +# vim: tabstop=4 expandtab shiftwidth=4 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/github.css b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/github.css new file mode 100644 index 0000000..23bb92f --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/github.css @@ -0,0 +1,364 @@ +html { + margin: 0; + padding: 0; } + +body { + background-color: white; + border-radius: 3px; + border: 3px solid #EEE; + box-shadow: inset 0 0 0 1px #CECECE; + font-family: Helvetica,arial,sans-serif; + font-size: 14px; + line-height: 1.6; + width: 975px; + padding: 30px; + margin: 2em auto; } + +body > *:first-child { + margin-top: 0 !important; } +body > *:last-child { + margin-bottom: 0 !important; } + +a { + color: #4183C4; + text-decoration: none; } +a:hover { + text-decoration: underline; } +a.absent { + color: #cc0000; } +a.anchor { + display: block; + padding-left: 30px; + margin-left: -30px; + cursor: pointer; + position: absolute; + top: 0; + left: 0; + bottom: 0; } + +h1, h2, h3, h4, h5, h6 { + margin: 20px 0 10px; + padding: 0; + font-weight: bold; + -webkit-font-smoothing: antialiased; + cursor: text; + position: relative; } + +h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor { + background: url("../../images/modules/styleguide/para.png") no-repeat 10px center; + text-decoration: none; } + +h1 tt, h1 code { + font-size: inherit; } + +h2 tt, h2 code { + font-size: inherit; } + +h3 tt, h3 code { + font-size: inherit; } + +h4 tt, h4 code { + font-size: inherit; } + +h5 tt, h5 code { + font-size: inherit; } + +h6 tt, h6 code { + font-size: inherit; } + +h1 { + font-size: 28px; + color: black; } + +h2 { + font-size: 24px; + border-bottom: 1px solid #cccccc; + color: black; } + +h3 { + font-size: 18px; } + +h4 { + font-size: 16px; } + +h5 { + font-size: 14px; } + +h6 { + color: #777777; + font-size: 14px; } + +p, blockquote, ul, ol, dl, li, table, pre { + margin: 15px 0; } + +hr { + background: transparent url("https://a248.e.akamai.net/assets.github.com/assets/primer/markdown/dirty-shade-0e7d81b119cc9beae17b0c98093d121fa0050a74.png") repeat-x 0 0; + border: 0 none; + color: #ccc; + height: 4px; + padding: 0; } + +body > h2:first-child { + margin-top: 0; + padding-top: 0; } +body > h1:first-child { + margin-top: 0; + padding-top: 0; } + body > h1:first-child + h2 { + margin-top: 0; + padding-top: 0; } +body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child { + margin-top: 0; + padding-top: 0; } + +a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 { + margin-top: 0; + padding-top: 0; } + +h1 p, h2 p, h3 p, h4 p, h5 p, h6 p { + margin-top: 0; } + +li p.first { + display: inline-block; } + +ul, ol { + padding-left: 30px; } + +ul :first-child, ol :first-child { + margin-top: 0; } + +ul :last-child, ol :last-child { + margin-bottom: 0; } + +dl { + padding: 0; } + dl dt { + font-size: 14px; + font-weight: bold; + font-style: italic; + padding: 0; + margin: 15px 0 5px; } + dl dt:first-child { + padding: 0; } + dl dt > :first-child { + margin-top: 0; } + dl dt > :last-child { + margin-bottom: 0; } + dl dd { + margin: 0 0 15px; + padding: 0 15px; } + dl dd > :first-child { + margin-top: 0; } + dl dd > :last-child { + margin-bottom: 0; } + +blockquote { + border-left: 4px solid #dddddd; + padding: 0 15px; + color: #777777; } + blockquote > :first-child { + margin-top: 0; } + blockquote > :last-child { + margin-bottom: 0; } + +table { + border-collapse: collapse; border-spacing: 0; padding: 0; } + table tr { + border-top: 1px solid #cccccc; + background-color: white; + margin: 0; + padding: 0; } + table tr:nth-child(2n) { + background-color: #f8f8f8; } + table tr th { + font-weight: bold; + border: 1px solid #cccccc; + text-align: left; + margin: 0; + padding: 6px 13px; } + table tr td { + border: 1px solid #cccccc; + text-align: left; + margin: 0; + padding: 6px 13px; } + table tr th :first-child, table tr td :first-child { + margin-top: 0; } + table tr th :last-child, table tr td :last-child { + margin-bottom: 0; } + +img { + max-width: 100%; } + +span.frame { + display: block; + overflow: hidden; } + span.frame > span { + border: 1px solid #dddddd; + display: block; + float: left; + overflow: hidden; + margin: 13px 0 0; + padding: 7px; + width: auto; } + span.frame span img { + display: block; + float: left; } + span.frame span span { + clear: both; + color: #333333; + display: block; + padding: 5px 0 0; } +span.align-center { + display: block; + overflow: hidden; + clear: both; } + span.align-center > span { + display: block; + overflow: hidden; + margin: 13px auto 0; + text-align: center; } + span.align-center span img { + margin: 0 auto; + text-align: center; } +span.align-right { + display: block; + overflow: hidden; + clear: both; } + span.align-right > span { + display: block; + overflow: hidden; + margin: 13px 0 0; + text-align: right; } + span.align-right span img { + margin: 0; + text-align: right; } +span.float-left { + display: block; + margin-right: 13px; + overflow: hidden; + float: left; } + span.float-left span { + margin: 13px 0 0; } +span.float-right { + display: block; + margin-left: 13px; + overflow: hidden; + float: right; } + span.float-right > span { + display: block; + overflow: hidden; + margin: 13px auto 0; + text-align: right; } + +code, tt { + margin: 0 2px; + padding: 0 5px; + white-space: nowrap; + border: 1px solid #eaeaea; + background-color: #f8f8f8; + border-radius: 3px; } + +pre code { + margin: 0; + padding: 0; + white-space: pre; + border: none; + background: transparent; } + +.highlight pre { + background-color: #f8f8f8; + border: 1px solid #cccccc; + font-size: 13px; + line-height: 19px; + overflow: auto; + padding: 6px 10px; + border-radius: 3px; } + +pre { + background-color: #f8f8f8; + border: 1px solid #cccccc; + font-size: 13px; + line-height: 19px; + overflow: auto; + padding: 6px 10px; + border-radius: 3px; } + pre code, pre tt { + background-color: transparent; + border: none; } + + +.markdown-body code,.markdown-body tt{margin:0 2px;padding:0px 5px;white-space:nowrap;border:1px solid #eaeaea;background-color:#f8f8f8;border-radius:3px} +.markdown-body pre>code{margin:0;padding:0;white-space:pre;border:none;background:transparent} +.markdown-body .highlight pre,.markdown-body pre{background-color:#f8f8f8;border:1px solid #ccc;font-size:13px;line-height:19px;overflow:auto;padding:6px 10px;border-radius:3px} +.markdown-body pre code,.markdown-body pre tt{background-color:transparent;border:none} +.highlight{background:#ffffff} +.highlight .c{color:#999988;font-style:italic} +.highlight .err{color:#a61717;background-color:#e3d2d2} +.highlight .k{font-weight:bold} +.highlight .o{font-weight:bold} +.highlight .cm{color:#999988;font-style:italic} +.highlight .cp{color:#999999;font-weight:bold} +.highlight .c1{color:#999988;font-style:italic} +.highlight .cs{color:#999999;font-weight:bold;font-style:italic} +.highlight .gd{color:#000000;background-color:#ffdddd} +.highlight .gd .x{color:#000000;background-color:#ffaaaa} +.highlight .ge{font-style:italic} +.highlight .gr{color:#aa0000} +.highlight .gh{color:#999999} +.highlight .gi{color:#000000;background-color:#ddffdd} +.highlight .gi .x{color:#000000;background-color:#aaffaa} +.highlight .go{color:#888888} +.highlight .gp{color:#555555} +.highlight .gs{font-weight:bold} +.highlight .gu{color:#800080;font-weight:bold} +.highlight .gt{color:#aa0000} +.highlight .kc{font-weight:bold} +.highlight .kd{font-weight:bold} +.highlight .kn{font-weight:bold} +.highlight .kp{font-weight:bold} +.highlight .kr{font-weight:bold} +.highlight .kt{color:#445588;font-weight:bold} +.highlight .m{color:#009999} +.highlight .s{color:#d14} +.highlight .na{color:#008080} +.highlight .nb{color:#0086B3} +.highlight .nc{color:#445588;font-weight:bold} +.highlight .no{color:#008080} +.highlight .ni{color:#800080} +.highlight .ne{color:#990000;font-weight:bold} +.highlight .nf{color:#990000;font-weight:bold} +.highlight .nn{color:#555555} +.highlight .nt{color:#000080} +.highlight .nv{color:#008080} +.highlight .ow{font-weight:bold} +.highlight .w{color:#bbbbbb} +.highlight .mf{color:#009999} +.highlight .mh{color:#009999} +.highlight .mi{color:#009999} +.highlight .mo{color:#009999} +.highlight .sb{color:#d14} +.highlight .sc{color:#d14} +.highlight .sd{color:#d14} +.highlight .s2{color:#d14} +.highlight .se{color:#d14} +.highlight .sh{color:#d14} +.highlight .si{color:#d14} +.highlight .sx{color:#d14} +.highlight .sr{color:#009926} +.highlight .s1{color:#d14} +.highlight .ss{color:#990073} +.highlight .bp{color:#999999} +.highlight .vc{color:#008080} +.highlight .vg{color:#008080} +.highlight .vi{color:#008080} +.highlight .il{color:#009999} +.highlight .gc{color:#999;background-color:#EAF2F5} +.type-csharp .highlight .k{color:#0000FF} +.type-csharp .highlight .kt{color:#0000FF} +.type-csharp .highlight .nf{color:#000000;font-weight:normal} +.type-csharp .highlight .nc{color:#2B91AF} +.type-csharp .highlight .nn{color:#000000} +.type-csharp .highlight .s{color:#A31515} +.type-csharp .highlight .sc{color:#A31515} + + diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/markdown.css b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/markdown.css new file mode 100644 index 0000000..b96117f --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/markdown.css @@ -0,0 +1,122 @@ +html { font-size: 100%; overflow-y: scroll; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100%; } + +body{ + color:#444; + font-family:Georgia, Palatino, 'Palatino Linotype', Times, 'Times New Roman', + "Hiragino Sans GB", "STXihei", "微软雅黑", serif; + font-size:12px; + line-height:1.5em; + background:#fefefe; + width: 45em; + margin: 10px auto; + padding: 1em; + outline: 1300px solid #FAFAFA; +} + +a{ color: #0645ad; text-decoration:none;} +a:visited{ color: #0b0080; } +a:hover{ color: #06e; } +a:active{ color:#faa700; } +a:focus{ outline: thin dotted; } +a:hover, a:active{ outline: 0; } + +span.backtick { + border:1px solid #EAEAEA; + border-radius:3px; + background:#F8F8F8; + padding:0 3px 0 3px; +} + +::-moz-selection{background:rgba(255,255,0,0.3);color:#000} +::selection{background:rgba(255,255,0,0.3);color:#000} + +a::-moz-selection{background:rgba(255,255,0,0.3);color:#0645ad} +a::selection{background:rgba(255,255,0,0.3);color:#0645ad} + +p{ +margin:1em 0; +} + +img{ +max-width:100%; +} + +h1,h2,h3,h4,h5,h6{ +font-weight:normal; +color:#111; +line-height:1em; +} +h4,h5,h6{ font-weight: bold; } +h1{ font-size:2.5em; } +h2{ font-size:2em; border-bottom:1px solid silver; padding-bottom: 5px; } +h3{ font-size:1.5em; } +h4{ font-size:1.2em; } +h5{ font-size:1em; } +h6{ font-size:0.9em; } + +blockquote{ +color:#666666; +margin:0; +padding-left: 3em; +border-left: 0.5em #EEE solid; +} +hr { display: block; height: 2px; border: 0; border-top: 1px solid #aaa;border-bottom: 1px solid #eee; margin: 1em 0; padding: 0; } + + +pre , code, kbd, samp { + color: #000; + font-family: monospace; + font-size: 0.88em; + border-radius:3px; + background-color: #F8F8F8; + border: 1px solid #CCC; +} +pre { white-space: pre; white-space: pre-wrap; word-wrap: break-word; padding: 5px 12px;} +pre code { border: 0px !important; padding: 0;} +code { padding: 0 3px 0 3px; } + +b, strong { font-weight: bold; } + +dfn { font-style: italic; } + +ins { background: #ff9; color: #000; text-decoration: none; } + +mark { background: #ff0; color: #000; font-style: italic; font-weight: bold; } + +sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; } +sup { top: -0.5em; } +sub { bottom: -0.25em; } + +ul, ol { margin: 1em 0; padding: 0 0 0 2em; } +li p:last-child { margin:0 } +dd { margin: 0 0 0 2em; } + +img { border: 0; -ms-interpolation-mode: bicubic; vertical-align: middle; } + +table { border-collapse: collapse; border-spacing: 0; } +td { vertical-align: top; } + +@media only screen and (min-width: 480px) { +body{font-size:14px;} +} + +@media only screen and (min-width: 768px) { +body{font-size:16px;} +} + +@media print { + * { background: transparent !important; color: black !important; filter:none !important; -ms-filter: none !important; } + body{font-size:12pt; max-width:100%; outline:none;} + a, a:visited { text-decoration: underline; } + hr { height: 1px; border:0; border-bottom:1px solid black; } + a[href]:after { content: " (" attr(href) ")"; } + abbr[title]:after { content: " (" attr(title) ")"; } + .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: ""; } + pre, blockquote { border: 1px solid #999; padding-right: 1em; page-break-inside: avoid; } + tr, img { page-break-inside: avoid; } + img { max-width: 100% !important; } + @page :left { margin: 15mm 20mm 15mm 10mm; } + @page :right { margin: 15mm 10mm 15mm 20mm; } + p, h2, h3 { orphans: 3; widows: 3; } + h2, h3 { page-break-after: avoid; } +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/markdown2.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/markdown2.py new file mode 100644 index 0000000..3bad0c5 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/markdown2.py @@ -0,0 +1,2322 @@ +#!/usr/bin/env python +# Copyright (c) 2012 Trent Mick. +# Copyright (c) 2007-2008 ActiveState Corp. +# License: MIT (http://www.opensource.org/licenses/mit-license.php) + +from __future__ import generators + +r"""A fast and complete Python implementation of Markdown. + +[from http://daringfireball.net/projects/markdown/] +> Markdown is a text-to-HTML filter; it translates an easy-to-read / +> easy-to-write structured text format into HTML. Markdown's text +> format is most similar to that of plain text email, and supports +> features such as headers, *emphasis*, code blocks, blockquotes, and +> links. +> +> Markdown's syntax is designed not as a generic markup language, but +> specifically to serve as a front-end to (X)HTML. You can use span-level +> HTML tags anywhere in a Markdown document, and you can use block level +> HTML tags (like
and as well). + +Module usage: + + >>> import markdown2 + >>> markdown2.markdown("*boo!*") # or use `html = markdown_path(PATH)` + u'

boo!

\n' + + >>> markdowner = Markdown() + >>> markdowner.convert("*boo!*") + u'

boo!

\n' + >>> markdowner.convert("**boom!**") + u'

boom!

\n' + +This implementation of Markdown implements the full "core" syntax plus a +number of extras (e.g., code syntax coloring, footnotes) as described on +. +""" + +cmdln_desc = """A fast and complete Python implementation of Markdown, a +text-to-HTML conversion tool for web writers. + +Supported extra syntax options (see -x|--extras option below and +see for details): + +* code-friendly: Disable _ and __ for em and strong. +* cuddled-lists: Allow lists to be cuddled to the preceding paragraph. +* fenced-code-blocks: Allows a code block to not have to be indented + by fencing it with '```' on a line before and after. Based on + with support for + syntax highlighting. +* footnotes: Support footnotes as in use on daringfireball.net and + implemented in other Markdown processors (tho not in Markdown.pl v1.0.1). +* header-ids: Adds "id" attributes to headers. The id value is a slug of + the header text. +* html-classes: Takes a dict mapping html tag names (lowercase) to a + string to use for a "class" tag attribute. Currently only supports + "pre" and "code" tags. Add an issue if you require this for other tags. +* markdown-in-html: Allow the use of `markdown="1"` in a block HTML tag to + have markdown processing be done on its contents. Similar to + but with + some limitations. +* metadata: Extract metadata from a leading '---'-fenced block. + See for details. +* nofollow: Add `rel="nofollow"` to add `` tags with an href. See + . +* pyshell: Treats unindented Python interactive shell sessions as + blocks. +* link-patterns: Auto-link given regex patterns in text (e.g. bug number + references, revision number references). +* smarty-pants: Replaces ' and " with curly quotation marks or curly + apostrophes. Replaces --, ---, ..., and . . . with en dashes, em dashes, + and ellipses. +* toc: The returned HTML string gets a new "toc_html" attribute which is + a Table of Contents for the document. (experimental) +* xml: Passes one-liner processing instructions and namespaced XML tags. +* wiki-tables: Google Code Wiki-style tables. See + . +""" + +# Dev Notes: +# - Python's regex syntax doesn't have '\z', so I'm using '\Z'. I'm +# not yet sure if there implications with this. Compare 'pydoc sre' +# and 'perldoc perlre'. + +__version_info__ = (2, 1, 1) +__version__ = '.'.join(map(str, __version_info__)) +__author__ = "Trent Mick" + +import os +import sys +from pprint import pprint +import re +import logging +try: + from hashlib import md5 +except ImportError: + from md5 import md5 +import optparse +from random import random, randint +import codecs + + +#---- Python version compat + +try: + from urllib.parse import quote # python3 +except ImportError: + from urllib import quote # python2 + +if sys.version_info[:2] < (2,4): + from sets import Set as set + def reversed(sequence): + for i in sequence[::-1]: + yield i + +# Use `bytes` for byte strings and `unicode` for unicode strings (str in Py3). +if sys.version_info[0] <= 2: + py3 = False + try: + bytes + except NameError: + bytes = str + base_string_type = basestring +elif sys.version_info[0] >= 3: + py3 = True + unicode = str + base_string_type = str + + + +#---- globals + +DEBUG = False +log = logging.getLogger("markdown") + +DEFAULT_TAB_WIDTH = 4 + + +SECRET_SALT = bytes(randint(0, 1000000)) +def _hash_text(s): + return 'md5-' + md5(SECRET_SALT + s.encode("utf-8")).hexdigest() + +# Table of hash values for escaped characters: +g_escape_table = dict([(ch, _hash_text(ch)) + for ch in '\\`*_{}[]()>#+-.!']) + + + +#---- exceptions + +class MarkdownError(Exception): + pass + + + +#---- public api + +def markdown_path(path, encoding="utf-8", + html4tags=False, tab_width=DEFAULT_TAB_WIDTH, + safe_mode=None, extras=None, link_patterns=None, + use_file_vars=False): + fp = codecs.open(path, 'r', encoding) + text = fp.read() + fp.close() + return Markdown(html4tags=html4tags, tab_width=tab_width, + safe_mode=safe_mode, extras=extras, + link_patterns=link_patterns, + use_file_vars=use_file_vars).convert(text) + +def markdown(text, html4tags=False, tab_width=DEFAULT_TAB_WIDTH, + safe_mode=None, extras=None, link_patterns=None, + use_file_vars=False): + return Markdown(html4tags=html4tags, tab_width=tab_width, + safe_mode=safe_mode, extras=extras, + link_patterns=link_patterns, + use_file_vars=use_file_vars).convert(text) + +class Markdown(object): + # The dict of "extras" to enable in processing -- a mapping of + # extra name to argument for the extra. Most extras do not have an + # argument, in which case the value is None. + # + # This can be set via (a) subclassing and (b) the constructor + # "extras" argument. + extras = None + + urls = None + titles = None + html_blocks = None + html_spans = None + html_removed_text = "[HTML_REMOVED]" # for compat with markdown.py + + # Used to track when we're inside an ordered or unordered list + # (see _ProcessListItems() for details): + list_level = 0 + + _ws_only_line_re = re.compile(r"^[ \t]+$", re.M) + + def __init__(self, html4tags=False, tab_width=4, safe_mode=None, + extras=None, link_patterns=None, use_file_vars=False): + if html4tags: + self.empty_element_suffix = ">" + else: + self.empty_element_suffix = " />" + self.tab_width = tab_width + + # For compatibility with earlier markdown2.py and with + # markdown.py's safe_mode being a boolean, + # safe_mode == True -> "replace" + if safe_mode is True: + self.safe_mode = "replace" + else: + self.safe_mode = safe_mode + + # Massaging and building the "extras" info. + if self.extras is None: + self.extras = {} + elif not isinstance(self.extras, dict): + self.extras = dict([(e, None) for e in self.extras]) + if extras: + if not isinstance(extras, dict): + extras = dict([(e, None) for e in extras]) + self.extras.update(extras) + assert isinstance(self.extras, dict) + if "toc" in self.extras and not "header-ids" in self.extras: + self.extras["header-ids"] = None # "toc" implies "header-ids" + self._instance_extras = self.extras.copy() + + self.link_patterns = link_patterns + self.use_file_vars = use_file_vars + self._outdent_re = re.compile(r'^(\t|[ ]{1,%d})' % tab_width, re.M) + + self._escape_table = g_escape_table.copy() + if "smarty-pants" in self.extras: + self._escape_table['"'] = _hash_text('"') + self._escape_table["'"] = _hash_text("'") + + def reset(self): + self.urls = {} + self.titles = {} + self.html_blocks = {} + self.html_spans = {} + self.list_level = 0 + self.extras = self._instance_extras.copy() + if "footnotes" in self.extras: + self.footnotes = {} + self.footnote_ids = [] + if "header-ids" in self.extras: + self._count_from_header_id = {} # no `defaultdict` in Python 2.4 + if "metadata" in self.extras: + self.metadata = {} + + # Per "rel" + # should only be used in tags with an "href" attribute. + _a_nofollow = re.compile(r"<(a)([^>]*href=)", re.IGNORECASE) + + def convert(self, text): + """Convert the given text.""" + # Main function. The order in which other subs are called here is + # essential. Link and image substitutions need to happen before + # _EscapeSpecialChars(), so that any *'s or _'s in the + # and tags get encoded. + + # Clear the global hashes. If we don't clear these, you get conflicts + # from other articles when generating a page which contains more than + # one article (e.g. an index page that shows the N most recent + # articles): + self.reset() + + if not isinstance(text, unicode): + #TODO: perhaps shouldn't presume UTF-8 for string input? + text = unicode(text, 'utf-8') + + if self.use_file_vars: + # Look for emacs-style file variable hints. + emacs_vars = self._get_emacs_vars(text) + if "markdown-extras" in emacs_vars: + splitter = re.compile("[ ,]+") + for e in splitter.split(emacs_vars["markdown-extras"]): + if '=' in e: + ename, earg = e.split('=', 1) + try: + earg = int(earg) + except ValueError: + pass + else: + ename, earg = e, None + self.extras[ename] = earg + + # Standardize line endings: + text = re.sub("\r\n|\r", "\n", text) + + # Make sure $text ends with a couple of newlines: + text += "\n\n" + + # Convert all tabs to spaces. + text = self._detab(text) + + # Strip any lines consisting only of spaces and tabs. + # This makes subsequent regexen easier to write, because we can + # match consecutive blank lines with /\n+/ instead of something + # contorted like /[ \t]*\n+/ . + text = self._ws_only_line_re.sub("", text) + + # strip metadata from head and extract + if "metadata" in self.extras: + text = self._extract_metadata(text) + + text = self.preprocess(text) + + if self.safe_mode: + text = self._hash_html_spans(text) + + # Turn block-level HTML blocks into hash entries + text = self._hash_html_blocks(text, raw=True) + + # Strip link definitions, store in hashes. + if "footnotes" in self.extras: + # Must do footnotes first because an unlucky footnote defn + # looks like a link defn: + # [^4]: this "looks like a link defn" + text = self._strip_footnote_definitions(text) + text = self._strip_link_definitions(text) + + text = self._run_block_gamut(text) + + if "footnotes" in self.extras: + text = self._add_footnotes(text) + + text = self.postprocess(text) + + text = self._unescape_special_chars(text) + + if self.safe_mode: + text = self._unhash_html_spans(text) + + if "nofollow" in self.extras: + text = self._a_nofollow.sub(r'<\1 rel="nofollow"\2', text) + + text += "\n" + + rv = UnicodeWithAttrs(text) + if "toc" in self.extras: + rv._toc = self._toc + if "metadata" in self.extras: + rv.metadata = self.metadata + return rv + + def postprocess(self, text): + """A hook for subclasses to do some postprocessing of the html, if + desired. This is called before unescaping of special chars and + unhashing of raw HTML spans. + """ + return text + + def preprocess(self, text): + """A hook for subclasses to do some preprocessing of the Markdown, if + desired. This is called after basic formatting of the text, but prior + to any extras, safe mode, etc. processing. + """ + return text + + # Is metadata if the content starts with '---'-fenced `key: value` + # pairs. E.g. (indented for presentation): + # --- + # foo: bar + # another-var: blah blah + # --- + _metadata_pat = re.compile("""^---[ \t]*\n((?:[ \t]*[^ \t:]+[ \t]*:[^\n]*\n)+)---[ \t]*\n""") + + def _extract_metadata(self, text): + # fast test + if not text.startswith("---"): + return text + match = self._metadata_pat.match(text) + if not match: + return text + + tail = text[len(match.group(0)):] + metadata_str = match.group(1).strip() + for line in metadata_str.split('\n'): + key, value = line.split(':', 1) + self.metadata[key.strip()] = value.strip() + + return tail + + + _emacs_oneliner_vars_pat = re.compile(r"-\*-\s*([^\r\n]*?)\s*-\*-", re.UNICODE) + # This regular expression is intended to match blocks like this: + # PREFIX Local Variables: SUFFIX + # PREFIX mode: Tcl SUFFIX + # PREFIX End: SUFFIX + # Some notes: + # - "[ \t]" is used instead of "\s" to specifically exclude newlines + # - "(\r\n|\n|\r)" is used instead of "$" because the sre engine does + # not like anything other than Unix-style line terminators. + _emacs_local_vars_pat = re.compile(r"""^ + (?P(?:[^\r\n|\n|\r])*?) + [\ \t]*Local\ Variables:[\ \t]* + (?P.*?)(?:\r\n|\n|\r) + (?P.*?\1End:) + """, re.IGNORECASE | re.MULTILINE | re.DOTALL | re.VERBOSE) + + def _get_emacs_vars(self, text): + """Return a dictionary of emacs-style local variables. + + Parsing is done loosely according to this spec (and according to + some in-practice deviations from this): + http://www.gnu.org/software/emacs/manual/html_node/emacs/Specifying-File-Variables.html#Specifying-File-Variables + """ + emacs_vars = {} + SIZE = pow(2, 13) # 8kB + + # Search near the start for a '-*-'-style one-liner of variables. + head = text[:SIZE] + if "-*-" in head: + match = self._emacs_oneliner_vars_pat.search(head) + if match: + emacs_vars_str = match.group(1) + assert '\n' not in emacs_vars_str + emacs_var_strs = [s.strip() for s in emacs_vars_str.split(';') + if s.strip()] + if len(emacs_var_strs) == 1 and ':' not in emacs_var_strs[0]: + # While not in the spec, this form is allowed by emacs: + # -*- Tcl -*- + # where the implied "variable" is "mode". This form + # is only allowed if there are no other variables. + emacs_vars["mode"] = emacs_var_strs[0].strip() + else: + for emacs_var_str in emacs_var_strs: + try: + variable, value = emacs_var_str.strip().split(':', 1) + except ValueError: + log.debug("emacs variables error: malformed -*- " + "line: %r", emacs_var_str) + continue + # Lowercase the variable name because Emacs allows "Mode" + # or "mode" or "MoDe", etc. + emacs_vars[variable.lower()] = value.strip() + + tail = text[-SIZE:] + if "Local Variables" in tail: + match = self._emacs_local_vars_pat.search(tail) + if match: + prefix = match.group("prefix") + suffix = match.group("suffix") + lines = match.group("content").splitlines(0) + #print "prefix=%r, suffix=%r, content=%r, lines: %s"\ + # % (prefix, suffix, match.group("content"), lines) + + # Validate the Local Variables block: proper prefix and suffix + # usage. + for i, line in enumerate(lines): + if not line.startswith(prefix): + log.debug("emacs variables error: line '%s' " + "does not use proper prefix '%s'" + % (line, prefix)) + return {} + # Don't validate suffix on last line. Emacs doesn't care, + # neither should we. + if i != len(lines)-1 and not line.endswith(suffix): + log.debug("emacs variables error: line '%s' " + "does not use proper suffix '%s'" + % (line, suffix)) + return {} + + # Parse out one emacs var per line. + continued_for = None + for line in lines[:-1]: # no var on the last line ("PREFIX End:") + if prefix: line = line[len(prefix):] # strip prefix + if suffix: line = line[:-len(suffix)] # strip suffix + line = line.strip() + if continued_for: + variable = continued_for + if line.endswith('\\'): + line = line[:-1].rstrip() + else: + continued_for = None + emacs_vars[variable] += ' ' + line + else: + try: + variable, value = line.split(':', 1) + except ValueError: + log.debug("local variables error: missing colon " + "in local variables entry: '%s'" % line) + continue + # Do NOT lowercase the variable name, because Emacs only + # allows "mode" (and not "Mode", "MoDe", etc.) in this block. + value = value.strip() + if value.endswith('\\'): + value = value[:-1].rstrip() + continued_for = variable + else: + continued_for = None + emacs_vars[variable] = value + + # Unquote values. + for var, val in list(emacs_vars.items()): + if len(val) > 1 and (val.startswith('"') and val.endswith('"') + or val.startswith('"') and val.endswith('"')): + emacs_vars[var] = val[1:-1] + + return emacs_vars + + # Cribbed from a post by Bart Lateur: + # + _detab_re = re.compile(r'(.*?)\t', re.M) + def _detab_sub(self, match): + g1 = match.group(1) + return g1 + (' ' * (self.tab_width - len(g1) % self.tab_width)) + def _detab(self, text): + r"""Remove (leading?) tabs from a file. + + >>> m = Markdown() + >>> m._detab("\tfoo") + ' foo' + >>> m._detab(" \tfoo") + ' foo' + >>> m._detab("\t foo") + ' foo' + >>> m._detab(" foo") + ' foo' + >>> m._detab(" foo\n\tbar\tblam") + ' foo\n bar blam' + """ + if '\t' not in text: + return text + return self._detab_re.subn(self._detab_sub, text)[0] + + # I broke out the html5 tags here and add them to _block_tags_a and + # _block_tags_b. This way html5 tags are easy to keep track of. + _html5tags = '|article|aside|header|hgroup|footer|nav|section|figure|figcaption' + + _block_tags_a = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del' + _block_tags_a += _html5tags + + _strict_tag_block_re = re.compile(r""" + ( # save in \1 + ^ # start of line (with re.M) + <(%s) # start tag = \2 + \b # word break + (.*\n)*? # any number of lines, minimally matching + # the matching end tag + [ \t]* # trailing spaces/tabs + (?=\n+|\Z) # followed by a newline or end of document + ) + """ % _block_tags_a, + re.X | re.M) + + _block_tags_b = 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math' + _block_tags_b += _html5tags + + _liberal_tag_block_re = re.compile(r""" + ( # save in \1 + ^ # start of line (with re.M) + <(%s) # start tag = \2 + \b # word break + (.*\n)*? # any number of lines, minimally matching + .* # the matching end tag + [ \t]* # trailing spaces/tabs + (?=\n+|\Z) # followed by a newline or end of document + ) + """ % _block_tags_b, + re.X | re.M) + + _html_markdown_attr_re = re.compile( + r'''\s+markdown=("1"|'1')''') + def _hash_html_block_sub(self, match, raw=False): + html = match.group(1) + if raw and self.safe_mode: + html = self._sanitize_html(html) + elif 'markdown-in-html' in self.extras and 'markdown=' in html: + first_line = html.split('\n', 1)[0] + m = self._html_markdown_attr_re.search(first_line) + if m: + lines = html.split('\n') + middle = '\n'.join(lines[1:-1]) + last_line = lines[-1] + first_line = first_line[:m.start()] + first_line[m.end():] + f_key = _hash_text(first_line) + self.html_blocks[f_key] = first_line + l_key = _hash_text(last_line) + self.html_blocks[l_key] = last_line + return ''.join(["\n\n", f_key, + "\n\n", middle, "\n\n", + l_key, "\n\n"]) + key = _hash_text(html) + self.html_blocks[key] = html + return "\n\n" + key + "\n\n" + + def _hash_html_blocks(self, text, raw=False): + """Hashify HTML blocks + + We only want to do this for block-level HTML tags, such as headers, + lists, and tables. That's because we still want to wrap

s around + "paragraphs" that are wrapped in non-block-level tags, such as anchors, + phrase emphasis, and spans. The list of tags we're looking for is + hard-coded. + + @param raw {boolean} indicates if these are raw HTML blocks in + the original source. It makes a difference in "safe" mode. + """ + if '<' not in text: + return text + + # Pass `raw` value into our calls to self._hash_html_block_sub. + hash_html_block_sub = _curry(self._hash_html_block_sub, raw=raw) + + # First, look for nested blocks, e.g.: + #

+ #
+ # tags for inner block must be indented. + #
+ #
+ # + # The outermost tags must start at the left margin for this to match, and + # the inner nested divs must be indented. + # We need to do this before the next, more liberal match, because the next + # match will start at the first `
` and stop at the first `
`. + text = self._strict_tag_block_re.sub(hash_html_block_sub, text) + + # Now match more liberally, simply from `\n` to `\n` + text = self._liberal_tag_block_re.sub(hash_html_block_sub, text) + + # Special case just for
. It was easier to make a special + # case than to make the other regex more complicated. + if "", start_idx) + 3 + except ValueError: + break + + # Start position for next comment block search. + start = end_idx + + # Validate whitespace before comment. + if start_idx: + # - Up to `tab_width - 1` spaces before start_idx. + for i in range(self.tab_width - 1): + if text[start_idx - 1] != ' ': + break + start_idx -= 1 + if start_idx == 0: + break + # - Must be preceded by 2 newlines or hit the start of + # the document. + if start_idx == 0: + pass + elif start_idx == 1 and text[0] == '\n': + start_idx = 0 # to match minute detail of Markdown.pl regex + elif text[start_idx-2:start_idx] == '\n\n': + pass + else: + break + + # Validate whitespace after comment. + # - Any number of spaces and tabs. + while end_idx < len(text): + if text[end_idx] not in ' \t': + break + end_idx += 1 + # - Must be following by 2 newlines or hit end of text. + if text[end_idx:end_idx+2] not in ('', '\n', '\n\n'): + continue + + # Escape and hash (must match `_hash_html_block_sub`). + html = text[start_idx:end_idx] + if raw and self.safe_mode: + html = self._sanitize_html(html) + key = _hash_text(html) + self.html_blocks[key] = html + text = text[:start_idx] + "\n\n" + key + "\n\n" + text[end_idx:] + + if "xml" in self.extras: + # Treat XML processing instructions and namespaced one-liner + # tags as if they were block HTML tags. E.g., if standalone + # (i.e. are their own paragraph), the following do not get + # wrapped in a

tag: + # + # + # + _xml_oneliner_re = _xml_oneliner_re_from_tab_width(self.tab_width) + text = _xml_oneliner_re.sub(hash_html_block_sub, text) + + return text + + def _strip_link_definitions(self, text): + # Strips link definitions from text, stores the URLs and titles in + # hash references. + less_than_tab = self.tab_width - 1 + + # Link defs are in the form: + # [id]: url "optional title" + _link_def_re = re.compile(r""" + ^[ ]{0,%d}\[(.+)\]: # id = \1 + [ \t]* + \n? # maybe *one* newline + [ \t]* + ? # url = \2 + [ \t]* + (?: + \n? # maybe one newline + [ \t]* + (?<=\s) # lookbehind for whitespace + ['"(] + ([^\n]*) # title = \3 + ['")] + [ \t]* + )? # title is optional + (?:\n+|\Z) + """ % less_than_tab, re.X | re.M | re.U) + return _link_def_re.sub(self._extract_link_def_sub, text) + + def _extract_link_def_sub(self, match): + id, url, title = match.groups() + key = id.lower() # Link IDs are case-insensitive + self.urls[key] = self._encode_amps_and_angles(url) + if title: + self.titles[key] = title + return "" + + def _extract_footnote_def_sub(self, match): + id, text = match.groups() + text = _dedent(text, skip_first_line=not text.startswith('\n')).strip() + normed_id = re.sub(r'\W', '-', id) + # Ensure footnote text ends with a couple newlines (for some + # block gamut matches). + self.footnotes[normed_id] = text + "\n\n" + return "" + + def _strip_footnote_definitions(self, text): + """A footnote definition looks like this: + + [^note-id]: Text of the note. + + May include one or more indented paragraphs. + + Where, + - The 'note-id' can be pretty much anything, though typically it + is the number of the footnote. + - The first paragraph may start on the next line, like so: + + [^note-id]: + Text of the note. + """ + less_than_tab = self.tab_width - 1 + footnote_def_re = re.compile(r''' + ^[ ]{0,%d}\[\^(.+)\]: # id = \1 + [ \t]* + ( # footnote text = \2 + # First line need not start with the spaces. + (?:\s*.*\n+) + (?: + (?:[ ]{%d} | \t) # Subsequent lines must be indented. + .*\n+ + )* + ) + # Lookahead for non-space at line-start, or end of doc. + (?:(?=^[ ]{0,%d}\S)|\Z) + ''' % (less_than_tab, self.tab_width, self.tab_width), + re.X | re.M) + return footnote_def_re.sub(self._extract_footnote_def_sub, text) + + + _hr_data = [ + ('*', re.compile(r"^[ ]{0,3}\*(.*?)$", re.M)), + ('-', re.compile(r"^[ ]{0,3}\-(.*?)$", re.M)), + ('_', re.compile(r"^[ ]{0,3}\_(.*?)$", re.M)), + ] + + def _run_block_gamut(self, text): + # These are all the transformations that form block-level + # tags like paragraphs, headers, and list items. + + if "fenced-code-blocks" in self.extras: + text = self._do_fenced_code_blocks(text) + + text = self._do_headers(text) + + # Do Horizontal Rules: + # On the number of spaces in horizontal rules: The spec is fuzzy: "If + # you wish, you may use spaces between the hyphens or asterisks." + # Markdown.pl 1.0.1's hr regexes limit the number of spaces between the + # hr chars to one or two. We'll reproduce that limit here. + hr = "\n tags around block-level tags. + text = self._hash_html_blocks(text) + + text = self._form_paragraphs(text) + + return text + + def _pyshell_block_sub(self, match): + lines = match.group(0).splitlines(0) + _dedentlines(lines) + indent = ' ' * self.tab_width + s = ('\n' # separate from possible cuddled paragraph + + indent + ('\n'+indent).join(lines) + + '\n\n') + return s + + def _prepare_pyshell_blocks(self, text): + """Ensure that Python interactive shell sessions are put in + code blocks -- even if not properly indented. + """ + if ">>>" not in text: + return text + + less_than_tab = self.tab_width - 1 + _pyshell_block_re = re.compile(r""" + ^([ ]{0,%d})>>>[ ].*\n # first line + ^(\1.*\S+.*\n)* # any number of subsequent lines + ^\n # ends with a blank line + """ % less_than_tab, re.M | re.X) + + return _pyshell_block_re.sub(self._pyshell_block_sub, text) + + def _wiki_table_sub(self, match): + ttext = match.group(0).strip() + #print 'wiki table: %r' % match.group(0) + rows = [] + for line in ttext.splitlines(0): + line = line.strip()[2:-2].strip() + row = [c.strip() for c in re.split(r'(?', '

'] + for row in rows: + hrow = [''] + for cell in row: + hrow.append('') + hrow.append('') + hlines.append(''.join(hrow)) + hlines += ['', '
') + hrow.append(self._run_span_gamut(cell)) + hrow.append('
'] + return '\n'.join(hlines) + '\n' + + def _do_wiki_tables(self, text): + # Optimization. + if "||" not in text: + return text + + less_than_tab = self.tab_width - 1 + wiki_table_re = re.compile(r''' + (?:(?<=\n\n)|\A\n?) # leading blank line + ^([ ]{0,%d})\|\|.+?\|\|[ ]*\n # first line + (^\1\|\|.+?\|\|\n)* # any number of subsequent lines + ''' % less_than_tab, re.M | re.X) + return wiki_table_re.sub(self._wiki_table_sub, text) + + def _run_span_gamut(self, text): + # These are all the transformations that occur *within* block-level + # tags like paragraphs, headers, and list items. + + text = self._do_code_spans(text) + + text = self._escape_special_chars(text) + + # Process anchor and image tags. + text = self._do_links(text) + + # Make links out of things like `` + # Must come after _do_links(), because you can use < and > + # delimiters in inline links like [this](). + text = self._do_auto_links(text) + + if "link-patterns" in self.extras: + text = self._do_link_patterns(text) + + text = self._encode_amps_and_angles(text) + + text = self._do_italics_and_bold(text) + + if "smarty-pants" in self.extras: + text = self._do_smart_punctuation(text) + + # Do hard breaks: + text = re.sub(r" {2,}\n", " + | + # auto-link (e.g., ) + <\w+[^>]*> + | + # comment + | + <\?.*?\?> # processing instruction + ) + """, re.X) + + def _escape_special_chars(self, text): + # Python markdown note: the HTML tokenization here differs from + # that in Markdown.pl, hence the behaviour for subtle cases can + # differ (I believe the tokenizer here does a better job because + # it isn't susceptible to unmatched '<' and '>' in HTML tags). + # Note, however, that '>' is not allowed in an auto-link URL + # here. + escaped = [] + is_html_markup = False + for token in self._sorta_html_tokenize_re.split(text): + if is_html_markup: + # Within tags/HTML-comments/auto-links, encode * and _ + # so they don't conflict with their use in Markdown for + # italics and strong. We're replacing each such + # character with its corresponding MD5 checksum value; + # this is likely overkill, but it should prevent us from + # colliding with the escape values by accident. + escaped.append(token.replace('*', self._escape_table['*']) + .replace('_', self._escape_table['_'])) + else: + escaped.append(self._encode_backslash_escapes(token)) + is_html_markup = not is_html_markup + return ''.join(escaped) + + def _hash_html_spans(self, text): + # Used for safe_mode. + + def _is_auto_link(s): + if ':' in s and self._auto_link_re.match(s): + return True + elif '@' in s and self._auto_email_link_re.match(s): + return True + return False + + tokens = [] + is_html_markup = False + for token in self._sorta_html_tokenize_re.split(text): + if is_html_markup and not _is_auto_link(token): + sanitized = self._sanitize_html(token) + key = _hash_text(sanitized) + self.html_spans[key] = sanitized + tokens.append(key) + else: + tokens.append(token) + is_html_markup = not is_html_markup + return ''.join(tokens) + + def _unhash_html_spans(self, text): + for key, sanitized in list(self.html_spans.items()): + text = text.replace(key, sanitized) + return text + + def _sanitize_html(self, s): + if self.safe_mode == "replace": + return self.html_removed_text + elif self.safe_mode == "escape": + replacements = [ + ('&', '&'), + ('<', '<'), + ('>', '>'), + ] + for before, after in replacements: + s = s.replace(before, after) + return s + else: + raise MarkdownError("invalid value for 'safe_mode': %r (must be " + "'escape' or 'replace')" % self.safe_mode) + + _tail_of_inline_link_re = re.compile(r''' + # Match tail of: [text](/url/) or [text](/url/ "title") + \( # literal paren + [ \t]* + (?P # \1 + <.*?> + | + .*? + ) + [ \t]* + ( # \2 + (['"]) # quote char = \3 + (?P.*?) + \3 # matching quote + )? # title is optional + \) + ''', re.X | re.S) + _tail_of_reference_link_re = re.compile(r''' + # Match tail of: [text][id] + [ ]? # one optional space + (?:\n[ ]*)? # one optional newline followed by spaces + \[ + (?P<id>.*?) + \] + ''', re.X | re.S) + + def _do_links(self, text): + """Turn Markdown link shortcuts into XHTML <a> and <img> tags. + + This is a combination of Markdown.pl's _DoAnchors() and + _DoImages(). They are done together because that simplified the + approach. It was necessary to use a different approach than + Markdown.pl because of the lack of atomic matching support in + Python's regex engine used in $g_nested_brackets. + """ + MAX_LINK_TEXT_SENTINEL = 3000 # markdown2 issue 24 + + # `anchor_allowed_pos` is used to support img links inside + # anchors, but not anchors inside anchors. An anchor's start + # pos must be `>= anchor_allowed_pos`. + anchor_allowed_pos = 0 + + curr_pos = 0 + while True: # Handle the next link. + # The next '[' is the start of: + # - an inline anchor: [text](url "title") + # - a reference anchor: [text][id] + # - an inline img: ![text](url "title") + # - a reference img: ![text][id] + # - a footnote ref: [^id] + # (Only if 'footnotes' extra enabled) + # - a footnote defn: [^id]: ... + # (Only if 'footnotes' extra enabled) These have already + # been stripped in _strip_footnote_definitions() so no + # need to watch for them. + # - a link definition: [id]: url "title" + # These have already been stripped in + # _strip_link_definitions() so no need to watch for them. + # - not markup: [...anything else... + try: + start_idx = text.index('[', curr_pos) + except ValueError: + break + text_length = len(text) + + # Find the matching closing ']'. + # Markdown.pl allows *matching* brackets in link text so we + # will here too. Markdown.pl *doesn't* currently allow + # matching brackets in img alt text -- we'll differ in that + # regard. + bracket_depth = 0 + for p in range(start_idx+1, min(start_idx+MAX_LINK_TEXT_SENTINEL, + text_length)): + ch = text[p] + if ch == ']': + bracket_depth -= 1 + if bracket_depth < 0: + break + elif ch == '[': + bracket_depth += 1 + else: + # Closing bracket not found within sentinel length. + # This isn't markup. + curr_pos = start_idx + 1 + continue + link_text = text[start_idx+1:p] + + # Possibly a footnote ref? + if "footnotes" in self.extras and link_text.startswith("^"): + normed_id = re.sub(r'\W', '-', link_text[1:]) + if normed_id in self.footnotes: + self.footnote_ids.append(normed_id) + result = '<sup class="footnote-ref" id="fnref-%s">' \ + '<a href="#fn-%s">%s</a></sup>' \ + % (normed_id, normed_id, len(self.footnote_ids)) + text = text[:start_idx] + result + text[p+1:] + else: + # This id isn't defined, leave the markup alone. + curr_pos = p+1 + continue + + # Now determine what this is by the remainder. + p += 1 + if p == text_length: + return text + + # Inline anchor or img? + if text[p] == '(': # attempt at perf improvement + match = self._tail_of_inline_link_re.match(text, p) + if match: + # Handle an inline anchor or img. + is_img = start_idx > 0 and text[start_idx-1] == "!" + if is_img: + start_idx -= 1 + + url, title = match.group("url"), match.group("title") + if url and url[0] == '<': + url = url[1:-1] # '<url>' -> 'url' + # We've got to encode these to avoid conflicting + # with italics/bold. + url = url.replace('*', self._escape_table['*']) \ + .replace('_', self._escape_table['_']) + if title: + title_str = ' title="%s"' % ( + _xml_escape_attr(title) + .replace('*', self._escape_table['*']) + .replace('_', self._escape_table['_'])) + else: + title_str = '' + if is_img: + result = '<img src="%s" alt="%s"%s%s' \ + % (url.replace('"', '"'), + _xml_escape_attr(link_text), + title_str, self.empty_element_suffix) + if "smarty-pants" in self.extras: + result = result.replace('"', self._escape_table['"']) + curr_pos = start_idx + len(result) + text = text[:start_idx] + result + text[match.end():] + elif start_idx >= anchor_allowed_pos: + result_head = '<a href="%s"%s>' % (url, title_str) + result = '%s%s</a>' % (result_head, link_text) + if "smarty-pants" in self.extras: + result = result.replace('"', self._escape_table['"']) + # <img> allowed from curr_pos on, <a> from + # anchor_allowed_pos on. + curr_pos = start_idx + len(result_head) + anchor_allowed_pos = start_idx + len(result) + text = text[:start_idx] + result + text[match.end():] + else: + # Anchor not allowed here. + curr_pos = start_idx + 1 + continue + + # Reference anchor or img? + else: + match = self._tail_of_reference_link_re.match(text, p) + if match: + # Handle a reference-style anchor or img. + is_img = start_idx > 0 and text[start_idx-1] == "!" + if is_img: + start_idx -= 1 + link_id = match.group("id").lower() + if not link_id: + link_id = link_text.lower() # for links like [this][] + if link_id in self.urls: + url = self.urls[link_id] + # We've got to encode these to avoid conflicting + # with italics/bold. + url = url.replace('*', self._escape_table['*']) \ + .replace('_', self._escape_table['_']) + title = self.titles.get(link_id) + if title: + before = title + title = _xml_escape_attr(title) \ + .replace('*', self._escape_table['*']) \ + .replace('_', self._escape_table['_']) + title_str = ' title="%s"' % title + else: + title_str = '' + if is_img: + result = '<img src="%s" alt="%s"%s%s' \ + % (url.replace('"', '"'), + link_text.replace('"', '"'), + title_str, self.empty_element_suffix) + if "smarty-pants" in self.extras: + result = result.replace('"', self._escape_table['"']) + curr_pos = start_idx + len(result) + text = text[:start_idx] + result + text[match.end():] + elif start_idx >= anchor_allowed_pos: + result = '<a href="%s"%s>%s</a>' \ + % (url, title_str, link_text) + result_head = '<a href="%s"%s>' % (url, title_str) + result = '%s%s</a>' % (result_head, link_text) + if "smarty-pants" in self.extras: + result = result.replace('"', self._escape_table['"']) + # <img> allowed from curr_pos on, <a> from + # anchor_allowed_pos on. + curr_pos = start_idx + len(result_head) + anchor_allowed_pos = start_idx + len(result) + text = text[:start_idx] + result + text[match.end():] + else: + # Anchor not allowed here. + curr_pos = start_idx + 1 + else: + # This id isn't defined, leave the markup alone. + curr_pos = match.end() + continue + + # Otherwise, it isn't markup. + curr_pos = start_idx + 1 + + return text + + def header_id_from_text(self, text, prefix, n): + """Generate a header id attribute value from the given header + HTML content. + + This is only called if the "header-ids" extra is enabled. + Subclasses may override this for different header ids. + + @param text {str} The text of the header tag + @param prefix {str} The requested prefix for header ids. This is the + value of the "header-ids" extra key, if any. Otherwise, None. + @param n {int} The <hN> tag number, i.e. `1` for an <h1> tag. + @returns {str} The value for the header tag's "id" attribute. Return + None to not have an id attribute and to exclude this header from + the TOC (if the "toc" extra is specified). + """ + header_id = _slugify(text) + if prefix and isinstance(prefix, base_string_type): + header_id = prefix + '-' + header_id + if header_id in self._count_from_header_id: + self._count_from_header_id[header_id] += 1 + header_id += '-%s' % self._count_from_header_id[header_id] + else: + self._count_from_header_id[header_id] = 1 + return header_id + + _toc = None + def _toc_add_entry(self, level, id, name): + if self._toc is None: + self._toc = [] + self._toc.append((level, id, self._unescape_special_chars(name))) + + _setext_h_re = re.compile(r'^(.+)[ \t]*\n(=+|-+)[ \t]*\n+', re.M) + def _setext_h_sub(self, match): + n = {"=": 1, "-": 2}[match.group(2)[0]] + demote_headers = self.extras.get("demote-headers") + if demote_headers: + n = min(n + demote_headers, 6) + header_id_attr = "" + if "header-ids" in self.extras: + header_id = self.header_id_from_text(match.group(1), + self.extras["header-ids"], n) + if header_id: + header_id_attr = ' id="%s"' % header_id + html = self._run_span_gamut(match.group(1)) + if "toc" in self.extras and header_id: + self._toc_add_entry(n, header_id, html) + return "<h%d%s>%s</h%d>\n\n" % (n, header_id_attr, html, n) + + _atx_h_re = re.compile(r''' + ^(\#{1,6}) # \1 = string of #'s + [ \t]+ + (.+?) # \2 = Header text + [ \t]* + (?<!\\) # ensure not an escaped trailing '#' + \#* # optional closing #'s (not counted) + \n+ + ''', re.X | re.M) + def _atx_h_sub(self, match): + n = len(match.group(1)) + demote_headers = self.extras.get("demote-headers") + if demote_headers: + n = min(n + demote_headers, 6) + header_id_attr = "" + if "header-ids" in self.extras: + header_id = self.header_id_from_text(match.group(2), + self.extras["header-ids"], n) + if header_id: + header_id_attr = ' id="%s"' % header_id + html = self._run_span_gamut(match.group(2)) + if "toc" in self.extras and header_id: + self._toc_add_entry(n, header_id, html) + return "<h%d%s>%s</h%d>\n\n" % (n, header_id_attr, html, n) + + def _do_headers(self, text): + # Setext-style headers: + # Header 1 + # ======== + # + # Header 2 + # -------- + text = self._setext_h_re.sub(self._setext_h_sub, text) + + # atx-style headers: + # # Header 1 + # ## Header 2 + # ## Header 2 with closing hashes ## + # ... + # ###### Header 6 + text = self._atx_h_re.sub(self._atx_h_sub, text) + + return text + + + _marker_ul_chars = '*+-' + _marker_any = r'(?:[%s]|\d+\.)' % _marker_ul_chars + _marker_ul = '(?:[%s])' % _marker_ul_chars + _marker_ol = r'(?:\d+\.)' + + def _list_sub(self, match): + lst = match.group(1) + lst_type = match.group(3) in self._marker_ul_chars and "ul" or "ol" + result = self._process_list_items(lst) + if self.list_level: + return "<%s>\n%s</%s>\n" % (lst_type, result, lst_type) + else: + return "<%s>\n%s</%s>\n\n" % (lst_type, result, lst_type) + + def _do_lists(self, text): + # Form HTML ordered (numbered) and unordered (bulleted) lists. + + # Iterate over each *non-overlapping* list match. + pos = 0 + while True: + # Find the *first* hit for either list style (ul or ol). We + # match ul and ol separately to avoid adjacent lists of different + # types running into each other (see issue #16). + hits = [] + for marker_pat in (self._marker_ul, self._marker_ol): + less_than_tab = self.tab_width - 1 + whole_list = r''' + ( # \1 = whole list + ( # \2 + [ ]{0,%d} + (%s) # \3 = first list item marker + [ \t]+ + (?!\ *\3\ ) # '- - - ...' isn't a list. See 'not_quite_a_list' test case. + ) + (?:.+?) + ( # \4 + \Z + | + \n{2,} + (?=\S) + (?! # Negative lookahead for another list item marker + [ \t]* + %s[ \t]+ + ) + ) + ) + ''' % (less_than_tab, marker_pat, marker_pat) + if self.list_level: # sub-list + list_re = re.compile("^"+whole_list, re.X | re.M | re.S) + else: + list_re = re.compile(r"(?:(?<=\n\n)|\A\n?)"+whole_list, + re.X | re.M | re.S) + match = list_re.search(text, pos) + if match: + hits.append((match.start(), match)) + if not hits: + break + hits.sort() + match = hits[0][1] + start, end = match.span() + text = text[:start] + self._list_sub(match) + text[end:] + pos = end + + return text + + _list_item_re = re.compile(r''' + (\n)? # leading line = \1 + (^[ \t]*) # leading whitespace = \2 + (?P<marker>%s) [ \t]+ # list marker = \3 + ((?:.+?) # list item text = \4 + (\n{1,2})) # eols = \5 + (?= \n* (\Z | \2 (?P<next_marker>%s) [ \t]+)) + ''' % (_marker_any, _marker_any), + re.M | re.X | re.S) + + _last_li_endswith_two_eols = False + def _list_item_sub(self, match): + item = match.group(4) + leading_line = match.group(1) + leading_space = match.group(2) + if leading_line or "\n\n" in item or self._last_li_endswith_two_eols: + item = self._run_block_gamut(self._outdent(item)) + else: + # Recursion for sub-lists: + item = self._do_lists(self._outdent(item)) + if item.endswith('\n'): + item = item[:-1] + item = self._run_span_gamut(item) + self._last_li_endswith_two_eols = (len(match.group(5)) == 2) + return "<li>%s</li>\n" % item + + def _process_list_items(self, list_str): + # Process the contents of a single ordered or unordered list, + # splitting it into individual list items. + + # The $g_list_level global keeps track of when we're inside a list. + # Each time we enter a list, we increment it; when we leave a list, + # we decrement. If it's zero, we're not in a list anymore. + # + # We do this because when we're not inside a list, we want to treat + # something like this: + # + # I recommend upgrading to version + # 8. Oops, now this line is treated + # as a sub-list. + # + # As a single paragraph, despite the fact that the second line starts + # with a digit-period-space sequence. + # + # Whereas when we're inside a list (or sub-list), that line will be + # treated as the start of a sub-list. What a kludge, huh? This is + # an aspect of Markdown's syntax that's hard to parse perfectly + # without resorting to mind-reading. Perhaps the solution is to + # change the syntax rules such that sub-lists must start with a + # starting cardinal number; e.g. "1." or "a.". + self.list_level += 1 + self._last_li_endswith_two_eols = False + list_str = list_str.rstrip('\n') + '\n' + list_str = self._list_item_re.sub(self._list_item_sub, list_str) + self.list_level -= 1 + return list_str + + def _get_pygments_lexer(self, lexer_name): + try: + from pygments import lexers, util + except ImportError: + return None + try: + return lexers.get_lexer_by_name(lexer_name) + except util.ClassNotFound: + return None + + def _color_with_pygments(self, codeblock, lexer, **formatter_opts): + import pygments + import pygments.formatters + + class HtmlCodeFormatter(pygments.formatters.HtmlFormatter): + def _wrap_code(self, inner): + """A function for use in a Pygments Formatter which + wraps in <code> tags. + """ + yield 0, "<code>" + for tup in inner: + yield tup + yield 0, "</code>" + + def wrap(self, source, outfile): + """Return the source with a code, pre, and div.""" + return self._wrap_div(self._wrap_pre(self._wrap_code(source))) + + formatter_opts.setdefault("cssclass", "codehilite") + formatter = HtmlCodeFormatter(**formatter_opts) + return pygments.highlight(codeblock, lexer, formatter) + + def _code_block_sub(self, match, is_fenced_code_block=False): + lexer_name = None + if is_fenced_code_block: + lexer_name = match.group(1) + if lexer_name: + formatter_opts = self.extras['fenced-code-blocks'] or {} + codeblock = match.group(2) + codeblock = codeblock[:-1] # drop one trailing newline + else: + codeblock = match.group(1) + codeblock = self._outdent(codeblock) + codeblock = self._detab(codeblock) + codeblock = codeblock.lstrip('\n') # trim leading newlines + codeblock = codeblock.rstrip() # trim trailing whitespace + + # Note: "code-color" extra is DEPRECATED. + if "code-color" in self.extras and codeblock.startswith(":::"): + lexer_name, rest = codeblock.split('\n', 1) + lexer_name = lexer_name[3:].strip() + codeblock = rest.lstrip("\n") # Remove lexer declaration line. + formatter_opts = self.extras['code-color'] or {} + + if lexer_name: + lexer = self._get_pygments_lexer(lexer_name) + if lexer: + colored = self._color_with_pygments(codeblock, lexer, + **formatter_opts) + return "\n\n%s\n\n" % colored + + codeblock = self._encode_code(codeblock) + pre_class_str = self._html_class_str_from_tag("pre") + code_class_str = self._html_class_str_from_tag("code") + return "\n\n<pre%s><code%s>%s\n</code></pre>\n\n" % ( + pre_class_str, code_class_str, codeblock) + + def _html_class_str_from_tag(self, tag): + """Get the appropriate ' class="..."' string (note the leading + space), if any, for the given tag. + """ + if "html-classes" not in self.extras: + return "" + try: + html_classes_from_tag = self.extras["html-classes"] + except TypeError: + return "" + else: + if tag in html_classes_from_tag: + return ' class="%s"' % html_classes_from_tag[tag] + return "" + + def _do_code_blocks(self, text): + """Process Markdown `<pre><code>` blocks.""" + code_block_re = re.compile(r''' + (?:\n\n|\A\n?) + ( # $1 = the code block -- one or more lines, starting with a space/tab + (?: + (?:[ ]{%d} | \t) # Lines must start with a tab or a tab-width of spaces + .*\n+ + )+ + ) + ((?=^[ ]{0,%d}\S)|\Z) # Lookahead for non-space at line-start, or end of doc + ''' % (self.tab_width, self.tab_width), + re.M | re.X) + return code_block_re.sub(self._code_block_sub, text) + + _fenced_code_block_re = re.compile(r''' + (?:\n\n|\A\n?) + ^```([\w+-]+)?[ \t]*\n # opening fence, $1 = optional lang + (.*?) # $2 = code block content + ^```[ \t]*\n # closing fence + ''', re.M | re.X | re.S) + + def _fenced_code_block_sub(self, match): + return self._code_block_sub(match, is_fenced_code_block=True); + + def _do_fenced_code_blocks(self, text): + """Process ```-fenced unindented code blocks ('fenced-code-blocks' extra).""" + return self._fenced_code_block_re.sub(self._fenced_code_block_sub, text) + + # Rules for a code span: + # - backslash escapes are not interpreted in a code span + # - to include one or or a run of more backticks the delimiters must + # be a longer run of backticks + # - cannot start or end a code span with a backtick; pad with a + # space and that space will be removed in the emitted HTML + # See `test/tm-cases/escapes.text` for a number of edge-case + # examples. + _code_span_re = re.compile(r''' + (?<!\\) + (`+) # \1 = Opening run of ` + (?!`) # See Note A test/tm-cases/escapes.text + (.+?) # \2 = The code block + (?<!`) + \1 # Matching closer + (?!`) + ''', re.X | re.S) + + def _code_span_sub(self, match): + c = match.group(2).strip(" \t") + c = self._encode_code(c) + return "<code>%s</code>" % c + + def _do_code_spans(self, text): + # * Backtick quotes are used for <code></code> spans. + # + # * You can use multiple backticks as the delimiters if you want to + # include literal backticks in the code span. So, this input: + # + # Just type ``foo `bar` baz`` at the prompt. + # + # Will translate to: + # + # <p>Just type <code>foo `bar` baz</code> at the prompt.</p> + # + # There's no arbitrary limit to the number of backticks you + # can use as delimters. If you need three consecutive backticks + # in your code, use four for delimiters, etc. + # + # * You can use spaces to get literal backticks at the edges: + # + # ... type `` `bar` `` ... + # + # Turns to: + # + # ... type <code>`bar`</code> ... + return self._code_span_re.sub(self._code_span_sub, text) + + def _encode_code(self, text): + """Encode/escape certain characters inside Markdown code runs. + The point is that in code, these characters are literals, + and lose their special Markdown meanings. + """ + replacements = [ + # Encode all ampersands; HTML entities are not + # entities within a Markdown code span. + ('&', '&'), + # Do the angle bracket song and dance: + ('<', '<'), + ('>', '>'), + ] + for before, after in replacements: + text = text.replace(before, after) + hashed = _hash_text(text) + self._escape_table[text] = hashed + return hashed + + _strong_re = re.compile(r"(\*\*|__)(?=\S)(.+?[*_]*)(?<=\S)\1", re.S) + _em_re = re.compile(r"(\*|_)(?=\S)(.+?)(?<=\S)\1", re.S) + _code_friendly_strong_re = re.compile(r"\*\*(?=\S)(.+?[*_]*)(?<=\S)\*\*", re.S) + _code_friendly_em_re = re.compile(r"\*(?=\S)(.+?)(?<=\S)\*", re.S) + def _do_italics_and_bold(self, text): + # <strong> must go first: + if "code-friendly" in self.extras: + text = self._code_friendly_strong_re.sub(r"<strong>\1</strong>", text) + text = self._code_friendly_em_re.sub(r"<em>\1</em>", text) + else: + text = self._strong_re.sub(r"<strong>\2</strong>", text) + text = self._em_re.sub(r"<em>\2</em>", text) + return text + + # "smarty-pants" extra: Very liberal in interpreting a single prime as an + # apostrophe; e.g. ignores the fact that "round", "bout", "twer", and + # "twixt" can be written without an initial apostrophe. This is fine because + # using scare quotes (single quotation marks) is rare. + _apostrophe_year_re = re.compile(r"'(\d\d)(?=(\s|,|;|\.|\?|!|$))") + _contractions = ["tis", "twas", "twer", "neath", "o", "n", + "round", "bout", "twixt", "nuff", "fraid", "sup"] + def _do_smart_contractions(self, text): + text = self._apostrophe_year_re.sub(r"’\1", text) + for c in self._contractions: + text = text.replace("'%s" % c, "’%s" % c) + text = text.replace("'%s" % c.capitalize(), + "’%s" % c.capitalize()) + return text + + # Substitute double-quotes before single-quotes. + _opening_single_quote_re = re.compile(r"(?<!\S)'(?=\S)") + _opening_double_quote_re = re.compile(r'(?<!\S)"(?=\S)') + _closing_single_quote_re = re.compile(r"(?<=\S)'") + _closing_double_quote_re = re.compile(r'(?<=\S)"(?=(\s|,|;|\.|\?|!|$))') + def _do_smart_punctuation(self, text): + """Fancifies 'single quotes', "double quotes", and apostrophes. + Converts --, ---, and ... into en dashes, em dashes, and ellipses. + + Inspiration is: <http://daringfireball.net/projects/smartypants/> + See "test/tm-cases/smarty_pants.text" for a full discussion of the + support here and + <http://code.google.com/p/python-markdown2/issues/detail?id=42> for a + discussion of some diversion from the original SmartyPants. + """ + if "'" in text: # guard for perf + text = self._do_smart_contractions(text) + text = self._opening_single_quote_re.sub("‘", text) + text = self._closing_single_quote_re.sub("’", text) + + if '"' in text: # guard for perf + text = self._opening_double_quote_re.sub("“", text) + text = self._closing_double_quote_re.sub("”", text) + + text = text.replace("---", "—") + text = text.replace("--", "–") + text = text.replace("...", "…") + text = text.replace(" . . . ", "…") + text = text.replace(". . .", "…") + return text + + _block_quote_re = re.compile(r''' + ( # Wrap whole match in \1 + ( + ^[ \t]*>[ \t]? # '>' at the start of a line + .+\n # rest of the first line + (.+\n)* # subsequent consecutive lines + \n* # blanks + )+ + ) + ''', re.M | re.X) + _bq_one_level_re = re.compile('^[ \t]*>[ \t]?', re.M); + + _html_pre_block_re = re.compile(r'(\s*<pre>.+?</pre>)', re.S) + def _dedent_two_spaces_sub(self, match): + return re.sub(r'(?m)^ ', '', match.group(1)) + + def _block_quote_sub(self, match): + bq = match.group(1) + bq = self._bq_one_level_re.sub('', bq) # trim one level of quoting + bq = self._ws_only_line_re.sub('', bq) # trim whitespace-only lines + bq = self._run_block_gamut(bq) # recurse + + bq = re.sub('(?m)^', ' ', bq) + # These leading spaces screw with <pre> content, so we need to fix that: + bq = self._html_pre_block_re.sub(self._dedent_two_spaces_sub, bq) + + return "<blockquote>\n%s\n</blockquote>\n\n" % bq + + def _do_block_quotes(self, text): + if '>' not in text: + return text + return self._block_quote_re.sub(self._block_quote_sub, text) + + def _form_paragraphs(self, text): + # Strip leading and trailing lines: + text = text.strip('\n') + + # Wrap <p> tags. + grafs = [] + for i, graf in enumerate(re.split(r"\n{2,}", text)): + if graf in self.html_blocks: + # Unhashify HTML blocks + grafs.append(self.html_blocks[graf]) + else: + cuddled_list = None + if "cuddled-lists" in self.extras: + # Need to put back trailing '\n' for `_list_item_re` + # match at the end of the paragraph. + li = self._list_item_re.search(graf + '\n') + # Two of the same list marker in this paragraph: a likely + # candidate for a list cuddled to preceding paragraph + # text (issue 33). Note the `[-1]` is a quick way to + # consider numeric bullets (e.g. "1." and "2.") to be + # equal. + if (li and len(li.group(2)) <= 3 and li.group("next_marker") + and li.group("marker")[-1] == li.group("next_marker")[-1]): + start = li.start() + cuddled_list = self._do_lists(graf[start:]).rstrip("\n") + assert cuddled_list.startswith("<ul>") or cuddled_list.startswith("<ol>") + graf = graf[:start] + + # Wrap <p> tags. + graf = self._run_span_gamut(graf) + grafs.append("<p>" + graf.lstrip(" \t") + "</p>") + + if cuddled_list: + grafs.append(cuddled_list) + + return "\n\n".join(grafs) + + def _add_footnotes(self, text): + if self.footnotes: + footer = [ + '<div class="footnotes">', + '<hr' + self.empty_element_suffix, + '<ol>', + ] + for i, id in enumerate(self.footnote_ids): + if i != 0: + footer.append('') + footer.append('<li id="fn-%s">' % id) + footer.append(self._run_block_gamut(self.footnotes[id])) + backlink = ('<a href="#fnref-%s" ' + 'class="footnoteBackLink" ' + 'title="Jump back to footnote %d in the text.">' + '↩</a>' % (id, i+1)) + if footer[-1].endswith("</p>"): + footer[-1] = footer[-1][:-len("</p>")] \ + + ' ' + backlink + "</p>" + else: + footer.append("\n<p>%s</p>" % backlink) + footer.append('</li>') + footer.append('</ol>') + footer.append('</div>') + return text + '\n\n' + '\n'.join(footer) + else: + return text + + # Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: + # http://bumppo.net/projects/amputator/ + _ampersand_re = re.compile(r'&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)') + _naked_lt_re = re.compile(r'<(?![a-z/?\$!])', re.I) + _naked_gt_re = re.compile(r'''(?<![a-z0-9?!/'"-])>''', re.I) + + def _encode_amps_and_angles(self, text): + # Smart processing for ampersands and angle brackets that need + # to be encoded. + text = self._ampersand_re.sub('&', text) + + # Encode naked <'s + text = self._naked_lt_re.sub('<', text) + + # Encode naked >'s + # Note: Other markdown implementations (e.g. Markdown.pl, PHP + # Markdown) don't do this. + text = self._naked_gt_re.sub('>', text) + return text + + def _encode_backslash_escapes(self, text): + for ch, escape in list(self._escape_table.items()): + text = text.replace("\\"+ch, escape) + return text + + _auto_link_re = re.compile(r'<((https?|ftp):[^\'">\s]+)>', re.I) + def _auto_link_sub(self, match): + g1 = match.group(1) + return '<a href="%s">%s</a>' % (g1, g1) + + _auto_email_link_re = re.compile(r""" + < + (?:mailto:)? + ( + [-.\w]+ + \@ + [-\w]+(\.[-\w]+)*\.[a-z]+ + ) + > + """, re.I | re.X | re.U) + def _auto_email_link_sub(self, match): + return self._encode_email_address( + self._unescape_special_chars(match.group(1))) + + def _do_auto_links(self, text): + text = self._auto_link_re.sub(self._auto_link_sub, text) + text = self._auto_email_link_re.sub(self._auto_email_link_sub, text) + return text + + def _encode_email_address(self, addr): + # Input: an email address, e.g. "foo@example.com" + # + # Output: the email address as a mailto link, with each character + # of the address encoded as either a decimal or hex entity, in + # the hopes of foiling most address harvesting spam bots. E.g.: + # + # <a href="mailto:foo@e + # xample.com">foo + # @example.com</a> + # + # Based on a filter by Matthew Wickline, posted to the BBEdit-Talk + # mailing list: <http://tinyurl.com/yu7ue> + chars = [_xml_encode_email_char_at_random(ch) + for ch in "mailto:" + addr] + # Strip the mailto: from the visible part. + addr = '<a href="%s">%s</a>' \ + % (''.join(chars), ''.join(chars[7:])) + return addr + + def _do_link_patterns(self, text): + """Caveat emptor: there isn't much guarding against link + patterns being formed inside other standard Markdown links, e.g. + inside a [link def][like this]. + + Dev Notes: *Could* consider prefixing regexes with a negative + lookbehind assertion to attempt to guard against this. + """ + link_from_hash = {} + for regex, repl in self.link_patterns: + replacements = [] + for match in regex.finditer(text): + if hasattr(repl, "__call__"): + href = repl(match) + else: + href = match.expand(repl) + replacements.append((match.span(), href)) + for (start, end), href in reversed(replacements): + escaped_href = ( + href.replace('"', '"') # b/c of attr quote + # To avoid markdown <em> and <strong>: + .replace('*', self._escape_table['*']) + .replace('_', self._escape_table['_'])) + link = '<a href="%s">%s</a>' % (escaped_href, text[start:end]) + hash = _hash_text(link) + link_from_hash[hash] = link + text = text[:start] + hash + text[end:] + for hash, link in list(link_from_hash.items()): + text = text.replace(hash, link) + return text + + def _unescape_special_chars(self, text): + # Swap back in all the special characters we've hidden. + for ch, hash in list(self._escape_table.items()): + text = text.replace(hash, ch) + return text + + def _outdent(self, text): + # Remove one level of line-leading tabs or spaces + return self._outdent_re.sub('', text) + + +class MarkdownWithExtras(Markdown): + """A markdowner class that enables most extras: + + - footnotes + - code-color (only has effect if 'pygments' Python module on path) + + These are not included: + - pyshell (specific to Python-related documenting) + - code-friendly (because it *disables* part of the syntax) + - link-patterns (because you need to specify some actual + link-patterns anyway) + """ + extras = ["footnotes", "code-color"] + + +#---- internal support functions + +class UnicodeWithAttrs(unicode): + """A subclass of unicode used for the return value of conversion to + possibly attach some attributes. E.g. the "toc_html" attribute when + the "toc" extra is used. + """ + metadata = None + _toc = None + def toc_html(self): + """Return the HTML for the current TOC. + + This expects the `_toc` attribute to have been set on this instance. + """ + if self._toc is None: + return None + + def indent(): + return ' ' * (len(h_stack) - 1) + lines = [] + h_stack = [0] # stack of header-level numbers + for level, id, name in self._toc: + if level > h_stack[-1]: + lines.append("%s<ul>" % indent()) + h_stack.append(level) + elif level == h_stack[-1]: + lines[-1] += "</li>" + else: + while level < h_stack[-1]: + h_stack.pop() + if not lines[-1].endswith("</li>"): + lines[-1] += "</li>" + lines.append("%s</ul></li>" % indent()) + lines.append('%s<li><a href="#%s">%s</a>' % ( + indent(), id, name)) + while len(h_stack) > 1: + h_stack.pop() + if not lines[-1].endswith("</li>"): + lines[-1] += "</li>" + lines.append("%s</ul>" % indent()) + return '\n'.join(lines) + '\n' + toc_html = property(toc_html) + +## {{{ http://code.activestate.com/recipes/577257/ (r1) +_slugify_strip_re = re.compile(r'[^\w\s-]') +_slugify_hyphenate_re = re.compile(r'[-\s]+') +def _slugify(value): + """ + Normalizes string, converts to lowercase, removes non-alpha characters, + and converts spaces to hyphens. + + From Django's "django/template/defaultfilters.py". + """ + + try: + import unicodedata + value = unicodedata.normalize('NFKD', value) + except ImportError: + pass + value = value.encode('ascii', 'ignore').decode() + value = _slugify_strip_re.sub('', value).strip().lower() + return _slugify_hyphenate_re.sub('-', value) +## end of http://code.activestate.com/recipes/577257/ }}} + + +# From http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52549 +def _curry(*args, **kwargs): + function, args = args[0], args[1:] + def result(*rest, **kwrest): + combined = kwargs.copy() + combined.update(kwrest) + return function(*args + rest, **combined) + return result + +# Recipe: regex_from_encoded_pattern (1.0) +def _regex_from_encoded_pattern(s): + """'foo' -> re.compile(re.escape('foo')) + '/foo/' -> re.compile('foo') + '/foo/i' -> re.compile('foo', re.I) + """ + if s.startswith('/') and s.rfind('/') != 0: + # Parse it: /PATTERN/FLAGS + idx = s.rfind('/') + pattern, flags_str = s[1:idx], s[idx+1:] + flag_from_char = { + "i": re.IGNORECASE, + "l": re.LOCALE, + "s": re.DOTALL, + "m": re.MULTILINE, + "u": re.UNICODE, + } + flags = 0 + for char in flags_str: + try: + flags |= flag_from_char[char] + except KeyError: + raise ValueError("unsupported regex flag: '%s' in '%s' " + "(must be one of '%s')" + % (char, s, ''.join(list(flag_from_char.keys())))) + return re.compile(s[1:idx], flags) + else: # not an encoded regex + return re.compile(re.escape(s)) + +# Recipe: dedent (0.1.2) +def _dedentlines(lines, tabsize=8, skip_first_line=False): + """_dedentlines(lines, tabsize=8, skip_first_line=False) -> dedented lines + + "lines" is a list of lines to dedent. + "tabsize" is the tab width to use for indent width calculations. + "skip_first_line" is a boolean indicating if the first line should + be skipped for calculating the indent width and for dedenting. + This is sometimes useful for docstrings and similar. + + Same as dedent() except operates on a sequence of lines. Note: the + lines list is modified **in-place**. + """ + DEBUG = False + if DEBUG: + print("dedent: dedent(..., tabsize=%d, skip_first_line=%r)"\ + % (tabsize, skip_first_line)) + indents = [] + margin = None + for i, line in enumerate(lines): + if i == 0 and skip_first_line: continue + indent = 0 + for ch in line: + if ch == ' ': + indent += 1 + elif ch == '\t': + indent += tabsize - (indent % tabsize) + elif ch in '\r\n': + continue # skip all-whitespace lines + else: + break + else: + continue # skip all-whitespace lines + if DEBUG: print("dedent: indent=%d: %r" % (indent, line)) + if margin is None: + margin = indent + else: + margin = min(margin, indent) + if DEBUG: print("dedent: margin=%r" % margin) + + if margin is not None and margin > 0: + for i, line in enumerate(lines): + if i == 0 and skip_first_line: continue + removed = 0 + for j, ch in enumerate(line): + if ch == ' ': + removed += 1 + elif ch == '\t': + removed += tabsize - (removed % tabsize) + elif ch in '\r\n': + if DEBUG: print("dedent: %r: EOL -> strip up to EOL" % line) + lines[i] = lines[i][j:] + break + else: + raise ValueError("unexpected non-whitespace char %r in " + "line %r while removing %d-space margin" + % (ch, line, margin)) + if DEBUG: + print("dedent: %r: %r -> removed %d/%d"\ + % (line, ch, removed, margin)) + if removed == margin: + lines[i] = lines[i][j+1:] + break + elif removed > margin: + lines[i] = ' '*(removed-margin) + lines[i][j+1:] + break + else: + if removed: + lines[i] = lines[i][removed:] + return lines + +def _dedent(text, tabsize=8, skip_first_line=False): + """_dedent(text, tabsize=8, skip_first_line=False) -> dedented text + + "text" is the text to dedent. + "tabsize" is the tab width to use for indent width calculations. + "skip_first_line" is a boolean indicating if the first line should + be skipped for calculating the indent width and for dedenting. + This is sometimes useful for docstrings and similar. + + textwrap.dedent(s), but don't expand tabs to spaces + """ + lines = text.splitlines(1) + _dedentlines(lines, tabsize=tabsize, skip_first_line=skip_first_line) + return ''.join(lines) + + +class _memoized(object): + """Decorator that caches a function's return value each time it is called. + If called later with the same arguments, the cached value is returned, and + not re-evaluated. + + http://wiki.python.org/moin/PythonDecoratorLibrary + """ + def __init__(self, func): + self.func = func + self.cache = {} + def __call__(self, *args): + try: + return self.cache[args] + except KeyError: + self.cache[args] = value = self.func(*args) + return value + except TypeError: + # uncachable -- for instance, passing a list as an argument. + # Better to not cache than to blow up entirely. + return self.func(*args) + def __repr__(self): + """Return the function's docstring.""" + return self.func.__doc__ + + +def _xml_oneliner_re_from_tab_width(tab_width): + """Standalone XML processing instruction regex.""" + return re.compile(r""" + (?: + (?<=\n\n) # Starting after a blank line + | # or + \A\n? # the beginning of the doc + ) + ( # save in $1 + [ ]{0,%d} + (?: + <\?\w+\b\s+.*?\?> # XML processing instruction + | + <\w+:\w+\b\s+.*?/> # namespaced single tag + ) + [ \t]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + ) + """ % (tab_width - 1), re.X) +_xml_oneliner_re_from_tab_width = _memoized(_xml_oneliner_re_from_tab_width) + +def _hr_tag_re_from_tab_width(tab_width): + return re.compile(r""" + (?: + (?<=\n\n) # Starting after a blank line + | # or + \A\n? # the beginning of the doc + ) + ( # save in \1 + [ ]{0,%d} + <(hr) # start tag = \2 + \b # word break + ([^<>])*? # + /?> # the matching end tag + [ \t]* + (?=\n{2,}|\Z) # followed by a blank line or end of document + ) + """ % (tab_width - 1), re.X) +_hr_tag_re_from_tab_width = _memoized(_hr_tag_re_from_tab_width) + + +def _xml_escape_attr(attr, skip_single_quote=True): + """Escape the given string for use in an HTML/XML tag attribute. + + By default this doesn't bother with escaping `'` to `'`, presuming that + the tag attribute is surrounded by double quotes. + """ + escaped = (attr + .replace('&', '&') + .replace('"', '"') + .replace('<', '<') + .replace('>', '>')) + if not skip_single_quote: + escaped = escaped.replace("'", "'") + return escaped + + +def _xml_encode_email_char_at_random(ch): + r = random() + # Roughly 10% raw, 45% hex, 45% dec. + # '@' *must* be encoded. I [John Gruber] insist. + # Issue 26: '_' must be encoded. + if r > 0.9 and ch not in "@_": + return ch + elif r < 0.45: + # The [1:] is to drop leading '0': 0x63 -> x63 + return '&#%s;' % hex(ord(ch))[1:] + else: + return '&#%s;' % ord(ch) + + + +#---- mainline + +class _NoReflowFormatter(optparse.IndentedHelpFormatter): + """An optparse formatter that does NOT reflow the description.""" + def format_description(self, description): + return description or "" + +def _test(): + import doctest + doctest.testmod() + +def main(argv=None): + if argv is None: + argv = sys.argv + if not logging.root.handlers: + logging.basicConfig() + + usage = "usage: %prog [PATHS...]" + version = "%prog "+__version__ + parser = optparse.OptionParser(prog="markdown2", usage=usage, + version=version, description=cmdln_desc, + formatter=_NoReflowFormatter()) + parser.add_option("-v", "--verbose", dest="log_level", + action="store_const", const=logging.DEBUG, + help="more verbose output") + parser.add_option("--encoding", + help="specify encoding of text content") + parser.add_option("--html4tags", action="store_true", default=False, + help="use HTML 4 style for empty element tags") + parser.add_option("-s", "--safe", metavar="MODE", dest="safe_mode", + help="sanitize literal HTML: 'escape' escapes " + "HTML meta chars, 'replace' replaces with an " + "[HTML_REMOVED] note") + parser.add_option("-x", "--extras", action="append", + help="Turn on specific extra features (not part of " + "the core Markdown spec). See above.") + parser.add_option("--use-file-vars", + help="Look for and use Emacs-style 'markdown-extras' " + "file var to turn on extras. See " + "<https://github.com/trentm/python-markdown2/wiki/Extras>") + parser.add_option("--link-patterns-file", + help="path to a link pattern file") + parser.add_option("--self-test", action="store_true", + help="run internal self-tests (some doctests)") + parser.add_option("--compare", action="store_true", + help="run against Markdown.pl as well (for testing)") + parser.set_defaults(log_level=logging.INFO, compare=False, + encoding="utf-8", safe_mode=None, use_file_vars=False) + opts, paths = parser.parse_args() + log.setLevel(opts.log_level) + + if opts.self_test: + return _test() + + if opts.extras: + extras = {} + for s in opts.extras: + splitter = re.compile("[,;: ]+") + for e in splitter.split(s): + if '=' in e: + ename, earg = e.split('=', 1) + try: + earg = int(earg) + except ValueError: + pass + else: + ename, earg = e, None + extras[ename] = earg + else: + extras = None + + if opts.link_patterns_file: + link_patterns = [] + f = open(opts.link_patterns_file) + try: + for i, line in enumerate(f.readlines()): + if not line.strip(): continue + if line.lstrip().startswith("#"): continue + try: + pat, href = line.rstrip().rsplit(None, 1) + except ValueError: + raise MarkdownError("%s:%d: invalid link pattern line: %r" + % (opts.link_patterns_file, i+1, line)) + link_patterns.append( + (_regex_from_encoded_pattern(pat), href)) + finally: + f.close() + else: + link_patterns = None + + from os.path import join, dirname, abspath, exists + markdown_pl = join(dirname(dirname(abspath(__file__))), "test", + "Markdown.pl") + if not paths: + paths = ['-'] + for path in paths: + if path == '-': + text = sys.stdin.read() + else: + fp = codecs.open(path, 'r', opts.encoding) + text = fp.read() + fp.close() + if opts.compare: + from subprocess import Popen, PIPE + print("==== Markdown.pl ====") + p = Popen('perl %s' % markdown_pl, shell=True, stdin=PIPE, stdout=PIPE, close_fds=True) + p.stdin.write(text.encode('utf-8')) + p.stdin.close() + perl_html = p.stdout.read().decode('utf-8') + if py3: + sys.stdout.write(perl_html) + else: + sys.stdout.write(perl_html.encode( + sys.stdout.encoding or "utf-8", 'xmlcharrefreplace')) + print("==== markdown2.py ====") + html = markdown(text, + html4tags=opts.html4tags, + safe_mode=opts.safe_mode, + extras=extras, link_patterns=link_patterns, + use_file_vars=opts.use_file_vars) + if py3: + sys.stdout.write(html) + else: + sys.stdout.write(html.encode( + sys.stdout.encoding or "utf-8", 'xmlcharrefreplace')) + if extras and "toc" in extras: + log.debug("toc_html: " + + html.toc_html.encode(sys.stdout.encoding or "utf-8", 'xmlcharrefreplace')) + if opts.compare: + test_dir = join(dirname(dirname(abspath(__file__))), "test") + if exists(join(test_dir, "test_markdown2.py")): + sys.path.insert(0, test_dir) + from test_markdown2 import norm_html_from_html + norm_html = norm_html_from_html(html) + norm_perl_html = norm_html_from_html(perl_html) + else: + norm_html = html + norm_perl_html = perl_html + print("==== match? %r ====" % (norm_perl_html == norm_html)) + + +if __name__ == "__main__": + sys.exit( main(sys.argv) ) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/package-metadata.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/package-metadata.json new file mode 100644 index 0000000..94a33d4 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/package-metadata.json @@ -0,0 +1 @@ +{"url": "https://github.com/revolunet/sublimetext-markdown-preview", "version": "2013.04.01.18.50.45", "description": "markdown preview plugin for sublime text 2"} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/sample.html b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/sample.html new file mode 100644 index 0000000..c63eacd --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/sample.html @@ -0,0 +1,141 @@ +<h1 id="sample-markdown-cheat-sheet">Sample Markdown Cheat Sheet</h1> + +<p>This is a sample markdown file to help you write Markdown quickly :)</p> + +<p>If you use the fabulous <a href="http://sublimetext.com">Sublime Text 2 editor</a> along with the <a href="https://github.com/revolunet/sublimetext-markdown-preview">Markdown Preview plugin</a>, open your ST2 Palette with <code>CMD+P</code> then choose <code>Markdown Preview in browser</code> to see the result in your browser.</p> + +<h2 id="text-basics">Text basics</h2> + +<p>this is <em>italic</em> and this is <strong>bold</strong> . another <em>italic</em> and another <strong>bold</strong></p> + +<p>this is <code>important</code> text. and percentage signs : % and <code>%</code></p> + +<p>This is a paragraph with a footnote (builtin parser only). <sup class="footnote-ref" id="fnref-note-id"><a href="#fn-note-id">1</a></sup> </p> + +<p>Insert <code>[ toc ]</code> without spaces to generate a table of contents (builtin parser only).</p> + +<h2 id="indentation">Indentation</h2> + +<blockquote> + <p>Here is some indented text</p> + + <blockquote> + <p>even more indented</p> + </blockquote> +</blockquote> + +<h2 id="titles">Titles</h2> + +<h1 id="big-title-h1">Big title (h1)</h1> + +<h2 id="middle-title-h2">Middle title (h2)</h2> + +<h3 id="smaller-title-h3">Smaller title (h3)</h3> + +<h4 id="and-so-on-hx">and so on (hX)</h4> + +<h5 id="and-so-on-hx-2">and so on (hX)</h5> + +<h6 id="and-so-on-hx-3">and so on (hX)</h6> + +<h2 id="example-lists-1">Example lists (1)</h2> + +<ul> +<li>bullets can be <code>-</code>, <code>+</code>, or <code>*</code></li> +<li>bullet list 1</li> +<li><p>bullet list 2</p> + +<ul> +<li>sub item 1</li> +<li><p>sub item 2</p> + +<p>with indented text inside</p></li> +</ul></li> +<li><p>bullet list 3</p></li> +<li>bullet list 4</li> +<li>bullet list 5</li> +</ul> + +<h2 id="links">Links</h2> + +<p>This is an <a href="http://lmgtfy.com/">example inline link</a> and <a href="http://lmgtfy.com/" title="Hello, world">another one with a title</a>.</p> + +<p>Links can also be reference based : <a href="http://revolunet.com">reference 1</a> or <a href="http://revolunet.com" title="rich web apps">reference 2 with title</a>.</p> + +<p>References are usually placed at the bottom of the document</p> + +<h2 id="images">Images</h2> + +<p>A sample image :</p> + +<p><img src="http://www.revolunet.com/static/parisjs8/img/logo-revolunet-carre.jpg" alt="revolunet logo" title="revolunet logo" /></p> + +<p>As links, images can also use references instead of inline links :</p> + +<p><img src="http://www.revolunet.com/static/parisjs8/img/logo-revolunet-carre.jpg" alt="revolunet logo" title="revolunet logo" /></p> + +<h2 id="code">Code</h2> + +<p>It's quite easy to show code in markdown files.</p> + +<p>Backticks can be used to <code>highlight</code> some words.</p> + +<p>Also, any indented block is considered a code block.</p> + +<pre><code><script> + document.location = 'http://lmgtfy.com/?q=markdown+cheat+sheet'; +</script> +</code></pre> + +<h2 id="github-flavored-markdown">GitHub Flavored Markdown</h2> + +<p>If you use the Github parser, you can use some of <a href="http://github.github.com/github-flavored-markdown/">Github Flavored Markdown</a> syntax :</p> + +<ul> +<li>User/Project@SHA: revolunet/sublimetext-markdown-preview@7da61badeda468b5019869d11000307e07e07401</li> +<li>User/Project#Issue: revolunet/sublimetext-markdown-preview#1</li> +<li>User : @revolunet</li> +</ul> + +<p>Some Python code :</p> + +<pre><code>import random + +class CardGame(object): + """ a sample python class """ + NB_CARDS = 32 + def __init__(self, cards=5): + self.cards = random.sample(range(self.NB_CARDS), 5) + print 'ready to play' +</code></pre> + +<p>Some Javascript code :</p> + +<pre><code>var config = { + duration: 5, + comment: 'WTF' +} +// callbacks beauty un action +async_call('/path/to/api', function(json) { + another_call(json, function(result2) { + another_another_call(result2, function(result3) { + another_another_another_call(result3, function(result4) { + alert('And if all went well, i got my result :)'); + }); + }); + }); +}) +</code></pre> + +<h2 id="about">About</h2> + +<p>This plugin and this sample file is proudly brought to you by the <a href="http://revolunet.com">revolunet team</a></p> + +<div class="footnotes"> +<hr /> +<ol> +<li id="fn-note-id"> +<p>This is the text of the note. <a href="#fnref-note-id" class="footnoteBackLink" title="Jump back to footnote 1 in the text.">↩</a></p> +</li> +</ol> +</div> diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/sample.md b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/sample.md new file mode 100644 index 0000000..abf7b25 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Markdown Preview/sample.md @@ -0,0 +1,132 @@ +Sample Markdown Cheat Sheet +=========================== + +This is a sample markdown file to help you write Markdown quickly :) + +If you use the fabulous [Sublime Text 2 editor][ST2] along with the [Markdown Preview plugin][MarkdownPreview], open your ST2 Palette with `CMD+P` then choose `Markdown Preview in browser` to see the result in your browser. + +## Text basics +this is *italic* and this is **bold** . another _italic_ and another __bold__ + +this is `important` text. and percentage signs : % and `%` + +This is a paragraph with a footnote (builtin parser only). [^note-id] + +Insert `[ toc ]` without spaces to generate a table of contents (builtin parser only). + +## Indentation +> Here is some indented text +>> even more indented + +## Titles +# Big title (h1) +## Middle title (h2) +### Smaller title (h3) +#### and so on (hX) +##### and so on (hX) +###### and so on (hX) + +## Example lists (1) + + - bullets can be `-`, `+`, or `*` + - bullet list 1 + - bullet list 2 + - sub item 1 + - sub item 2 + + with indented text inside + + - bullet list 3 + + bullet list 4 + * bullet list 5 + +## Links + +This is an [example inline link](http://lmgtfy.com/) and [another one with a title](http://lmgtfy.com/ "Hello, world"). + +Links can also be reference based : [reference 1][ref1] or [reference 2 with title][ref2]. + +References are usually placed at the bottom of the document + +## Images + +A sample image : + +![revolunet logo](http://www.revolunet.com/static/parisjs8/img/logo-revolunet-carre.jpg "revolunet logo") + +As links, images can also use references instead of inline links : + +![revolunet logo][revolunet-logo] + + +## Code + +It's quite easy to show code in markdown files. + +Backticks can be used to `highlight` some words. + +Also, any indented block is considered a code block. + + <script> + document.location = 'http://lmgtfy.com/?q=markdown+cheat+sheet'; + </script> + +## GitHub Flavored Markdown + +If you use the Github parser, you can use some of [Github Flavored Markdown][gfm] syntax : + + * User/Project@SHA: revolunet/sublimetext-markdown-preview@7da61badeda468b5019869d11000307e07e07401 + * User/Project#Issue: revolunet/sublimetext-markdown-preview#1 + * User : @revolunet + +Some Python code : + +```python +import random + +class CardGame(object): + """ a sample python class """ + NB_CARDS = 32 + def __init__(self, cards=5): + self.cards = random.sample(range(self.NB_CARDS), 5) + print 'ready to play' +``` + +Some Javascript code : + +```js +var config = { + duration: 5, + comment: 'WTF' +} +// callbacks beauty un action +async_call('/path/to/api', function(json) { + another_call(json, function(result2) { + another_another_call(result2, function(result3) { + another_another_another_call(result3, function(result4) { + alert('And if all went well, i got my result :)'); + }); + }); + }); +}) +``` + +The Github Markdown also brings some [nice Emoji support][emoji] : :+1: :heart: :beer: + +[^note-id]: This is the text of the note. + +## About + +This plugin and this sample file is proudly brought to you by the [revolunet team][revolunet] + + [ref1]: http://revolunet.com + [ref2]: http://revolunet.com "rich web apps" + [MarkdownREF]: http://daringfireball.net/projects/markdown/basics + [MarkdownPreview]: https://github.com/revolunet/sublimetext-markdown-preview + [ST2]: http://sublimetext.com + [revolunet]: http://revolunet.com + [revolunet-logo]: http://www.revolunet.com/static/parisjs8/img/logo-revolunet-carre.jpg "revolunet logo" + [gfm]: http://github.github.com/github-flavored-markdown/ + [emoji]: http://www.emoji-cheat-sheet.com/ + + diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Missing Palette Commands/Missing.sublime-commands b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Missing Palette Commands/Missing.sublime-commands new file mode 100644 index 0000000..6fdf4ab --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Missing Palette Commands/Missing.sublime-commands @@ -0,0 +1,115 @@ +[ + // File Menu + { "caption": "Exit Sublime Text 2", "command": "exit" }, + { "caption": "New Window", "command": "new_window"}, + { "caption": "Close Window", "command": "close_window" }, + { "caption": "File: Save As", "command": "prompt_save_as" }, + { "caption": "File: Close", "command": "close" }, + { "caption": "File: Reopen Closed File", "command": "reopen_last_file" }, + + // Edit Menu + { "caption": "Code Folding: Fold All", "command": "fold_by_level", "args": {"level": 1} }, + + { "caption": "Permute Lines: Reverse", "command": "permute_lines", "args": {"operation": "reverse"} }, + { "caption": "Permute Lines: Unique", "command": "permute_lines", "args": {"operation": "unique"} }, + { "caption": "Permute Lines: Shuffle", "command": "permute_lines", "args": {"operation": "shuffle"} }, + + { "caption": "Permute Selections: Sort", "command": "sort_selection", "args": {"case_sensitive": false} }, + { "caption": "Permute Selections: Sort (Case Sensitive)", "command": "sort_selection", "args": {"case_sensitive": true} }, + { "caption": "Permute Selections: Reverse", "command": "permute_selection", "args": {"operation": "reverse"} }, + { "caption": "Permute Selections: Unique", "command": "permute_selection", "args": {"operation": "unique"} }, + { "caption": "Permute Selections: Shuffle", "command": "permute_selection", "args": {"operation": "shuffle"} }, + + // Find Menu + { "caption": "Find", "command": "show_panel", "args": {"panel": "find"} }, + { "caption": "Find in Files", "command": "show_panel", "args": {"panel": "find_in_files"} }, + { "caption": "Replace", "command": "show_panel", "args": {"panel": "replace"} }, + { "caption": "Show Find Results Panel", "command": "show_panel", "args": {"panel": "output.find_results"} }, + + { "caption": "Next Result", "command": "next_result" }, + { "caption": "Previous Result", "command": "previous_result" }, + + // View Menu + { "caption": "View: Toggle Fullscreen", "command": "toggle_full_screen" }, + { "caption": "View: Toggle Distraction Free mode", "command": "toggle_distraction_free" }, + { "caption": "View: Toggle Console", "command": "show_panel", "args": {"panel": "console", "toggle": true} }, + + { "caption": "Indentation: Indent Using Spaces", "command": "toggle_setting", "args": {"setting": "translate_tabs_to_spaces"} }, + { "caption": "Indentation: Tab Width: 1", "command": "set_setting", "args": {"setting": "tab_size", "value": 1} }, + { "caption": "Indentation: Tab Width: 2", "command": "set_setting", "args": {"setting": "tab_size", "value": 2} }, + { "caption": "Indentation: Tab Width: 3", "command": "set_setting", "args": {"setting": "tab_size", "value": 3} }, + { "caption": "Indentation: Tab Width: 4", "command": "set_setting", "args": {"setting": "tab_size", "value": 4} }, + { "caption": "Indentation: Tab Width: 5", "command": "set_setting", "args": {"setting": "tab_size", "value": 5} }, + { "caption": "Indentation: Tab Width: 6", "command": "set_setting", "args": {"setting": "tab_size", "value": 6} }, + { "caption": "Indentation: Tab Width: 7", "command": "set_setting", "args": {"setting": "tab_size", "value": 7} }, + { "caption": "Indentation: Tab Width: 8", "command": "set_setting", "args": {"setting": "tab_size", "value": 8} }, + { "caption": "Indentation: Guess Settings From Buffer", "command": "detect_indentation" }, + + { "caption": "Line Endings: Windows", "command": "set_line_ending", "args": {"type": "windows"} }, + { "caption": "Line Endings: Unix", "command": "set_line_ending", "args": {"type": "unix"} }, + { "caption": "Line Endings: Mac OS 9", "command": "set_line_ending", "args": {"type": "cr"} }, + + { "caption": "View: Toggle Spell Check", "command": "toggle_setting", "args": {"setting": "spell_check"} }, + { "caption": "Spell Check: Next Misspelling", "command": "next_misspelling" }, + { "caption": "Spell Check: Previous Misspelling", "command": "previous_misspelling" }, + + { "caption": "Ruler: None", "command": "set_setting", "args": {"setting": "rulers", "value": []}}, + { "caption": "Ruler: Column 70", "command": "set_setting", "args": {"setting": "rulers", "value": [70]}}, + { "caption": "Ruler: Column 78", "command": "set_setting", "args": {"setting": "rulers", "value": [78]}}, + { "caption": "Ruler: Column 80", "command": "set_setting", "args": {"setting": "rulers", "value": [80]}}, + { "caption": "Ruler: Column 100", "command": "set_setting", "args": {"setting": "rulers", "value": [100]}}, + { "caption": "Ruler: Column 120", "command": "set_setting", "args": {"setting": "rulers", "value": [120]}}, + + { "caption": "Word Wrap: Automatic Column", "command": "set_setting", "args": {"setting": "wrap_width", "value": 0}}, + { "caption": "Word Wrap: Column 70", "command": "set_setting", "args": {"setting": "wrap_width", "value": 70}}, + { "caption": "Word Wrap: Column 78", "command": "set_setting", "args": {"setting": "wrap_width", "value": 78}}, + { "caption": "Word Wrap: Column 80", "command": "set_setting", "args": {"setting": "wrap_width", "value": 80}}, + { "caption": "Word Wrap: Column 100", "command": "set_setting", "args": {"setting": "wrap_width", "value": 100}}, + { "caption": "Word Wrap: Column 120", "command": "set_setting", "args": {"setting": "wrap_width", "value": 120}}, + + { "caption": "Layout: Single", + "command": "set_layout", + "args": {"cols": [0.0, 1.0], "rows": [0.0, 1.0], "cells": [[0, 0, 1, 1]]} + }, + { "caption": "Layout: Columns: 2", + "command": "set_layout", + "args": {"cols": [0.0, 0.5, 1.0], "rows": [0.0, 1.0], "cells": [[0, 0, 1, 1], [1, 0, 2, 1]]} + }, + { "caption": "Layout: Columns: 3", + "command": "set_layout", + "args": {"cols": [0.0, 0.33, 0.66, 1.0], "rows": [0.0, 1.0], "cells": [[0, 0, 1, 1], [1, 0, 2, 1], [2, 0, 3, 1]]} + }, + { "caption": "Layout: Columns: 4", + "command": "set_layout", + "args": {"cols": [0.0, 0.25, 0.5, 0.75, 1.0], "rows": [0.0, 1.0], "cells": [[0, 0, 1, 1], [1, 0, 2, 1], [2, 0, 3, 1], [3, 0, 4, 1]]} + }, + { "caption": "Layout: Rows: 2", + "command": "set_layout", + "args": {"cols": [0.0, 1.0], "rows": [0.0, 0.5, 1.0], "cells": [[0, 0, 1, 1], [0, 1, 1, 2]]} + }, + { "caption": "Layout: Rows: 3", + "command": "set_layout", + "args": {"cols": [0.0, 1.0], "rows": [0.0, 0.33, 0.66, 1.0], "cells": [[0, 0, 1, 1], [0, 1, 1, 2], [0, 2, 1, 3]]} + }, + { "caption": "Layout: Grid: 4", + "command": "set_layout", + "args": {"cols": [0.0, 0.5, 1.0], "rows": [0.0, 0.5, 1.0], "cells": [[0, 0, 1, 1], [1, 0, 2, 1], [0, 1, 1, 2], [1, 1, 2, 2]]} + }, + + // Tools Menu + { "caption": "Cancel Build", "command": "exec", "args": {"kill": true} }, + { "caption": "Show Build Results Panel", "command": "show_panel", "args": {"panel": "output.exec"} }, + + // Project Menu + { "caption": "Switch Project in Window", "command": "prompt_select_project" }, + { "caption": "Project: Open Project", "command": "prompt_open_project" }, + { "caption": "Project: Edit Project", "command": "open_file", "args": {"file": "${project}"} }, + { "caption": "Project: Remove all Folders", "command": "close_folder_list" }, + { "caption": "Project: Refresh Folders", "command": "refresh_folder_list" }, + + // Preferences Menu + { "caption": "Preferences: Syntax Specific – User", "command": "open_file_settings" }, + { "caption": "Font: Increase Font Size", "command": "increase_font_size" }, + { "caption": "Font: Decrease Font Size", "command": "decrease_font_size" }, + { "caption": "Font: Reset Font Size", "command": "reset_font_size" } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Missing Palette Commands/README.md b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Missing Palette Commands/README.md new file mode 100644 index 0000000..09f1d1b --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Missing Palette Commands/README.md @@ -0,0 +1,8 @@ +# Missing Palette Commands + +I really like the _Command Palette_ feature of Sublime Text 2. +The only problem I have with it is that some useful commands +have been omitted and are only available through the main menu. + +This package provides a `.sublime-commands` file that contains +the missing commands. diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Missing Palette Commands/package-metadata.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Missing Palette Commands/package-metadata.json new file mode 100644 index 0000000..e558006 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Missing Palette Commands/package-metadata.json @@ -0,0 +1 @@ +{"url": "https://github.com/fjl/Sublime-Missing-Palette-Commands", "version": "2012.08.08.06.49.39", "description": "Menu Commands that are missing from the Sublime Text 2 Command Palette"} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/.gitignore b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/Default.sublime-commands b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/Default.sublime-commands new file mode 100644 index 0000000..d55e1e1 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/Default.sublime-commands @@ -0,0 +1,3 @@ +[ + { "caption": "Related Files", "command": "related_files" } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/Default.sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/Default.sublime-keymap new file mode 100644 index 0000000..9b0a2d1 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/Default.sublime-keymap @@ -0,0 +1,3 @@ +[ + { "keys": ["ctrl+super+p"], "command": "related_files"} +] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/Main.sublime-menu b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/Main.sublime-menu new file mode 100644 index 0000000..b9ddbed --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/Main.sublime-menu @@ -0,0 +1,27 @@ +[ + { + "caption": "Preferences", + "mnemonic": "n", + "id": "preferences", + "children": + [ + { + "caption": "Package Settings", + "mnemonic": "P", + "id": "package-settings", + "children": + [ + { + "caption": "RelatedFiles", + "children": + [ + { "command": "open_file", "args": {"file": "${packages}/Related Files/RelatedFiles.sublime-settings"}, "caption": "Settings – Default" }, + { "command": "open_file", "args": {"file": "${packages}/User/RelatedFiles.sublime-settings"}, "caption": "Settings – User" }, + { "caption": "-" } + ] + } + ] + } + ] + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/README.md b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/README.md new file mode 100644 index 0000000..f2fc0f2 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/README.md @@ -0,0 +1,36 @@ +# Sublime Text 2 - Related Files + +![Screenshot](https://raw.github.com/fabiokr/sublime-related-files/master/screenshots/list.png) + +This plugin provides a quick list of related files to the currently open file. + +My main use case is to list related files under a Ruby on Rails project. For example, for an opened "app/controllers/examples_controller.rb", related files would be "app/helpers/examples_helper.rb", "app/views/examples/**", and "spec/controllers/examples_controller_spec.rb". + +This plugin was inspired by the existing [Open Related](https://github.com/vojtajina/sublime-OpenRelated) and [Rails Related Files](https://github.com/luqman/SublimeText2RailsRelatedFiles). + +I wanted something between the two of them (a quick list of results that could be setup for any kinds of projects, not only Rails), so I created my own. + +# Key Shortcut + +The default shortcut is mapped to "ctrl+super+p". To change it to something more suitable for your needs, you can easily change that by copying the following and replacing the "keys" to your desired key combination: + +```json +{ "keys": ["ctrl+super+p"], "command": "related_files"} +``` + +# Configuration + +The plugins comes configured to lookup Rails related files, but you can add your own setups. Let's see an existing example: + +```json +// Test/specs for ruby files +".+\/(app|lib)\/(.+).rb": + [ + "spec/$2_spec.rb", + "test/$2_test.rb" + ] +``` + +The configuration has two parts: the key, which is a regular expression to match against the currently open file, and a list of globs to map the related files. + +You can use the $1, $2, etc. on the glob strings to be replace by the extracted parts from the regex. \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/RelatedFiles.sublime-settings b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/RelatedFiles.sublime-settings new file mode 100644 index 0000000..eb9502c --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/RelatedFiles.sublime-settings @@ -0,0 +1,84 @@ +{ + "patterns": { + // Test/specs for ruby files + ".+\/(app|lib)\/(.+).rb": + [ + "spec/$2_spec.rb", + "test/$2_test.rb" + ], + + // Ruby files for test/specs + ".+\/(test|spec)\/(.+)_(test|spec).rb": + [ + "app/$2.rb", + "lib/$2.rb" + ], + + // Rails controllers + ".+\/app\/controllers\/(.+)_controller.rb": + [ + "app/views/$1/**", + "app/helpers/$1_helper.rb", + "config/routes.rb", + "spec/requests/$1_spec.rb", + "spec/routing/$1_routing_spec.rb" + ], + + // Rails helpers + ".+\/app\/helpers\/(.+)_helper.rb": + [ + "app/views/$1/**", + "app/controllers/$1_controller.rb", + "config/routes.rb", + "spec/requests/$1_spec.rb" + ], + + // Rails views + ".+\/app\/views\/(.+)\/[^\/].+": + [ + "app/views/$1/**", + "app/controllers/$1_controller.rb", + "app/helpers/$1_helper.rb", + "config/routes.rb", + "spec/controllers/$1_spec.rb", + "spec/requests/$1_spec.rb" + ], + + // Rails routes + ".+\/config\/routes.rb": + [ + "spec/routing/**" + ], + + // Rails libs + ".+\/(lib)\/(.+).rb": + [ + "spec/lib/$2_spec.rb", + "test/lib/$2_test.rb" + ], + + // Rails controllers specs + ".+/spec/controllers/(.+)_controller_spec.rb": + [ + "app/controllers/$1_controller.rb", + "app/helpers/$1_helper.rb", + "app/views/$1/**", + "config/routes.rb" + ], + + // Rails request specs + ".+/spec/requests/(.+)_spec.rb": + [ + "app/controllers/$1_controller.rb", + "app/helpers/$1_helper.rb", + "app/views/$1/**", + "config/routes.rb" + ], + + // Rails libs specs + ".+/spec/lib/(.+)_spec.rb": + [ + "lib/$1.rb" + ] + } +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example1/app/controllers/examples_controller.rb b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example1/app/controllers/examples_controller.rb new file mode 100644 index 0000000..f712c1a --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example1/app/controllers/examples_controller.rb @@ -0,0 +1,3 @@ +class ExamplesController < ApplicationController + +end \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example1/app/helpers/examples_helper.rb b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example1/app/helpers/examples_helper.rb new file mode 100644 index 0000000..e69de29 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example1/app/views/examples/index.html b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example1/app/views/examples/index.html new file mode 100644 index 0000000..e69de29 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example1/app/views/examples/show.html b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example1/app/views/examples/show.html new file mode 100644 index 0000000..e69de29 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example1/test/controllers/examples_controller_test.rb b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example1/test/controllers/examples_controller_test.rb new file mode 100644 index 0000000..e69de29 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example2/app/controllers/examples_controller.rb b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example2/app/controllers/examples_controller.rb new file mode 100644 index 0000000..e69de29 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example2/app/views/examples/index.html b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example2/app/views/examples/index.html new file mode 100644 index 0000000..e69de29 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example2/app/views/examples/show.html b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example2/app/views/examples/show.html new file mode 100644 index 0000000..e69de29 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example2/test/controllers/examples_controller_test.rb b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/fixtures/example2/test/controllers/examples_controller_test.rb new file mode 100644 index 0000000..e69de29 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/package-metadata.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/package-metadata.json new file mode 100644 index 0000000..b709a77 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/package-metadata.json @@ -0,0 +1 @@ +{"url": "https://github.com/fabiokr/sublime-related-files", "version": "2013.03.15.08.29.58", "description": "A Sublime Text 2 plugin to list related files"} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/related.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/related.py new file mode 100644 index 0000000..a1c6007 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/related.py @@ -0,0 +1,86 @@ +import os +import re +import glob +import itertools + + +class Related(object): + # Initializes the RelatedFiles object. + # + # file_path - the file to look related files for + # patterns - a dictionary of patterns in the following format: + # {"(.+)_controller.rb": ["*/the/paths/$1/**", "*/test/$1_controller_test.rb"]} + # + # The glob paths will have their $i replaced by the matched groups within the file name + # matcher. + def __init__(self, file_path, patterns, folders): + self.__file_path = file_path + self.__patterns = patterns + self.__root = self.__root(folders) + self.__files = [] + self.__descriptions = [] + self.__build() + + # # Retrieves a list of all related descriptions. + def descriptions(self): + return self.__descriptions + + # # Retrieves a list of all related files paths. + def files(self): + return self.__files + + # Builds a list with all related files and sets self.descriptions and + # self.files. + def __build(self): + files = set() + + file_path = self.__to_posixpath(self.__file_path) + + # for each matching pattern + for regex, paths in self.__patterns.iteritems(): + match = re.compile(regex).match(file_path) + if match: + # returns a flattened file list + files.update(self.__files_for_paths(regex, match, paths)) + + # sorts items + files = list(files) + files.sort() + + self.__files = files + self.__descriptions = [self.__file_without_root(file) for file in files] + + # Returns the root folder for the given file and folders + def __root(self, folders): + for folder in folders: + if self.__file_path.startswith(os.path.join(folder, "")): + return folder + + # Retrieves a list of files fot the given match and paths + def __files_for_paths(self, regex, match, paths): + paths = [self.__replaced_path(match, path) for path in paths] + + files = [glob.glob(os.path.join(self.__root, path)) for path in paths] + flattened = [self.__to_posixpath(path) for path in list(itertools.chain.from_iterable(files))] + + # Ignores current file + if self.__file_path in flattened: + flattened.remove(unicode(self.__file_path)) + + return flattened + + # Retrieves the file name without the root part. + def __file_without_root(self, file): + return os.path.basename(self.__root) + file[len(self.__root):] + + # Retrieves a path with its interpolation vars replaces by the found groups + # on match. + def __replaced_path(self, match, path): + replaced_path = path + for i, group in enumerate(match.groups()): + replaced_path = replaced_path.replace("$%s" % (i + 1), group) + return replaced_path + + # Converts paths to posixpaths. + def __to_posixpath(self, path): + return re.sub("\\\\", "/", path) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/related_files.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/related_files.py new file mode 100644 index 0000000..ef289f1 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/related_files.py @@ -0,0 +1,39 @@ +import sublime +import sublime_plugin +from related import * + + +class RelatedFilesCommand(sublime_plugin.WindowCommand): + def run(self, index=None): + active_file_path = self.__active_file_path() + + if active_file_path: + # Builds a list of related files for the current open file. + self.__related = Related(active_file_path, self.__patterns(), sublime.active_window().folders()) + + self.window.show_quick_panel(self.__related.descriptions(), self.__open_file) + else: + self.__status_msg("No open files") + + # Opens the file in path. + def __open_file(self, index): + if index >= 0: + self.window.open_file(self.__related.files()[index]) + else: + self.__status_msg("No related files found") + + # Retrieves the patterns from settings. + def __patterns(self): + return sublime.load_settings("RelatedFiles.sublime-settings").get('patterns') + + # Returns the activelly open file path from sublime. + def __active_file_path(self): + if self.window.active_view(): + file_path = self.window.active_view().file_name() + + if file_path and len(file_path) > 0: + return file_path + + # Displays a status message on sublime. + def __status_msg(self, message): + sublime.status_message(message) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/related_test.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/related_test.py new file mode 100644 index 0000000..a47b52a --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/related_test.py @@ -0,0 +1,52 @@ +import unittest +import os +from related import * + + +class RelatedTest(unittest.TestCase): + + def test_descriptions_with_matches(self): + self.assertEqual(self.__related().descriptions(), [ + "example1/app/helpers/examples_helper.rb", + "example1/app/views/examples/index.html", + "example1/app/views/examples/show.html", + "example1/test/controllers/examples_controller_test.rb" + ]) + + def test_descriptions_without_matches(self): + self.assertEqual(self.__related_without_match().descriptions(), []) + + def test_files_with_matches(self): + self.assertEqual(self.__related().files(), [ + self.__expand("fixtures/example1/app/helpers/examples_helper.rb"), + self.__expand("fixtures/example1/app/views/examples/index.html"), + self.__expand("fixtures/example1/app/views/examples/show.html"), + self.__expand("fixtures/example1/test/controllers/examples_controller_test.rb") + ]) + + def test_files_without_matches(self): + self.assertEqual(self.__related_without_match().files(), []) + + def __patterns(self): + return { + ".+\/app\/controllers\/(.+)_controller.rb": ["app/views/$1/**", "app/helpers/$1_helper.rb"], + ".+\/app\/(.+).rb": ["test/$1_test.rb"] + } + + def __file(self): + return self.__expand("fixtures/example1/app/controllers/examples_controller.rb") + + def __folders(self): + return [self.__expand("fixtures/example1"), self.__expand("fixtures/example2")] + + def __expand(self, path): + return os.path.join(os.path.dirname(os.path.realpath(__file__)), path) + + def __related(self): + return Related(self.__file(), self.__patterns(), self.__folders()) + + def __related_without_match(self): + return Related("/should/not/match", self.__patterns(), self.__folders()) + +if __name__ == '__main__': + unittest.main() diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/screenshots/list.png b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Related Files/screenshots/list.png new file mode 100644 index 0000000000000000000000000000000000000000..933c9ab938a46dc8b59483a213178a8a1a8fa536 GIT binary patch literal 22696 zcmYiO1yo$U_dO0{g;KOgaR!RJyL)kmQrz9$OQE>CyK8ZGcXt_Na39?Nw9n`J{@yi< zxvWc)droq0&fYr-AqsL5NS|;&K|nwtNq!Smf`EWFdjEavBkcQkk+%!~`vulOT0#`! z?e8bMy(sSe4!qqr4MzwF<i5W@kO?%%c<(pioFrw%;MNccQ7LJw+E?`6Z{awJeRmSE zwYE04ae@$WFxGc6HX?F0cQPZAkd#$W_d~{lfFOd96cti&TRB;Gb;F;(xwt&HigTIw ztR)?7SJ$dWDr{Q4))9VDM27;&{$y49_7gQ00$#E>$B=OxdlczT2Sh!pcHUlDx59n( zO01C>L0e(8oOp8OPN=WO!gII5;#z+v)0`I&5NPh?S7=W0+*w*W-Z`5st%;F~5MTI4 z8Tw1ENJKGyC+x?|8xEyPFyXxhs_;i*(W+d(EvMLO!w~FLaW^B<oE-i8WUr}1IeeTt z#Bfd6p&zTGtN4aPlsnJIuBnT|KBJVis#%t@s%-djaH^^aD)9+IWqmTUbu7v8@WcV{ zP1--kPh=AFFyUOGqN08?FwhMrCy9t~(UPMmEp)cf6n@S&^ho)3i18tBIAHlCZ1nIe z2M1hPJ#vN|uqNV6euQ)wo$peFg!r#@9t0I(@XOUkw49s)0ezzBa#-wXvQX3$RAK$U zhK)!WIFeFRj}r4`(3pwgQ~p)35MC&e{=z2fzc-38Zh~+L55Hs;!HJIyU&y(Si;HX5 z>K!cqt!v9}YH2B0r$_|4*-7!^U$d{j>0}Y}xVYz(mQr?gb&Duq{PrkQ&876fc>4*# zDogV64Hq|i_Ar&(vysd=b}c0Heo;%9=x;@CZ7z|*66$Gtdu&A5(y}tL;M~EIvN94< z(tzS*>`(-R>4_oZuy<$P3#ao}q17aY@Ty?y)}~BFE0mU&l6vG0zF)qwlExpEQscYu zgz7NV(tr9}I(Q=`nm~F)MV5q_`BTvBGV6LfDqnK%At^I+=-L`ShUCC|q4yy~$>&I; zn{<f}W_u|!U{4DP56vwsEWB&fvd-pg#fI`{yaCy_?DhT7M&ai_kZ;Ub!Lh3SA+M-L z)sih3&PSSw9OR$9Nra1gWZ$YyDdoB@O&*eQ;FquY*NFDmiL!LTSq0UIsXuo3$P%M- zckPdZ%n>M&KI6aiJY4vYs>hlg3E>BnY1MhACn_5>ZVZ}%#$GX3ACjJK%I~@Gk%fDE z6w_A!_SL(s@&*T#pLuSprp@|*0zXGaM&zt?dJW5Rb7w-8=LMOzvA)O#XF;Z`Vqmdt z1F)pC6?+v`1yfi8l8Ye_IZJ403MRJ9Th0fu1;49sn()b*dO6Ts!m~~-ZFW`Q!iR@< z8POCKc8A~rei<5kWIjw?q#N8c?b(@DSf?J?H0yy5r~DyJGR=Auf1{$p!&Xr6Jp(rs z3fn&<6xn8<RzW#!l;+6J{1c@I?PvRd6ils~-FWqdodpPg0CCJ`n7X-ID8rxdQKSBY z1qO&A#Jqx0>$pAj0?nCs{Lj(a&K&`wkBV`+e$&8!)%OD7U*~kAo>W%(b22s>nw$A4 z;`KRwAUY~`6AK%7Xb>nV9k&&4l6wtO4Nk31S+SFu47u^xt@=(~-Zq`p=jJuw5fFOK zShp|T6fnfj$k6{q5TAIE=}_c&IW|82uhrADxn=lyV-cJiY=G_Zq#tz9xAiNeNtwAF zC(C<e&-8ioEXQ_Tz6MylOM!L4BRCG!yu~RQ=;piA^W_dtlqX~YlgT7`X0>zC`P7L% zuaw3_-Pc<~_JaJFj9a8AlrX)8tPbPjZYwZ~_fUr7%B3@c>*Wy*pc-^#`@PkgjbXB3 z)2@Vql;U(=ndN$S<F3K}LhzHP(rxW=Z4kDZMdx@6Q`F^QWG_DdpD1rCOE;fTfzsJP zV1X~ugC49zWWJ-@=_u`|>K!Zo)@$Q#H65>4?JvI7;m$mZS9i;=ZgoWTFTJ%g2q}Gz zz)I8gnxtmhGlve^r4eag@2slMmqb?pesFcWb=Fs6%f|8X8*}Zz5@#OyYJ6`>thXbC zh7<m8;*Wu9)wh+MGFki){Py0-{N6v(s#+g+Tw39gk%g31xunVeg{4Ql%S5#K;F~HD zC1-L=ON*Sf7Jxaa^OY&{&Ng|7Lrl)=abp5bI@5H|TE3R+1v>L_l5@j-hd;6dbzGp5 zXYBl3ODF|y>eIt*N|`%d6>Q@~&y++5Xnl~~VF$=!51IVLl)JgOR!s@$9Mv&8G_}1v z-9d5K(LMgw^g_4cVZuz~+W?LClPdI&Hp=DBt}&`R8AHbtN~NrKva!qgQM2tegWJ@d zJAFpmr?e=5-{;TCTBmhfR*-LA-t14l9wY+mwqk5Bs3tXeQ5~6!y4d|Ia9_YaE#f`S zzPsy<XFEf50Hu$5p`=EOy}Y+cQcfX_SFZv9<Gy-^>eJn%WryVqFBf2*RUK5~@Y)u- z2q;s+{a|v}4MO|+=DDcd+SLW<_A_qe-Y+gm^J_QnPp1Ax`zN-PXeaclmpj$U_AQ}A zRnH_CG71XE{@tXw__?T2`ZP(eEVr84+VAZqNaS(yNj&b_+5|$~-6|^aY<HFg^PT_A zh}T^KGP9|#d8N31Lfw?1Gl!!C&*EC6{gRS>-gTiAX<-PAtc;w(lLJ2}9;7q()JZx) zWhfT+ixG|0)guwGW)tE)wqAa|YrLs?;~^Gu`SQkSuldR)7J93b+w)wkrMMGy8K4BU z%hOwCEozLjW%->G7^np6xiZ`(<jeU&C9P6eGR=GDr~NhFxv`mb*;>)k)X?S(51Y|- zxrqzl)dX-Lrp<;8NV_gn?|ZcyCy>1<u#FpN+CHxQWNBaavo_J8NO-fU8FB16dN69` z!YsP|MEjQa`6JeEA5oun*anrvQ_8^tD6$d|9tFDj3@3>HbqG@RL+a}F_(Dw~d(Ngn zU52%0RO8jsSTaGqyW#Y(ak{9rq5GB3gPA!jd(@{CAlT>8dyVnkJlUHKvI`GrAf4{S z8kgfYDXFhCG;zDb!^z``v#ImSs;U@b#Eg{5Eh|5+xsi}QG+X>Hw=D%ArcMx>l}gzu z>`zAe{n}UzX1TFlv%iwUmGuzpJ+;eyPx&;|$Z04da8C-r1BtpSq%_T-<?KzCBdq8B zN)I3Rec6*z`&_`QsoKk!ofkxfyIoT%I%E{w{7yEPt<Y6*@j+zPlLCfH5CRf(6Rx5& zw-aptvB%MzYqRHu$AB(xie%DDYJz;x6$r`KMBRJU)Z0m%FVtjkPocXQTDCv$P$Aqy z0k66_m2mL1hAlg{cgkV9IP__$q<HV{AinUCO19IY-f(RxV{r7`Z8c+!!-pa%XWPO` zluhGen#OuzYsteUt(I#6X^Cw&|9j)48LLD8E<#AuxrmDkyQHKft=6CD8$5QW2f`Y| zqobpb$e#)uT5x5H;v5bC1!As*nd8ugQ<m&(yP-+gUu-gacmitYp<WOf#y`6=B*fj# zXBCL*`Sr;w78bQs-F{Uo{C+oW%?X54+5Ut8`?ibuB^l4I6gM!NQfB^X*}Lv1*dgMQ zLsusGb!qG;U4oUzdg+90RlQmjWM}o7w<@ReZ+Dg7W4jwdT;+T#KMUD(<Cksty0J0* zO5Av)f!-;)0<Ulj2LsIXN<f>m*++pfYwu&qiOIa;waDds1z#gRF6eW8A3H9whXR;! zkDp<sjWvxk>*8s3YE`PZ>n5}}zcDZq^smzEDhpkjtSgMYZCBRR!o3ezOrS)doUPCO z4LfVYx6XcV&3)NZ#AIz%EpI<&VKI@R;O{`u&{(F`?MN>#x7yx$Vl*7Mp3StLK6El* zO<~leH7i}2>)iEW{tHn)PNSpO>{YXu7JkM@3+=TzpBY&12VT7SG^FPXScB|V^pT5B zO)e)*P;D4dJ;%%1_`RX!JDXy1=Unf7Z67a3U447(SeBGtdspXCE>*#3{I3T9S};gC z@5<T(IcWkeZz7<ic`vjZ%YUQMctNE9AZ+76dNc%QRT25xH}m|CaPOkgRpGi32sX}v zbK+0FCiilB&qv+1Apf@32Z$0l6g#sgprp(zoV0_yH+o7d2+!xTj84YO8_PTh#olfo zjSyfwknzUV=|-GpnRG%y_rf@OR<<*r)hXL@6=W^UrNePz?$xr^HMw|V2uyrq=6KF` z&gr#ov>GjAZ&U;re&uL?Chx>HH9un$D2L_-ghy&cBEC8*mnW@@9Rkw~PYXaGqner; zjdl|@lQ2NpVmUbtO&SOUvN=3KZq3+?o+^abCB^z*e*cMvhFh4C9L(0PqKetBBVhpa z$w)uyfUB0-?WD2UZ(l~K9VX{#Et+LT^2|fhtt~M2cwDGz75V-hyO~SFjhC8RTOEKw zhs7KE{}5^i<OS6kv1Q5;KkZ4~`8rq83U}Nr0WXqD!s0gfA#K<MzPQ_;sPM2B?o1Vo zu`iv<9cRdcg<VyY3=+y1?u7q<bpU6h;pT$r(2pzvk=cXQDuMd?`XaX8^@nc&wlrY7 z9hSeB{LoDC$_2i_%cA}XT5e`q-mw$RhYvAv3Gksa{k2B1egFTU`3D|+N}+i%A&UOi z77SdFf`%%YAKHGxpD6_ki_((vV!@)tJf}yQlyI-#q(*<=(1XkXnMH0K%T)KXdzQ4) z(lWAd+il9K+UsB|KG`@Ntlxj(WnXiirR2C-riSAD$EZ-@Mc$HMsQY-bvr|-`wiFeM zEsk-p3OW(~VQa_eX!9SlM5?NM$>Sza&``TdV)i;^<)vR4iBM~o!%IEnygE|<wi$qG z8X}Z;=x&%=ntO09DmLiy&}=AtP5wT1#RuQPR}KpEh2|IStw}P>9MjN}jTrBBJjlox zIYpKEWKqsfXDJ8)x`5JB&<2ye{zoLFZhcn;h3F_Kx;U?YoZzFq8XEIE$&jsLL0zRl z1^|E?PpD?ZEtIshh(Wz_ibBMM^AWiRq*VV!h$#henEXN6@4&!Z$a&_n+$O=_0Rh<% zs;a7Ub1FM7&7_q7Y~CKDFd`9;n0}w@jKsx=0kH3xfe}?q@#5w(=I6iX2D3~kXll|Z zqW&sO5#PP~51&t12$TP1UiS1i@>20cfBQXl>|{*`2%B*t{_lk+4u}qr`Ok1-ddl|4 zSTXM<C?Ds^uU{cAqFX@luPO7Cp7X020FL-79bEC*eqrh~U9Ay+s<Vy7$W7G3Yjw_e zC9wpL+ik~=zVmQze#57$Gy!CyQZD|W*N#N>yDWUb@1+mu5B**QWv_J^wpa6KRT2tB zB5@cFcZ&uu-FuT>52m~mrw!+0rvm4<Re#55LQuG{mqDL5Ne4q_p>!U<A+vQEhmY;} zevyX?+Z)z#m2UB_F9$8}ETuO{{W0_U=~HHtEngY#g!SoKK??8lm9W?NXWpliViq;I zi%m#g_hZ35<eJSLGtktTjLs|Yv&)?G-~#4Xiin-8MK#C4sIp>*%=Y{Aiu(+EpU=D( zN%rAvYT(<r))w>5pGCA$zOOA9d0g?US%h9AofNOtKKJ*OeK46JrkzwUx~%CEUlzbg zS&IhFnWx}xTfMJ~>(N62kS5{CJk6e0_uF|^k>4l9pC;B`uWa62B1U&}b8;XXHPLwA z2Vh(>tovtH7C=1jvPW*{+D8!)n?H67-tVAT;I95|PFY70rBDIa6I}jMaQF<G=aq^g zXjjt)Eqe&IugQVI8F~#)$1yhx&u~$o7gM;cMve%9m-=5)aROKoHF_sV@)`$}{HDY{ zrv7hJZh@SoLw0a1hVdvo99##9>pL!rY6noYDFey_gT2$PjTdX5e%J6$Hs6yth>zY8 za3*I7tYZP8HN3zUY|i8e&=U8#=a?^@{xAFUucxVxj~-_oPd36LBA1t!LPeG>Ew3QB zhQ=ERSNG!9cOLC!&1Z8A0up#ulv2Ml($Z4u4flNlVPRo<P{7!?`wOOPwl9AyHMvO0 z#WD)0Q$qmGZ0hpKL)i)fPEFN`Hk%dl{KV^FdEJ3SAD%&v&DVD<P%b02i`AqLmdJA) zNF7d)c^a(yu!VOKeOMQq)SPWw=g+RTID`y|p7V$J>@&5Nl$_>M2Ek{OnhfEhZ-=;N zuM?pN1d>+_TUTXx?t5?6hGIy`XhGmTWq%yJa=10n`6Q^{7hs@WAL!oo@%0HLH~>SV zV)CV?fGJ>^#EWvJElJ`vLpsxBs}zp>>OibpG!4^k<=e7bvVc!?uVQOb!X3O`H=Uuo zlL?GHISQHia@+GcG_CCfJyH(S^Sz(q8tVu^pv%}&Z<HeK{;<?z&BNEG%@oz%oQl?i zDhvumkm|Ux4wqL$xM{c2p4ZKuk(iUS?t#(QjUrAY;^#(B?h^&LYY7z%$~l{htL2n& zvLmG%5>GF3Prz1ZOoc#@5%A1V;$D{Uy#2OtnSqJ;+1u?p(Ps=FS4Q~HXXs7ZvCT8n z+iKvl(8GF8-o3Lz=Y-POYj_7|KnvZ2k$c=V4SC=MVvfy=Tq5?sMIYH`7+d=6AJEq( zg=>7NzYU?`*=RN`NDUpoWYN-Rcb)ZvBatuM9EZ~ki1Z%?D>Kl0v&~4jRmz(m+Aa-n zCGfgYfmvDn(@~H@++DH{-Cq2jJQ*)+xXC2<!(}_zh0q^C`)`r98)j^_d=375kOWCp z$<WQ9hArA;{9pk&hel)YR**Z0jlJF%93{?d>x3EuDf?1uXZq#*jUg$0@0DjKvLk>% zc6V7FTw?a>Z?EUcVcqF}ds_F4pEp%(agfEsj_9)x?Dfww&ZH#YB=Lq?e{ie+aFiqm zqT$xHLXRx<yu{VR_G#V;t>w|%;8CNm*m8(@&7epYzxPLY_-=!0C+gwYzjLF9sqn%^ z3ws*UyGk{<!^N;O9TG4mb{L6S4&fw3g6*SLYD($?Mt}D5z-abN0&lcH9jO3*J>$nW zx2^svvJs$BGtw(-%(+L|3SC}wJWtp1xp}ZTx-`8me`@|}M+2bu34>}y?%c$NDmzY% ztB6d@B6OAfGWpC9$Z(d?=UrmyKAYJGu(>v!?QE<mEp*QqG1&0R`?X?$C1pnHx-Jl# zL|AOHfbhFXL<Gl%c8_Dy1l{+I^=tw8^5E*?A};UYa6mZo8yl06+s8t0D&D%8P<ryf zr*rH{Ph!F^?eX#&5?LSmgJ=M4@d8M+JLgpuhK=i4TpSU*{aLL;&tlXP>*OXI0wJxv zjt{3<F3}6wsQ5VFzK0NSWTS+c`20${ncU}~Fr=7H(YW8i;a#sv_w8_rhL)mqU1JMu z^xZa?)&*0}r(kn4hySeeel}92%e=rQY%yi6S}IMsGa-w({gnCAkNYT})px8u2ObqD zirs@zK@l6Lu#=tkZgLJ40Q&w3N^-z0QAY|$J_R+kQNB4vJ-T9dA|EZL$V-W0&zev$ z9oH}lTa0LU{rO3ohUI(mERsC4*gb(#0m73iZs!dY^7~*_^}$K}5fQ1!jWI#J(^fDf zRBwZ>_d(-Nkf|<RVPW1b0S54r%$9q5Pkyt+k`7ck3R>@1vsm{K{<Rj*o!Y3VDhF<z zXPs-B{vF`3tL@+XmiZ&|L6b_&6BF=kg21FKakV3T?0&b42+sJqms3N?8$l3Xb!9x` zkKWX5%O?HS8fHb7JZg(K5(jCM`6BUoo}1*dSL6>q-`KI`v10GnWZx%L!iIV9@tuX; zZ*j(ws5EF_8XL)h7RRSZ<dsDyLF&{Lbh?w5Yt*d$74y04`zLNHXLrR{^E+SkowJM~ z?U%B9aObLI;}g?Xi~ibEa#}9XMB#v6r}Krz=l(W28nvgVCpbJD6iQR6cXKq){OhxD zFoMcEIdEU^$mA;tI#>82Czo$AnB`{djjY|wCtSO4Pg(mQW=^bomOfG(28(X9B-=h1 z+;7mQnj|eQ#J)?I&00JEGuMB2BT^boy!N?wny$)`U$?(r`p7OvvB`#`6W~fVDPaHE z9*K$c6xhGBdQzvg{#~~>kZ+u!#`3|^>1z^QaCTI6G=Azt2f=tL5yyJBwb1+t-%{5~ z4e?kcbB^?fsp3%@(^Y+%v)<j4s@mvh5bBqHj0+RRClV78CtbpngY8q@GtenMW-)2~ zN>4ZzS~5Y}z_b^i5F|H#!u*L`8TsS~Bh~sgAO@N!40{Ka^%Fm;h|Jf!C`FCeXSvt* z$q(@k*~C9VMS*szPW*3lwxzz-a#VOIKkStyRe}6oyzHrNB4iTlHlYX>ilu92HWbb7 z&iK}4ac1o7R+M@>l6NQA20WCV2iKJ}$BaJi7Ogi|xu&1z1QJL0DVQ9?0k>nq3vCT4 zNS?>l?p(>Yc&HM<N9GR7S-WE?N$YUa89|a&SAx-xY*rss^9jtm9P>l1%2M`y)}n?L z$w<{*{<P)$-#a@Er|Dvc?ZGM4d(rqll~;~Wb}lFkc6*OAI8@BhMHybFEA&LX3!sFh zp+}0PeD78of4ITmZfDR15R9(H;PU8hOxF0@WXIyC0Ver2-rMe{tunLl711GsQ^vbc z@Mj}VEfN<))i2JWUCJUlv7)hlx)?vS>Fin;diwEeZpe?044=2e_t21=vh)7>v6G%U z^1}{a`SS~;+Fpqu(nEbm_`NpmP96&t?bMH>MHArlRj)zy5T)oxVwf*sA6nOaHX5oV zD2xKT{H~eQY8fh`D>uq)5L3E(MzI))=|A~ulib({V9>#}Lz%JVmupYV9iM}igBZw+ z-(cDEt#-|{uk`Bjb~mW`Mo4@o3-aBkOsh(Yj1eqIMuBU+C!Mm}rGKO{y-R+A)pm%o z4)~uzl{sU0_=3d~VhN)GxvGYoho$(cJU%6n63}9P4fJazk_=z{FJ*!kuHIvbW9(@_ zG`jd|XG1`@x1EC4J_a@W=fcnkN|fEH@{Y!B)ob~Ww0;U2$7RKw1}U6P6%Nt|@gVf5 z1Yc7?l&Q8HQ_8izS2*E_yibsnaSXI|DT9x!x?QGD2T<`rtVmrj=#ocvz(>SwFA4s8 zjidW{<LYw8x$r4yP{3Xa_4Xx&b$9IML{8b=P6~BT8?%m0#%74NAow$4=nMyi(J3th zSNlCqk8to^bzDvDYkZckp$e<|`a)I+^vlI!bBQaC;@pqx^^3bfY#d=2B-&BF4P;Fl zu6|J#Mk!!AW5$xeacG8FoXq{f#LKOG>+|;Ps&8SjLavjEK~{V0GKI;u=;Lu$*fAmq zEb@*@v3l-=s*-OzMhDH2;Axw-d`ws?^)DCCV@+p2b8YLK5x97bHvFn`JJempVShF# zjBLpqB%d(7b7cUZ<w(sTCwXf4N|0!{I}0=gog{a4D288rNYJZH-JDz;zL9MXWJ;FE zO6ePS#^JpK(<@bQqcNm(Nzbk<1()WO>WE6sPARk<FMXn%y8zeneAo5eCag~6Kq@>! z25jHLT_v-(Gc0Pc$2NXJCVRMX=56l|EL)HwvkwoR5-DlTKuNi4Rk0r7U6o-^p!Xe` zd>8E_cU-yGFbm38aeC6>L)N^KVuC^V3+L4{o2P}cuUf22qGnx2I_+)q=f`;=EUM8t z^)LEXM*>B!e;Qibc<%g}qQ&GR_s}fqv)WvudCP=BbunuK&8HbTPW8`nbJEIDNxPMn z*^y>wz0L}I=8aiS#!ng6x~}oShgaP}EZ>buzd&@JkcysGqrWR#f4kRJILe2yu9ZIS ztLw)+*g<^@-{eTm`JH1&1D}%nYkk4!usT`YS~7~XUp2JMqvZnM@>xxpf|U68Bi{J> z1#YRsvzXFg{O?BPE#U(CF4H8?ihn%wakT692CtIaS;}068~6x?M_v56sTJ|E&rYpN zm%0UUuY7~yJMnhJiF;F$l3+P}q7aP}g?^H23YY5LRlH!ya?|P7(GsG64M(w$RW>6l z+bzljs}<4CiPPYz5pG@<B{`l`Cp(T31*UXqYmBTokR;KEle9!+F4VmU(jR(yJ<fFb zR3mncR2ozj$#fg5D|*R%=XU?m_XJ*zly~i~8ik`iD*gWY_{Hb8mcT1P-{1&Z`Qdmp z?tc3Dn}K&br_N{eepF>Pz?(gK=acH}<aw5Wlpk08^tAPx`9%Z@MXGp>T#Q0Pv(~I# z*{2SZb*fs#AkFT@TpnT$JM{&H1F-S6*sHbAE2cCAbdM^sZ^Fc$NIhtOK03w~tRall zC?a;rl=)abLp9=>pr#xM$U}?rU80kW>6;StFduwTt|&M1{q3jdm<Mm3cQU))v3Sb! zp=$Kp?ybK4dFd7oww02~BD-mKP`m>DFLlPpi*vTIMi6=>zlQ8Mz~tM@M*ZcCd}N8o zNg^LsT?9Uz_?nptCKX`w9eo@VE-ww~A_9aeHOB0l@duoje4Q+1bnU?>=b62{e^PRA z!+VG{03SujWb#IZtnf>neA1739jsL#3!YA*C8`7y9b(oug~uSmH~y?eZawOEu&wm~ zAVA0cf>((`$t^Fa2XQ<yA7@@_DLF(RC#l6%oNfb@t3cIn$D?<?W4T=>y9)K0WtD_I zQ1!tlu)mvNlqqV>e)k=UG_}@5BeDe_+sJpFALw+@+ss2GMTn?=LZQVUQ;qz@OAK4X zz8~`=y|>c^ak~JZ+jGSv@ol1Jr~3PWW%=d~l3IEwLlTfZUg!ha3GJVq>q}FIU+`AQ zM|?kv5>YlL_g5(Bw_hlr-7J+qe4s~%4uVm-Unf@kvVSp<+5P-5EeUGR8Wtjp*E~P) zx0yulpkHV^%UI~Kp!RwMPtC()D`~@sK18=t1^E+<F&~^2KSyrQx}{zo7e<AYm8M1j z&P`^MzKCnZ_N9+LF>x29=OUl^A>a|pDHwk?Z$_#M??l4PUqCbh+39xB+8hbQC_gfl zmBxQ%`)SV6znx~ETtW8ZYAf9(u@R;vrj7cyyJKAPcJTnZRko4m^kvGkR)kRkjTTEI zK>1X)$v-0AiiWf?=U2h-4R5U-l>EJEbya?D!T;?Au*pi`R+U#s&djY41F<i}Y33Oo zJ^RQgjZ&Ha{NuKR!VhT3<+kwNPVOZ7qMeE$ogY$D@mrP!vq9ZQk{Ea@y`A|ap?oVz z*xaILvVk8_W9zH@#C?=yLu1KLwYM!Had9O<M`~M^(_>6(6Ai$mVOer)S-orUF4{aS z_OiVLmLw)&)Hu27p84v1y%RO9r5W_IxFs7=7;m97J>U^}7kM~==*%Mb-|4fy%rkEu zay%$KDt0eaaVQ*cqt83Wc0Mq)&DqhZYqMtYNIXDM-|X^yk8Sqn4ccEC`h`@c&S<nj zd)9O-b)w31PihJGuz=t3x^3uHnchpsG1d)JJi#r65SmKPr*MspR4vnSg0`0ifv<vf z2HGhoOFg@rasIQWsR-@98wD+YuR1~_?Ox}@DIZaP`KX*?!~yKRI$XtB1FzW;;;D8` zDwp05N_$qX@g)O!S+H3)t5=8lwWO^nD%~89{hJRvU~<E4z)DK)_W~Le6CMW*;6omX z6!3-M>wT@?QYiRAM2f#8!bR=*$}=8M{OaQQa_mOAYkBCEjpxC^AG1g<Xc%KiRp4l_ zw*$gPDst~CNPTA5V#8;5LVFFY#3v7&%PR6^#wMT#qwIm@p|h7YZdW{@&Il)aQRH^t z?4nz!ez$I18o&-MxO*<>`#f0_b*6Cm`O^Oi?BVt4q)GB87-v4Pekcl$a`Cu4-jfeA zX|&htCq^#6uz7*pdA0*BC^!o1nt{wZMiO>}KWfdgA_SD87!71p2VnEXpH6PpH>MTY z*}jU{jqK8`TIs#OEh9E}G6+0M>YnuXd)0L&w;5huEiFC!!1e-8CJ6-O`V^JMf}TQ$ ztW}1eEZ`XCkhfPfUYaZeA*YHNDEDf;`$1|8;CY&$%Vxfi)y$K{>jxK3N-iaNk>edC zu9UW{lS%7Zaqt1}A1usEeW}k}1c~Q*`@XIQ&DNX<X9Ph4a^QwS@FEI#rclkUUUw!m z?^|d@T4mUNA>30=udKTTu{6?8m&et+QtzS@Mx8|9^efM<16(U`DbV3Y;ma7|1WXu| z9MAGG${(?RIWYG(T2^wtB=&^zfS%9g-Dq-y2j-%ec>-kgfzca0C~%*G#bDjUr?YM= zNSV>8PU|DXyMoEP<t!%l!M6S8i@v`8ciJy0w>lq*>FwClB1T4}&f4#EdU|{1zbB?- ztRAlx#c9+2TMQ0Bjg9_qD+_Vohkhbr&ZuS!x2+fkH=(b@Epxs{_b0;%UJLWP|8&+| z5r@G4lS4^~|C4BiKS~b$uO;F14;f8CGTr!m$<ri{pYcw@<gD|l&ly{l9E#%}D*uVr z0jRmNZ2}^NC?=c59rr5u`h$CPR`L}CYp(dpuy7;h?0=KTUnLgmd@|a-bh(onCz4OR zrRftLbKr1{AGXr5ofPf=wWQ4L>L6`-^A`F!3}xxqY6$axJ5N5^7oyjw*&-YWMCvCp zBNwVl3;(zD{Vs1)5YK*&W(|>+NEu#kh{I1KSNyACzdtmXB`f;yLO!)`%-#>fdbO)V zsoC-WoSl#oE=0i@>E98iTCMiu6LSHrCI77<G|S2=+hP1(JN*p^pW%j_g2L1bx2fwH z(0;=Y^Wlm0Z9~>jn-5u)JM6vqSxzZ+DYY)`Uk&%|9OfLIIpg;Q5nre~_ACQn!NM;A zdbo^Bi9v$--M@7HcU^woqTir8oAQ&c?+Y6e5|U24x*`UA_Pcy&m%+7KV2&FR5%Il3 zZOr7#9S4PspGlIL>fMojnYa8tbe$-CuGk9AMuWyxGS2r0LR{S37>~qL;d9z;1^`V> z_HQ2>aE$KWcfA})N=ZvMpS2vnmq^fi?+S=6o5r}<SO=X3*?+;vC#9i*L!(w6E%^S& zCL-)`@i<YTe_i%*`Zyh>Ib6;q@4vMJ;cvn&UnsvDm^$3lg?)@uQTeZweWbSDUG(9{ z1C|Ks!2BA|V|r@iHw0qQD0ECrGNJdX(}goLGYDM7EU3wx@t>i0f-hKp7R8J_lbrA= z2)6UKD*x9G@r03ZVeTQDMJ-CS#(obr+T@=c!=~b#)EgY6q296oMd3CgxzR$oKR)+u zO&hYe3?hl}_<?zs_kacVxY(?X(OIxi0FKrihRs%5f^JRG0#XYjxz;O*$&EBoj}=6P zU+%RLzjke;mV|O>aV?_$LIMX93l$W=m5mI^kFo~%cW5v>5L+%G>8oDSIn*#Ro6kow z1usoOba%|n2k09*>Y)+_<S$_11FZE0eqwlyqQh!Y@>uN6+^@YJd;Azu5P7Bo6V}rb z59|-NM%*OWo|m#~?g<gwC`pRp0B^J>efs-!jytRz*hsyJvE!!6m{Guj(bn8=7V9mM zF?i0Q#?ddz`NzFWk_aISqmfwey=TaRHx?3?!j&UXJ^dL5JI8MMn3mtd)=-<|((z0` z(v<4FoSGZXe@<<;s*)$_Zn!zJ7F8InX2JF&V6Z&QkO^ucxBZ0HA-#I!@D~M3V`l~% zpmQ+Q0V`V4M})H1CTnOZ8WTeu+H7eA#gVfU17&$mB77$#e=Fd)mJ%s*EIe7f!S|KF z4B@0pk=mc!ahh`Z@;%}_=-Y*Y+OI$IAe*vyEcCg61HJ{43ciBDj&Y>v3LWz2(%#P8 ztg5F*JpB(7t3EjgPG%01k(^lYDXrWI5yy^~C?Uq17fdpM9V2ZioCy{MF69fNuTJ_} z_N&0A?Eo^D9`DuE#*K*jQbKDxBK}XwzhXZYgr8HN))f6l%~g*Kr|r{ajl<b_L^q$# zTN(KVtWP|@!_Z$8yu=&(1sErD0uFlBHDh4u1*eWwG=}GpKky|ME`P*k_8+A{{7S!u zLSj??yTnSP)^AQq-4SOz2|t0XR+V6ME{5se-C}47OhAL&gza`{RfrTekh>6F?T$0- zIbv7u0gNw2I+;Z=Rq<jp$@AZSv6S3M_ESCmktUeO@5ll)g(`eSOm#joox|>domE%w z8d1W^CYc&-Xwcl3t2^!kr=c@@qx#dQbk|LLUo0+DE_WsxA4e(`d91$1y>FNC=3Y6g zK2@nC5fg~De<5L1_ny=Q5QuWv;i}8wNqR+pwwqPR?A|M&;Z!MWn4UnW*FA#Ao%2aw z5g3_TeeCBi|ADug(Y7&|GA`+fO;XtwPSu;oRCa@La!j-#oGvFEI2c(!5LsUsOYI6# zJ9ta+tbikC`jtdkFz2W3m(I}~bO-8$0dDm)+i6xzj1M2C6d*rjtg*h&ukFz>2j(1h zH_=iJ<<9}OH2C-8QAT?wmKvLGQDkt(Q@D|TxVb`ju*Ubd(@`{Z2`BomC=9q-;wPi# z@^S|C%t%dFnb}pp+aHM>aF~Bl*U3|Q>8!_S^h!9m0C4+vw=Q0e!_8x(7gb-xmNl2^ z@=ZKjd))}p?Y**S*bQq9?6ytbQrGW#`-o7WCblIvCd~^@p_%}Bvn;g%m)(hA_A`ph zTv?q#3HgI;QpDdt<!!6Snd^oMg2m-DXE|zJ^T?e)(%99m(4+j%glq<^j(ZdOYLdj# zN_bN(Yp;<<JEtzr_eg2q`brtxUEM#}<Ux+xeq5X+^fO&m+K?{mGQXyv<womwEalWq zenER99<$+)J3aJ%5uTS*+(22lvNi`sua|uXyWsrjqpVDCjDEK({p)*|uhq&)T)D5X zB%C){FjG>*zP=*Gt0pL{fR)8FG_Sxf)#0I+Ng=%NnX~ay)@a0xMIuhUhNm_Bhb3LX zp{7QCtKSmS3%u29??&=_h~FM#x9WYFHJvm~e<d32oa`%vbSvg=%80)f9w|F6t{5WQ zeY(L&g=1tr$v2GMUHx9omJvJh34CY0l|3&^+p1B|7dJEV$>G*&@0%gxi@_L0N0kvC z-mJw1MCJ;K)sm9<#ldACcIyWDIk*}X_Vz9ZWXB}Sf#Q@~u`$=2=j@~4QyXecYRfIG zXiP?CuDO&BH~rF^7739h_q19jZ`z<)_xmP@+Rkj#zD3as>T-6Ka)ds6X60vnX?*$| zv=P9waB}#xP4KCpGLO?aKj|h+Ig(kvUgw5h&1N{etuiunjE)xgL+GGno32mtFO>Km zjEC)?(-8?vkqq_qfP)pW-isljl0}(I{)Ro4rE*ik(RYNPyX+fPnHbye&pGm@vs*LB zRXevTwuZ(Fv}egrM2j}x$8&z5#C-ONO@}8IWynSeC0R2Yczcw<b^-T1>UlXU97Ebg zcOLR6J+AlNLptZvZV1|xl~K#Y<TZh2!mu&00uJvvV9wC#z17VrX#{-C<Bzg8a+Gv` zxbrSg;m`394Oof7u_B4ES?%t!HiVC`p6nK|KK7y6s8QNE*UJ*jen8R7E{LxXyOnFG z_vhAZH&0qHdB~noSb6levAUtZ_9MUkxw3oPsGDU_i?ynncC4=RJ8#}>RXI*vYU8`f z#-%Uf(C}9dhNN(kLyURPmGG+%s|-BB%1<7;ot(xzD_Zh-)%kzscSERee)lgGiZy!a z1RN9w$VA!dA-=#O_ZvX0u~Q?U)I2B%tuEfWz*Qv1gF3t7k5gFA*ase{H!F$v1#wo} zzYR@UEVn-!%dprO+=f3T+d!;2W0+vD19H&b6Okj8AkylFXH-SPDazwDq<!Bb^gS9H z$Y1Heirc|D)O4>t>2p%`ugN4+MaA<j!;{f@6k1`f!-Mof3krP9#Z=!x$<8MsgM6Mt z1N4lJj8XYETV=RIY(GipOB$f8-|5mv;8TN#^8W3jdET>fUv<gyh<Rh@%cEPh_U`wW zIrQWG5|eW*+i1)r(YHQPv>@#PSr+oRntPH3%$8NN{ogALt6|E6cNNzyKK3In#o!Zd zdt`Lxd8xfhS)<HT+?LUA%l8aQ><!gpaf3;=ugMIWFOsEP7hj_QX7!O}(G`|DCdatT z%kQ41xgCWTUxynDDiN*HlyU9;Rt%&hAMZ@wEKl%KWDYH(&+0AiAv-BkoqiZZ-BIrM zTbH1;=J%>V2`28#R+yEQ%(Ioj_iGVOwE<3lo}&0tn-?D{UU6)F^k)eoHV{U!o*~N6 z{nwrKP2mJVc^=Gb_Os1;QAT*}pmU48v!c3Ne0)z{zSVkn)9ShYOz=l`%=x$M3@n#U zUa|B~^5@NZe2uzpx+soL_>e7MOVf_G09gM0iW??vzwft*|3d6s3X3t5(M;-|BJJ^V zrjP)*4`^tg2Y*&4IqU{}OeLf;|9ZRaE>nlE-+!dy;Oag27;8qK!jY)JSKJ^~x-LXY z_S*mkBg<?+n>}*#jCX*5H&a6y4wVp9(Cw{cECoaA=9RC4+~mM24Z;vYKPE<09A*pp z9!3Hc6G!s9-YgPTsP9*iU!|Pst5Y+va`p3R+>Nzzn!Bz*{!`&}dBfJnKhtB$+_4?g zIWUh8=&6j!K7Q2ByGUl%^;b}*NvHjid^A}%wW|z*5>v$wYhI@qxoz$~)<a@rsWY9; zgv!Y*WY2S#U$#Lz9B5GQ-txtT#g1oz&3h3GqjZM|SRyv7l{FJ3Rwl$*yD0QwuLf~A z@B4o+TjMH>zJl0D*WJmkC_q=24OR=J=cXIRjTaXxSPg*|HOdXqczo??N5x86Et#yN zq&7_qXu9=&J>H}D2Aa!MCThn`vJe9YhihIhn>%-3w7vKmbr37B$)5LDd1c^5;4659 z<-E%v+tyeAi{eYmvS*u>BJD6o^+tr$)et5VsM-N-zpT%8Dsi5hJRR%m{^!xl)^-1! z?vvz0cz1f8L9iPeRZIpkITDz@Xu}tIFMG@Uo<-(Xx6$K3TBYwi!?Dm;X&b&OFE`g? zn8WPKF*lTd>(sfx)OnK)^eB-Wf5&uwUVOBsx0J^1SuK||+NrSZ<##J>Uc`y_P6g!M zGX7CSQq#Y@E?8$Eqc$Q!!eT-r)~<3i$>kIA1jT=pv0Y}MavjU*VP+z8w{j3bkv~p0 zZ}kB5tRVbO7ho2qRYMtox}APhpG5o4GB}SX2Bp6kCyM>0TJLcF|Lv>!e?-#$%TPYy zU*w39HctMR7yNh5!Jht|<iP)Ww*?{aU!#BP|Btrr;~|g$ydr^X(E*AE#MK80OsAF1 zUf5oDfctf8XvU0^QffaLuWPPMjir5BZ76c@AK(5_!So=EXIPzXx;ue^*-qcg{Vd;1 zI0+58>dKH;j+ao=%45$+*nkWwpzKWy-^pmT!x<sESLM#UW?iJR{F3eZa1;{?-(Xdh zbUZ)NfA)*6!l!H)Wmnq)fWEbfGcJSU)!{f0IG$m48FBWWrX#LOe_Y^o&&#_UC&1y3 z_*FV_QT36^C`-1&>H9`XD1U=AX(H>D<qMPTirx0}P4SHw{`WT|UE7iFLO*&t)<!;i z^v{}a{gyPJ?4SmkG<Y9}RHy|!Q&5y}KjN}PS<(}T0S_Q#nu>>)drKJ-t6RsG)8Am2 zFJpA=#HLNlm5P`x4WJ;;J|R~=Cr`RIVOL+k?uADU0+okzFahAh!msHS8?MF=u2wAJ zrUspM&oU7pYu%CUUAU4<rwZ9@C0ELnDrCrxUTkN+1Nmwy!#Ql&!<zfHDfk2U%bJSQ zg=X|q3vHi^e2IJCS{k>f-}1vw_?o>ve*k>_75uv39z2PIr!I!(La^-XvxbYZ@SG5> z7wpEsY~%3*X!1P91kM|@%lq1waDVRYFW?;;f(~7M<ugF?(y`y~dnPVmK)*Pi@$F3k zeY59>S1{%|gNb^=ig+9S+-m`alacARkv_=3;&!$AHr}h>gWO@bm=g+#9qj7WD<OTS z>|L%?XhyE%OyW2&Xkfn>-E2(ubiMxaP-*#8o<SU-h_J`xDJAgB=>}vgpwsB*%HKa8 z;_=NZSU?7%81&@ZCOPgbaQw`VmYa6q|4Sq@f$=^5IMszke#uw&i9V+XHQWA*pwp$@ zD+3{&(6O_aY<&$}JU6zDA(&SbH!;U%sZ5RZU9g!s^-lJDyCE&~?cpgyu*;@k<VU9S zhAvaD)PIR|vZ`ijI+`JxamCMBfTrZ{lWhxNf<|XFF5eET>M+%{#%IAA%l7So5Dx3u zAGKYupGq7WYu-_~J11%t;(6`puAZy%4C3H_iFAI#!~Sz&LVw9MHX%bpEuKrC+C4SE zXC+l3bdxK)0uMd<aX^<Fcir#igqrUmJ<>1v5TSXfIugW(*~BxuaC>y3+UoP&$6wWW zlMD+zr8WXO;jB=r&z3Um3sR9s|KbVd?;*K?o*h<3w*j&5GYv6nWuF<Hkkt|Ev+vA7 zQ@2j#Ff0~l1)}*CMj{V-XKs4rM*}EscUMLWwgTU5BJk9WKVu?SM7U`uzl3#NJ{K$7 zbC-l0-r!=$z7(Rk@5k9{cn*yZiK#P{n{ZI|BP-A%wwx*25d7#aeS3)_vy9p#WX}4~ z3!QC+f`_8Ab(nacm0w`v&9q;+b6llf!#5c;?O2)byGySWN}zJKD%9U)PFnC5B+%9p zkWkhby{vb%!v84r+~2IAt-Sga#W!N}F8@rEXu#U{V<0?X`3?lueWkXfrsDh)ONMuJ zz~hYF%?%L|SXbcHrajr;xZbsPY-^aRPm$%7A=M7MS!lJ7%MKVSEY3|Wjdf!8eW6?S zA;HFTo*^I=D|@h-hXjvG4Xw4rX0W}I{)r*8RHmY&**h3WII;X_YWC|x!OhbrwOTJ* zGMg^#=A?7AFKF!UxbTIQ4a2J<rJuv`O~`%a2_u|p{)5)<B&C*6U$enaC@#*dWk<H# zcZxK&&Z)teJw4gX0|#L|ad1lt;RP-*doZbKYfHsON4nA-;swfC@2vOFg?P<u+0+;@ z%t1K_f4H!`FftVk6?wKS^rK?MrHy{-wcyw)U9LMWVa(NY)ZPMjB9_l;B=CMq5gW4K zmN6;vmUt&^ayeO1PS%*7r{f_M8VTQ4v$5|DBUHVX)185f%otQuZe$!dqtxi)><oan zPX)#Po$UFp5UBGRrpX0<oxKt4$mx8?3KWA=rOsxHZGJXnM{$qFA+cC`K0p7~NPt&C zbw40$@Y;hDeuNh)KMK3nG*(dyz_e6kqO(=TE2%I0jxhSC@l`ud{8tvrr-eNjk%Erm z<iN!4oWP2vXjmXj?u(NVSL+hFEx7uRKH?pNp0kZ%(dvp#hGm<1(qk{U9(JbDICqUR z#mC1SzwQI0n}hC}#H&PNMcC7#wzQf!Uu_WSc4KvXPBnea0wAB&9F}Bz{G=2hSFSd+ zr-Vgl`YDZt!DQNSl5yJ%SAr!nw&k3NtNx6A$GQ8qDs-a1#F`q(c%Jje(QdcSxiH`; z3{t^=2Yyktx*%Kis|^Erg^aOLqff_bWm^3o=P|9hMXsqJ*7@<}ji;8nsQ+?e--DE* zLb2Wr@O9v5=C%l(5$3MImgr~71*5JL*KOfwkk@1NMP}JI?z&_Sel8iw3W7#+M%R}| zWT%#e!6r7=D3=}xX%P`s`DfW#)Mrud7s;afYY(T9!^RSCZsEYluYcV5GzP^pbsFbe zxf?s>@OJa2qnjg~PSwa!u6OJv7|Cz!qaR)9Z|MhnWZi!dR>Jp6h9l)y<@_`zQ=KbV zbbPpgF|b5uO7ND?A(*0G0^eqFrgH}9`@=HZt<8@myM9P}yTX;O%@8O*|FGK0jZ?&K zo?T!496(;yG>;D4QPk|u=GpcQZOOp5tYe2d?tf}qmk>^;Z#H^Azabh0Dw)WcBbW## z&M8WtgMu)`Pg>^=`%^dRZYjnEUD**MsV{pz1xFscsW}me^h8-)y|W+)h!qUTX$@52 zriFj`Xj77%xtTBed_oq_TC8oW9MliX;^y?x*ppe4ejVKL_f7J5UZDj19I$EtTu!-y z70|@xNGmT<dB6lk^}ZSXB^bwO$&C7>ewZZ~Tx%+X<08`OU41!_bNIu2I&kBX&J#ZD zRa!l*Rc$?uWLx3wl(2?)H|^O^>J-~^1(P<i{FoMu@cI>%Wvl_w*oC%|CM_iI5u0Q2 zDO4`7OZY!||3zQ9_p_P}HHF87wibso5mOFvz#uu(^Y(8oS97Z?GD80A3~w5im%G~g zu=RY~`()dzofO}zeo@@6Sp}&Ao&aKzuS}4kT$zVqg`XEPFp<&*kZJ?b(eY)jFErNN z+(|K4Ib!xtEYbv-KA;oeNodJncl@;dj%aUREgF=p06}JlYS=p>DJkHytWvacB%86S zQB(m=TN+-^46Qx4s?5ZZet9Lp;FdRiBkYW_nz}bTLf)Ur_NygLKzq{<hi{C_(D@C= zC@QD_S{{NJ8AVD;6g>2rhl87|q^%XSBJRyF_d37K>3mb$x|JQYb;VGSB?cFYQXRL? z#MR^}0L-Sanz82fI#1|x_a09QYZ(!zh4+{Icr!R@FhE(<*2hKu4>TU+q=Ty5GJG}n zSzO;aM#B({4W6nPPJVfr#D_8HJ;za25+2}VVDAmC!tiasE)(eTxanpF@wNGpQI~dO zqC%Z-#MYF{?2lS>gFD&brDJPJB;k<@gJIR1@!FdT{+D^Fi3JpQOV!V3^bPP`*aAsG zX8`Z#%>3oMtDWUd-V1WyXGUlsq+v+$#%RonnyIAKM#EpO(kZ8>xy#`QI^R@y;UvFb z{G+dK&olW1rvFl0Z)7|tgkfaFI%#CeKw1w2D~=hx!uyoJ*I+1>JbW`-N!A6kMDq>! z$^@_}>$4q;u%5f?Ink9W+L=oT9}{1}vaPvJ#cO&=DVh2%zdKz~K72O1yN>(DEJJKh zN@{k0)YS@C&`kL~ZERz8-~Mfqp}=^>^TD5Hg81(Oz?tNGHnX3&mX|sy0A`O*8jg5p zU?qJ?H~8sg65jmgs+D+&`}SRPqdDltL)nyJ;n3k>|C?>udGnEOJG0#OucUv-pXmRP zzq1kB=$(3It<Y67{hL=+Ri9)qLfF3(3Ze{-X1xOlEzei&9cUE9^t2Acxqq2Q>7vem zu%8D&uMw~D<$o(R|AqZu+}`wOi0|(ahyNeOpC%9M|N8Gdvpu<(n3yE$J3pu!wy2l- zhtr3WD*mny@PP>bJAw0m#DNp|9s6O^@p&(hG#m{fr2pZIGFfy;6#pO#!c%&ybmfEx zK|TQC-^x_*Rr)Vr7fsXd>!91?-Q?Bt)#TM?vGRJKP41g)=XT2RV`g5r{yRcf_BKL! zawsp}r)}8h)@grllR{vC_Zq*mgI@8r^?!tgfWpWl&TpW{+v{!ohtw(o#~`x_gwY9I zkF(ss={sSN>|P=BYOjTw<Qip}uy6B_<tMjC+qR08<a_t)T8rEFaq@d#n|W`@)neWP zmc8B!hgUllTpDak*Yw(LpRB0W3szMHWfwg5j>+W#iE_#Fi822-b0V`b-pXbt4DUBz z&pSeZvp2cNGYg9(Db`nFCM{vKGuO{`*<W{qu~0GQz7}UU)Q_55w^rHQ&DXBpq-T0N zyRJH~dY8C4SuT#ctWpOjQE$@yY@qy9987el`7=5o`hD{wnnMbguN~7S=##_f{tF-u zICk~I4-85HvRu84JDq;RuaDoYG?=ETx{_2JpMIrCQ53lMv>Eb^5!;#$2PB9MwixhF zru0hFSmJQI<8o-nNGf^5A8+_XVr@*mA5wm?*~=u*fq=le|NDOd*6v_O4J-9j&RNlR zN|)*lAg?x_(`z0Z#^HP7t31Qh7a<8?Sx%5YvVj$<wF74bnlh4Z=OBocesC_553Yr} zvxNf>M*tU{<QP{co$Xx>y@Qe8^e#AjN5aEi`#SmRLo$Icz%?B(>)YdYBnidiIA}Z9 zhvgy2Q+VuU5m;M|D}1<kwbR)ilRqziIoiBhOdNO?=hK7Y(b-#-c^@z}6(W<t8Ooor z^YaJwr-jQ~z~1HMt6jWT{o>=N>4%vGP)W#+bX10HA>&D+q)bx_2NoPkjc31CrtY7> z8AHQxS;KqTMiw6<0QaC+@k*<oan)OhD?8w!uFm<mEfzY)%(N~dDhKE-uv>Y8Y9!Dl z5CucIm9Z?Z%zi^rp^G&fC=)y9(-*_F)#~MWuj8QO@oOIe1wD%^lxp_O)Z~VQ_ezm! z3(VFA?t$>}0wR)I`Go)epq;|#h%agRHl$oO3Xd*gbwbQe=bqX4X{0z((s0VzmGS1) z<Cc#%OM7tgk1(k&f5>Pe{pMZN8Hr7o=vn*js+Z#wWN9PamTOdEETa?i-I@cIA^YTB zM(*Rz-7)9(j~QLJSX}{is1lOo+b+Zo<txrwM{tSdX!?&!v`l_0i0bC5|F4zvj%upe z7Pti|(v=R<i_#$?MVcUBP(i8El_EkY0tuZU<r9!<=paFmD!oWAA1$;Pqz0rZC4^3Z z5L)nstLv>-*1hkox87fK&RKiU>^(DQ<~MWpocE@*46ksOnLJ((;}m6}__xa&aM|>d z>bsS$UikOcf>lzc1lr1=xtDa>=GoC1p?IcnVVRpMDY4CK50D({x_K<s<OwV_v8S(P zTaO`mE<`=iyKz4%wzz<^3u-$%YprA5!in&ytmi>?N6y)JW*~RRzRA+9ZyPv1SVbBV zozmLz;TJ&^KdTKOqE=_B#8L}Qldn^Ck8V$Fd!qBT#NmKUV=caFZGDID#2QK@G)C(R zq|AIJY&fIpC8BbfKrcfGp-8vf&sN^wMG3f*lfl@>F_8CqdjatU>M*dLE_R64RHX9d zT(tOXnm*8s$`9n_>*u7XqC;O`$iS<yjJSd{V_e8cA5HV^eT3)^?4ChS@Tho{e8PSb z61Qj$J6$k1V0C*j1)`pMPx{nprPq`p7%U?tX+F4>t5Fw})}`YBw8wEvwKv0CaxdL! zPwjF{cA^>3<^8q;^^5lNf>1%!tw^yLeN&oi#&&)0K<gjOZ-S{(>Eav-pRJ6B2O8__ zkza3k8Js@o5l-2T%=u;9s^HmU@h0!gbVRKBujOgcs9HYO_nOU<PhjfV&|BeHbwt8# z-IUfr)M4Ac9-T%YJ$a#VmE9neA9o8(W0KJ9M4{!doU}P;xA!^OMVTQEUg2$S(?N=A zom#wjl?AX}HonBdQgt%Q<ylAG$2IcTGu{M+1P_60ld8Y2q>v(jgMr*7^J!hMpw$@e zOf9Z5ixq8)Tw%nwyt?veFvk&9x*%!4EJSw|h;Fzq3CDX$svX5I7YmZT2xf~ks@nXj zD9(APf}oSIn@V)AT>e%y`s>0C+43b!za9HT4S@AV@EaxPMNV(E7oxk=3efu3%oLxy zj5coNHvM!pW~{y@#joBoOfG=zIK3nq@59MR$GnkKt;u3_J<O%TY4L;kNPIrs%LfMB z(LN?${L3H5HI@qPT@6-*=D{Qe>lxFa%&phucaL)7Xc|6?Kr+uVHzU3`v_U8)+wi>C z$zLWO&RLT~DjZI1R2|(bs%XB(tSp9UV!5Zfs$0K|p#ul1UD49ZI`p#OJ?%U0v&iEi zRS|-^)mY${>0?nhEBIQbB6p|JhLZxqoGyh&IB6t=jd!M)!l{E8*idrM(As9;AS-z$ zZ8$dXhxb8*MH^~ZPUS74o}&(8CCDiYGzegzWcZlY6R+I4(E8Y&`Q7SRBE0_sE-&Ak zH_al*etll6H9hcG*}l1ad?Ci|<Bqx<#N+be#L$)yo68~8C;UOsQg~t+C1dB$>+Xu4 zSE0Gjv3c=dH26Y846_YKL-S}jv^_g)CZQYRwLM<0Q)!v1!n%R{Ms60Uls=mBwbZ`1 zGaZ&wi_+`avp`6u*VEp%*yf@eis2hu{rfTfi}+4rlDlFbUb7kt<;`r!^_euJz5nj$ z$COPqUjT2|3E|WHa=ncA2(9?Z=A!uI)4gR}-Sn}u1F2pVROOFnBJZa^NHniPJ^ccq zakXJx1$z$W3a)-IvdI{nYr6fGZes?UYJPzsvjYM^BsSrM;C=x-LGGET<YJvIMqQL} zNtddLBr&F?+IfwiLAQSamNp9UXBOluk<W^wm%d+UJtb(uYVeHR?c_rS?R3<|)chy4 z&eJ%Mv$-ldC8jamF1MTFqcerro$=AV&-d)Dl-|XBhLp|(9VSiG7d%(F(jl%L*OIif zfM4P%|75=>$v-8)E>|SrkjY_b%2b?l+lO}0(;DLwuY&08ysaHr##!|gF<ri1!tCd~ zdzf;nCOQ4sqX7^E;B+~n&i{7z?)$aQOLT9iD^X=Ed|Xrvl>XnvNUaPrhh8;qjQT(z z4C4#jWguX+CWsziS%B1AOF()t+FPxuXOZ7uFRh~?PYxNde7#OQtE+Q1eI^hsm)ENv zv}~=rX27N(ou$Cbkll|ijN!wB>25=4MiKNm7KM7?8?x|KbfIC(0+HMX`|Z2pJ>K%` zR?UtH!xG7K>JejZ_V(aHkS^Cn-h`QK#^RGh@F1&2k-6M?1x4BA_Ne7%WQk1}j(T0H zk6lM~$carEdWo)|^x(CK6<FP)w^>Ho9}8Zq`~?hShnobQHsz-6mo)mF-Xym#NF2f0 zj+|H`J|;=$Us(?S6jR?iVG7zC8^hrxPJ>Uk3L~&y>sc|JlgX%J6*kpXxAOM9Ncxm5 zrM9ff{B;^WgNhIRgmy0hc8R$a<A;7gW-7DGeQO`?+iECRL6`EqA}v^ifGb5D-t9zA zcxgj=l->5Sj^V<`Eryf5`rXxR`fKD1Cv(VIJKG~{`jLwQ+eO`HGTX-FXTG9<^4Wpw z(egV3EzHOUe8lPEVV98!Q;mliorSCjyg+8c@T`BmRIe${OzkT#pXI%+h#?#@_tpFx zbhYBe?w5K?A!eyT9*;-D%}()TMP{T(pxqf0rTrTnLir_AV>{8zbYH;b66{{1y@EQ? z!DByX#mszmJhg?BtM(T|g`vHl9U6H9-)ve8=1{xLntLxDtf`brg+r}&p9SwX<vy^I z^DtX%utUmY*(i)yYaDvG;1<>z3p=Z%{-wGu_H95}E*UM|FM;j*Jh}VbJw4#i8!>dq ziRis&JnVmVx=|v2>y>mgOF4in@?k%(n3zTcZ|#Q8rgMJ)LBeDn5M>tZBhlM_FehS` zC2%Qys<lP+DGWTZznUBbZwuun1{G(u|1_oE<xsqL=1h}~_bQ)0R;ZbuMK|%7x%H=6 zynd9qm@Tw2?K@-=l$Gjd&|L4@i`s+IK{bSdBYp+mqqcLFhMp688AINu)Ilv6ptFrj zpCC|asMO-vx;ZX5CmyW~+>AbNKF4vtW{2~_ttc&6h$&h?m>{s4FHsN(%RA@+k(5@F zG$ZKJ$$$n%R8&-V>|ugI$xYx@+iQZrr^ZG`lL<iBQgoRhurLBmx+JO5rV9dDGsq-~ zb5e)xqyCH_SyUL_qqWorv=u`s2w00~|IyK}GE?rTNwQVZr$EEDowFfeBK|NU*grCY z+dp!f7PP^Cpm02M6zu!jS>G0De|Wk5hofPQ{$iAsVf>WzLHe#S66d1Csc3Xk+6W+? z@}9rMsKvM9OstyY@F8Evv*r!g-N3fHT_>Fr<40p~ob`e@!c8(Phc{%8?@aN7rqxF| z$8r=9s(TZt<Z~qv3-G?0h@jyUxOM;Vxhm+FY&LpsX!!|QXn(5Ap8NI^jQ&?x?>y=g zdE{5NA^KqH3>Yg9CO+H56So7Lfo@9sd;eF0uL-l)CjQuN<ol(?@|O+(YQ%D5{oRqW zBb`)PsRMS&X$Gt%48#kS2An8b`>JJUkQ16c<~haV8$+MAjy{Mfu|>bTf(+R68d#gY zBSJ|!L@!}&MFD0*R^H_u>(lJ<X4m`m!`u41p=SFD6k#>R*9UzQ%uV&A^KQKABfY1A z0GtN64a{#S5|^vLqVO^0T_~i57~#j&uv|FIQ&V{sKG};IKUuGrUd_nae-OuK(3(A? z&$4^EzFtguxyp&AebpNgzoEI(KxrHxU%7!-!=W%&J8985JH}_7H_v)2X<&AARRPPt zk=!5aW0QnKLNlt1)qWycsm)jbFd5CJ#7cr36yF_@^jAidABXdf?~TA)XS$)9xhwcZ z;!mkEo0BpEVtxA=Jj0w5w3T-lOBSURU$JxaGkUA!ebrfS(<gV`Jb>q#>&$B{`fCCd zlV>jgc>D8ilg^}r3E$q`{29>iw@L+<QhpSOxmob2IXitkeg~<OA3liQ8KAy=hzkOw zRUXN(uz(#pksg9&L;?LWNs&i1RaFs_wM%|;qBlP&3CG?;&vuNRtWObk0=>t%qM7-R zc7c|4;EV0SU>=Xl!k*g_^O(#qx+kr!RP6z^N#Qw(jVme5Y&Cv&wwcCx+HHCn(Xg}5 z#M9%(YY%R8)Ka|M$;#+W&q~y{vcQbJOWoOjaXIp9P;85H(J@gI8T8|xFe_1nzO`i! zD<bJ(ujis<E$Bx!y8YiMT*W2oIL#)G*28{oU*&8??u&RZ-VEa^7M54Ozs0D!tLMuL zht@+=n<XV|aUZ5iMX$b>^A{c6%GP>*U?(nG1gfN4#GWk#<F=((HB(EDFoZ&n-f~O0 zwJ~>)a)`Y)W9}j|Nnn>G;6+^=8_jpk$q8v#+F=KIZ4e04mM(eUawnt9``Py6ahn>D z-PbI(_9&5f3CI1TpZ3EDj$lE8=}hMi!5Xt_v!Y25J3`D7&OPTSE~Gwe)v?(j%?)|s zXEU!Mmq3`-zLD6Yq@?78`a2P+sVSpg`_GlGTqKc><=-Ee`O4E3W21bK_K%P{$RJl0 z3V{m{rl8tz2arz!>A3${r-XQbE8pE~5wf|1KvR_^DNK(RXv)f{ua4g9ut+>gd~R{s z2c2kY^f!j!c~~kNd(VtY#I3&H0-0Izu;}uduF0M@irb)qL(|04Vr5Jt=V1f*wQ{rR zX6_o3CtHkuj$LKw%;UQ^Ma2qTY`8N`%j_3JUJu%d&^<rJqBb&q%_D!kx~p|UFsucs z4602&@MF6ab(vkBKV);y=6V0yO9!b-RmZW4;!~+8Y?ayVN5;g1dS5%dsxv>M^Ho#a z86An+^N&DA-k8dr#Y>+B7}x00*POJFh9w<XoRegQ$Vz3_L_p@+P)0O|+9|VgZ=GLI zM*lc2fK_U_ryl+EiMeh^6PI=0)Syu8_~;d+{`s(sp*GUhZ$b!p;!3l<j{1Ye^@aE- zPrB6e-nqJ?UQGSHE#v))eGncPCZTii{lY~hd&~Z>iM_I=j*gN_XBQx22zliiD#Q0S zf8(0}Dp7cHqrg_5ah%q@<Xu9tFXKhgNUqlTe=z+G&2Y^t0Yw`LTIg=xY0!6t!jyfx z;xKktQtv{RabZf=g1%YZCP%?3_TF2!xjsx;rrEEll_l;XkjF+^5*!?L>&4LlcDh<4 z%Rhl-W+<XrR1yC9*r0B^bO{mTCU~X3<`>rc>p-pnU9bnkI0c9bvZYS*WE30OX~e|z zl-bDvx#LvSag4>SXZC|?x#|Y}-8G43Uc-yQLPJ%qmf09j$i~5dRb$}r^k0ez&52<+ zmADDR24c(H5ljlGBy!~v+0B<Yf%r;a_tBG7TP1v+qpZ5JQ}GQ-j0uk-u(?P+al1X) zdL4rso1tamE-H|8yD)z|)k+pdJ5PvbD2``P#IgM5_kn<a!td7XialH`dmT0H_L>Lk z<b-XV_Ca^MjXch02h+V~m7kca1Pbkj1sI}s)k84bti|-%aK73*!6DiUX!&GxhSW;s z>_mW}-)FZv%hJY*W5v2JL-dZ)rmPJ0;<_*t5TD<;QTEUd`!^~UT72a~6}zRpopLd! z%|d!TVVF?2YMJi8#GSL`QmwRMw>&b`CstA*c_}AJTrx`0Y99AL^7wF>vyPsB=lcsG z&nxjbez_z}`3p1D;OSOX@3UuVh{y_vz?}g*?Px1g(z4*#i2PuS4d0#H<|5zo6Se=< zrIIL-q_Mqm|1&kU!sLQ{=cky$s$)f38GXN2jzLfoSKYe5d+iyA<JT{3IAwt$`m)l( zZY122>vUnBm4Fm37WG$|BB2BLt(K_#`@J*Orn!BIg19Z6dep5v$aYTxNEm?sPP+BA zygx(*U`wri>sJxjnUe4Itl@5A=62KDeYT>CnTJ5DtCuMXu2YaL0FI86Wb)vskb<}u z-w*k~>tj*nng{^#82rw(W&Q^>llff{%5F$5h(IITls|G2oA5n0CT0u#I7;9G8SDhW z58=snD}J?8?j^A-WGrY;%Uj}gEs6@)&E>3W{pfgnMj6U<S#}h->MOGvBx(0VH*F-; zbM2Gz%bA|1wx({j!xSg|8J;|bZ4=S^NvJ$WTFe0HUoLdqq_ct9+_^(#KC*7lO_d4{ zo*(8lH=e=xRi8DLM7h2YIob8Ky&Q`kDn_`h`fH6ES$931sA!!C7{#N>K;_9O<esJr z@BOu8z|onHo8Tv*uvYsGR{U*W<x%$y4cq`059aeaU%zn~cWu=NcO^2*13WfbPhaVB zjoq*fJ~L4z?%nHD51h|z40t5YqXNjLr4Uf362r6({LQ29fcRqJXNx1ZPOlGTuRlu& z<_b<5cxQV@a`44NO!w$*g#Un=U)DGfEwf(8E*LJ>N8BMreT+@Yhs&vnyqQEsDNC<c zOO;>DkdAJLUyuW3V{G8lx)E&x4S8U>OudH6wv-m1d0V_nVjA8D?RhFzlo`cZR3a=1 z;_`_(BEPZtX3e&)G_A{P@F8VaD)$~yi{evv`3tl~`||4N{zpx#6%C@d?l<_WHS$Q= ztB^)Hi3(y$V}!foX-xZzKrSQZvX!BswgFh^gk|(Khq#*T0`ax<&DF@MYWzEnwx||i zx@>N_h|Q|G%#!wk2}H)?7zW3ztVWuI?Mfg2LG;_R#yrJ^LrQhEa7oJ$<c77|YLZ}< zT#&y*c45Uwo#3ulEAEHmp)COaOEQ5ILyd0hi%6&skK^%*KkDTMb&p?qSIx6+1H-z_ zNx!+Fj`Lw!X=u#N9GMByMGf?yPQ`2<1DhEB(c9r>_4}>f?9+IV4RrULW}X)Dp-eP4 zG+aolt`@W2`GZ8_BW92}H#3q25Z2BVt#%TnlkobS<6(luK{yUs5<V|~%h43R2V*kU z&<H&T@3u)@7}&qzKZ>63c9K-U<cD`j@D5aRq~TVM#IMVzQGVe(UoViJol`v!c1~8L z^f&yU?|z&=JkJy+cy2K%{ayB-4F8D#*8L;?@4AM(=L-M-`TpN+`s2cHfBv}ezxC&z v>;4ND|J23*N%vn=`)`~7bGgUP#NZ311+x5H87ib72wwnd>D@2acpmz1f)^@H literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/.gitignore b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/.gitignore new file mode 100644 index 0000000..4a8e770 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/.gitignore @@ -0,0 +1,6 @@ +*.pyc +SmartMarkdown.sublime-project +SmartMarkdown.sublime-workspace +release.sh +test/* +.ropeproject diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default (Linux).sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default (Linux).sublime-keymap new file mode 100644 index 0000000..a773003 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default (Linux).sublime-keymap @@ -0,0 +1,26 @@ +[ + { "keys": ["ctrl+;", "ctrl+n"], "command": "headline_move", + "args": {"forward": true, "same_level": false}, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" } + ] + }, + { "keys": ["ctrl+;", "ctrl+p"], "command": "headline_move", + "args": {"forward": false, "same_level": false}, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" } + ] + }, + { "keys": ["ctrl+;", "ctrl+f"], "command": "headline_move", + "args": {"forward": true, "same_level": true}, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" } + ] + }, + { "keys": ["ctrl+;", "ctrl+b"], "command": "headline_move", + "args": {"forward": false, "same_level": true}, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" } + ] + } +] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default (OSX).sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default (OSX).sublime-keymap new file mode 100644 index 0000000..0d7fb6e --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default (OSX).sublime-keymap @@ -0,0 +1,26 @@ +[ + { "keys": ["ctrl+c", "ctrl+n"], "command": "headline_move", + "args": {"forward": true, "same_level": false}, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" } + ] + }, + { "keys": ["ctrl+c", "ctrl+p"], "command": "headline_move", + "args": {"forward": false, "same_level": false}, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" } + ] + }, + { "keys": ["ctrl+c", "ctrl+f"], "command": "headline_move", + "args": {"forward": true, "same_level": true}, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" } + ] + }, + { "keys": ["ctrl+c", "ctrl+b"], "command": "headline_move", + "args": {"forward": false, "same_level": true}, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" } + ] + } +] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default (Windows).sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default (Windows).sublime-keymap new file mode 100644 index 0000000..a773003 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default (Windows).sublime-keymap @@ -0,0 +1,26 @@ +[ + { "keys": ["ctrl+;", "ctrl+n"], "command": "headline_move", + "args": {"forward": true, "same_level": false}, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" } + ] + }, + { "keys": ["ctrl+;", "ctrl+p"], "command": "headline_move", + "args": {"forward": false, "same_level": false}, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" } + ] + }, + { "keys": ["ctrl+;", "ctrl+f"], "command": "headline_move", + "args": {"forward": true, "same_level": true}, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" } + ] + }, + { "keys": ["ctrl+;", "ctrl+b"], "command": "headline_move", + "args": {"forward": false, "same_level": true}, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" } + ] + } +] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default.sublime-commands b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default.sublime-commands new file mode 100644 index 0000000..90028e7 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default.sublime-commands @@ -0,0 +1,27 @@ +[ + { + "caption": "Pandoc: Render Markdown to temp PDF and View", + "command": "pandoc_render", + "args":{"open_after":true, "target":"pdf", "save_result":false} + }, + { + "caption": "Pandoc: Render Markdown to temp HTML and View", + "command": "pandoc_render", + "args":{"open_after":true, "target":"html", "save_result":false} + }, + { + "caption": "Pandoc: Render Markdown to HTML", + "command": "pandoc_render", + "args":{"open_after":false, "target":"html", "save_result":true} + }, + { + "caption": "Pandoc: Render Markdown to PDF", + "command": "pandoc_render", + "args":{"open_after":false, "target":"pdf", "save_result":true} + }, + { + "caption": "Pandoc: Render Markdown DocX", + "command": "pandoc_render", + "args":{"open_after":false, "target":"docx", "save_result":true} + } +] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default.sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default.sublime-keymap new file mode 100644 index 0000000..28a78b4 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Default.sublime-keymap @@ -0,0 +1,72 @@ +[ + { "keys": ["tab"], "command": "smart_folding", "context": + [ + { "key": "selector", "operator": "equal", "operand": "markup.heading.markdown" } + ] + }, + { "keys": ["shift+tab"], "command": "global_folding", "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" } + ] + }, + { "keys": ["enter"], "command": "smart_list", "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "^\\s*([-+\\**]|\\d+\\.+)\\s+" } + ] + }, + { "keys": ["enter"], "command": "smart_list", "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" }, + { "key": "selector", "operator": "equal", "operand": "markup.list" } + ] + }, + { "keys": ["tab"], "command": "smart_table", + "args": {"forward": true}, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "^\\s*(\\||\\+[-=])", + "match_all": true} + ] + }, + { "keys": ["tab"], "command": "smart_table", + "args": {"forward": true}, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "^\\s*" }, + { "key": "following_text", "operator": "regex_contains", "operand": "\\s*(\\||\\+[-=])", + "match_all": true} + ] + }, + { "keys": ["shift+tab"], "command": "smart_table", + "args": {"forward": false}, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "^\\s*(\\||\\+[-=])", + "match_all": true} + ] + }, + { "keys": ["shift+tab"], "command": "smart_table", + "args": {"forward": false}, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "^\\s*" }, + { "key": "following_text", "operator": "regex_contains", "operand": "\\s*(\\||\\+[-=])", + "match_all": true} + ] + }, + { + "keys": ["super+shift+."], "command": "change_heading_level", + "args": {"up": true}, "context": + [ + {"key": "selector", "operator": "equal", "operand": "text.html.markdown"} + ] + }, + { + "keys": ["super+shift+,"], "command": "change_heading_level", + "args": {"up": false}, "context": + [ + {"key": "selector", "operator": "equal", "operand": "text.html.markdown"} + ] + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Main.sublime-menu b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Main.sublime-menu new file mode 100644 index 0000000..91bce87 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/Main.sublime-menu @@ -0,0 +1,70 @@ +[ + { + "caption": "Preferences", + "mnemonic": "n", + "id": "preferences", + "children": + [ + { + "caption": "Package Settings", + "mnemonic": "P", + "id": "package-settings", + "children": + [ + { + "caption": "SmartMarkdown", + "children": + [ + { + "command": "open_file", "args": + { + "file": "${packages}/SmartMarkdown/SmartMarkdown.sublime-settings" + }, + "caption": "Settings – Default" + }, + { + "command": "open_file", "args": + { + "file": "${packages}/User/SmartMarkdown.sublime-settings" + }, + "caption": "Settings – User" + }, + { "caption": "-" }, + { + "command": "open_file", "args": + { + "file": "${packages}/SmartMarkdown/Default.sublime-keymap" + }, + "caption": "Key Bindings – Default" + }, + { + "command": "open_file", "args": + { + "file": "${packages}/User/Default (OSX).sublime-keymap", + "platform": "OSX" + }, + "caption": "Key Bindings – User" + }, + { + "command": "open_file", "args": + { + "file": "${packages}/User/Default (Linux).sublime-keymap", + "platform": "Linux" + }, + "caption": "Key Bindings – User" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/User/Default (Windows).sublime-keymap", + "platform": "Windows" + }, + "caption": "Key Bindings – User" + } + ] + } + ] + } + ] + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/SmartMarkdown.sublime-settings b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/SmartMarkdown.sublime-settings new file mode 100644 index 0000000..69d1fb2 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/SmartMarkdown.sublime-settings @@ -0,0 +1,11 @@ +{ + /* Please specify the PATH of pdflatex if you wanna generate PDF */ + "tex_path": ["/usr/local/texlive/2011/bin/x86_64-darwin", + "/usr/local/texlive/2012/bin/x86_64-darwin"], + /* Provide your arguments here as a list e.g.: ["--latex-engine=xelatex", "--toc"] + arguments that are separated by space must be in separate slots. e.g. ["-H", "template.tex"] */ + "pandoc_args": [], + "pandoc_args_pdf": [], + "pandoc_args_html": [], + "pandoc_args_docx": [] +} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/headline.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/headline.py new file mode 100644 index 0000000..91dce43 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/headline.py @@ -0,0 +1,263 @@ +"""Some utility functions for working with headline of Markdown. + +Terminologies +- Headline :: The headline entity OR the text of the headline +- Content :: The content under the current headline. It stops after + encountering a headline with the same or higher level OR EOF. +""" +# Author: Muchenxuan Tong <demon386@gmail.com> + +import re +import sublime + +try: + from .utilities import is_region_void +except ValueError: + from utilities import is_region_void + +MATCH_PARENT = 1 # Match headlines at the same or higher level +MATCH_CHILD = 2 # Match headlines at the same or lower level +MATCH_SILBING = 3 # Only Match headlines at the same level. +MATCH_ANY = 4 # Any headlines would be matched. +ANY_LEVEL = -1 # level used when MATCH_ANY is used as match type + + +def region_of_content_of_headline_at_point(view, from_point): + """Extract the region of the content of under current headline.""" + _, level = headline_and_level_at_point(view, from_point) + if level == None: + return None + + if is_content_empty_at_point(view, from_point): + return None + + line_num, _ = view.rowcol(from_point) + content_line_start_point = view.text_point(line_num + 1, 0) + + next_headline, _ = find_headline(view, \ + content_line_start_point, \ + level, \ + True, \ + MATCH_PARENT) + if not is_region_void(next_headline): + end_pos = next_headline.a - 1 + else: + end_pos = view.size() + return sublime.Region(content_line_start_point, end_pos) + + +def headline_and_level_at_point(view, from_point, search_above_and_down=False): + """Return the current headline and level. + + If from_point is inside a headline, then return the headline and level. + Otherwise depends on the argument it might search above and down. + """ + line_region = view.line(from_point) + line_content = view.substr(line_region) + # Update the level in case it's headline.ANY_LEVEL + level = _extract_level_from_headline(line_content) + + # Search above and down + if level is None and search_above_and_down: + # Search above + headline_region, _ = find_headline(view,\ + from_point,\ + ANY_LEVEL, + False, + skip_folded=True) + if not is_region_void(headline_region): + line_content, level = headline_and_level_at_point(view,\ + headline_region.a) + # Search down + if level is None: + headline_region, _ = find_headline(view,\ + from_point,\ + ANY_LEVEL, + True, + skip_folded=True) + if not is_region_void(headline_region): + line_content, level = headline_and_level_at_point(view, headline_region.a) + + return line_content, level + + +def _extract_level_from_headline(headline): + """Extract the level of headline, None if not found. + + """ + re_string = _get_re_string(ANY_LEVEL, MATCH_ANY) + match = re.match(re_string, headline) + + if match: + return len(match.group(1)) + else: + return None + + +def is_content_empty_at_point(view, from_point): + """Check if the content under the current headline is empty. + + For implementation, check if next line is a headline a the same + or higher level. + + """ + _, level = headline_and_level_at_point(view, from_point) + if level is None: + raise ValueError("from_point must be inside a valid headline.") + + line_num, _ = view.rowcol(from_point) + next_line_region = view.line(view.text_point(line_num + 1, 0)) + next_line_content = view.substr(next_line_region) + next_line_level = _extract_level_from_headline(next_line_content) + + # Note that EOF works too in this case. + if next_line_level and next_line_level <= level: + return True + else: + return False + + +def find_headline(view, from_point, level, forward=True, \ + match_type=MATCH_ANY, skip_headline_at_point=False, \ + skip_folded=False): + """Return the region of the next headline or EOF. + + Parameters + ---------- + view: sublime.view + + from_point: int + From which to find. + + level: int + The headline level to match. + + forward: boolean + Search forward or backward + + match_type: int + MATCH_SILBING, MATCH_PARENT, MATCH_CHILD or MATCH_ANY. + + skip_headline_at_point: boolean + When searching whether skip the headline at point + + skip_folded: boolean + Whether to skip the folded region + + Returns + ------- + match_region: int + Matched region, or None if not found. + + match_level: int + The level of matched headline, or None if not found. + + """ + if skip_headline_at_point: + # Move the point to the next line if we are + # current in a headline already. + from_point = _get_new_point_if_already_in_headline(view, from_point, + forward) + + re_string = _get_re_string(level, match_type) + if forward: + match_region = view.find(re_string, from_point) + else: + all_match_regions = view.find_all(re_string) + match_region = _nearest_region_among_matches_from_point(view, \ + all_match_regions, \ + from_point, \ + False, \ + skip_folded) + + if skip_folded: + while (_is_region_folded(match_region, view)): + from_point = match_region.b + match_region = view.find(re_string, from_point) + + if not is_region_void(match_region): + if not is_scope_headline(view, match_region.a): + return find_headline(view, match_region.a, level, forward, \ + match_type, True, skip_folded) + else: + ## Extract the level of matched headlines according to the region + headline = view.substr(match_region) + match_level = _extract_level_from_headline(headline) + else: + match_level = None + return (match_region, match_level) + +def _get_re_string(level, match_type=MATCH_ANY): + """Get regular expression string according to match type. + + Return regular expression string, rather than compiled string. Since + sublime's view.find function needs string. + + Parameters + ---------- + match_type: int + MATCH_SILBING, MATCH_PARENT, MATCH_CHILD or ANY_LEVEL. + + """ + if match_type == MATCH_ANY: + re_string = r'^(#+)\s.*' + else: + try: + if match_type == MATCH_PARENT: + re_string = r'^(#{1,%d})\s.*' % level + elif match_type == MATCH_CHILD: + re_string = r'^(#{%d,})\s.*' % level + elif match_type == MATCH_SILBING: + re_string = r'^(#{%d,%d})\s.*' % (level, level) + except ValueError: + print("match_type has to be specified if level isn't ANY_LEVE") + return re_string + + +def _get_new_point_if_already_in_headline(view, from_point, forward=True): + line_content = view.substr(view.line(from_point)) + if _extract_level_from_headline(line_content): + line_num, _ = view.rowcol(from_point) + if forward: + return view.text_point(line_num + 1, 0) + else: + return view.text_point(line_num, 0) - 1 + else: + return from_point + + +def is_scope_headline(view, from_point): + return view.score_selector(from_point, "markup.heading") > 0 or \ + view.score_selector(from_point, "meta.block-level.markdown") > 0 + + +def _nearest_region_among_matches_from_point(view, all_match_regions, \ + from_point, forward=False, + skip_folded=True): + """Find the nearest matched region among all matched regions. + + None if not found. + + """ + nearest_region = None + + for r in all_match_regions: + if not forward and r.b <= from_point and \ + (not nearest_region or r.a > nearest_region.a): + candidate = r + elif forward and r.a >= from_point and \ + (not nearest_region or r.b < nearest_region.b): + candidate = r + else: + continue + if skip_folded and not _is_region_folded(candidate, view): + nearest_region = candidate + + return nearest_region + + +def _is_region_folded(region, view): + for i in view.folded_regions(): + if i.contains(region): + return True + return False diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/headline_level.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/headline_level.py new file mode 100644 index 0000000..3ad56b9 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/headline_level.py @@ -0,0 +1,21 @@ +"""This file is contributed by [David Smith](https://github.com/djs070) +""" +import sublime +import sublime_plugin + + +class ChangeHeadingLevelCommand(sublime_plugin.TextCommand): + def run(self, edit, up=True): + for region in self.view.sel(): + line = self.view.line(region) + if up: + # Increase heading level + if not self.view.substr(line)[0] in ['#', ' ']: + self.view.insert(edit, line.begin(), " ") + self.view.insert(edit, line.begin(), "#") + else: + # Decrease heading level + if self.view.substr(line)[0] == '#': + self.view.erase(edit, sublime.Region(line.begin(), line.begin() + 1)) + if self.view.substr(line)[0] == ' ': + self.view.erase(edit, sublime.Region(line.begin(), line.begin() + 1)) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/headline_move.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/headline_move.py new file mode 100644 index 0000000..824d6a9 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/headline_move.py @@ -0,0 +1,61 @@ +"""This module provides commands for easily moving between headilnes. + +The feature is borrowed from [Org-mode](http://org-mode.org). + +""" +# Author: Muchenxuan Tong <demon386@gmail.com> + +import sublime +import sublime_plugin + +try: + from . import headline + from .utilities import is_region_void +except ValueError: + import headline + from utilities import is_region_void + + +class HeadlineMoveCommand(sublime_plugin.TextCommand): + def run(self, edit, forward=True, same_level=True): + """Move between headlines, forward or backward. + + If same_level is true, only move to headline with the same level + or higher level. + + """ + new_sel = [] + if same_level: + level_type = headline.MATCH_PARENT + else: + level_type = headline.MATCH_ANY + + for region in self.view.sel(): + if same_level: + _, level = headline.headline_and_level_at_point(self.view,\ + region.a, + search_above_and_down=True) + if level is None: + return + else: + level = headline.ANY_LEVEL + + match_region, _ = headline.find_headline(self.view, \ + region.a, \ + level, \ + forward, \ + level_type, \ + skip_headline_at_point=True,\ + skip_folded=True) + + if is_region_void(match_region): + return + new_sel.append(sublime.Region(match_region.a, match_region.a)) + + self.adjust_view(new_sel) + + def adjust_view(self, new_sel): + self.view.sel().clear() + for region in new_sel: + self.view.sel().add(region) + self.view.show(region) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/license.txt b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/license.txt new file mode 100644 index 0000000..d71c44d --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/license.txt @@ -0,0 +1,7 @@ +Copyright (C) <2012> Muchenxuan Tong <demon386@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/package-metadata.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/package-metadata.json new file mode 100644 index 0000000..b5e2cd5 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/package-metadata.json @@ -0,0 +1 @@ +{"url": "https://github.com/demon386/SmartMarkdown", "version": "2013.03.23.12.24.10", "description": "A plugin for facilitating editing markdown in Sublime Text 2. Features are borrowed from Org mode of Emacs."} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/pandoc_render.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/pandoc_render.py new file mode 100644 index 0000000..67f7320 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/pandoc_render.py @@ -0,0 +1,124 @@ +"""This file is initially forked from +[SublimePandoc](https://github.com/jclement/SublimePandoc) +by [DanielMe](https://github.com/DanielMe/) + +@todo naming convention should be foo_bar rather than fooBar. +@bug PDF export doesn't work in my Mac, gonna check it later. + +2012-07-02: Muchenxuan Tong changed some stylical errors (with SublimeLinter) +""" + +import sublime +import sublime_plugin +import webbrowser +import tempfile +import os +import os.path +import sys +import subprocess +from subprocess import PIPE + + +class PandocRenderCommand(sublime_plugin.TextCommand): + def is_enabled(self): + return self.view.score_selector(0, "text.html.markdown") > 0 + + def is_visible(self): + return True + + def run(self, edit, target="pdf", open_after=True, save_result=False): + if target not in ["html", "docx", "pdf"]: + raise Exception("Format %s currently unsopported" % target) + + self.setting = sublime.load_settings("SmartMarkdown.sublime-settings") + + encoding = self.view.encoding() + if encoding == 'Undefined': + encoding = 'UTF-8' + elif encoding == 'Western (Windows 1252)': + encoding = 'windows-1252' + contents = self.view.substr(sublime.Region(0, self.view.size())) + contents = contents.encode(encoding) + + file_name = self.view.file_name() + if file_name: + os.chdir(os.path.dirname(file_name)) + + # write buffer to temporary file + # This is useful because it means we don't need to save the buffer + tmp_md = tempfile.NamedTemporaryFile(delete=False, suffix=".md") + tmp_md.write(contents) + tmp_md.close() + + # output file... + suffix = "." + target + if save_result: + output_name = os.path.splitext(self.view.file_name())[0] + suffix + if not self.view.file_name(): + raise Exception("Please safe the buffer before trying to export with pandoc.") + else: + output = tempfile.NamedTemporaryFile(delete=False, suffix=suffix) + output.close() + output_name = output.name + + args = self.pandoc_args(target) + self.run_pandoc(tmp_md.name, output_name, args) + + if open_after: + self.open_result(output_name, target) + #os.unlink(tmp_md.name) + + def run_pandoc(self, infile, outfile, args): + cmd = ['pandoc'] + args + cmd += [infile, "-o", outfile] + + # Merge the path in settings + setting_path = self.setting.get("tex_path", []) + for p in setting_path: + if p not in os.environ["PATH"]: + os.environ["PATH"] += ":" + p + + try: + # Use the current directory as working dir whenever possible + file_name = self.view.file_name() + if file_name: + working_dir = os.path.dirname(file_name) + p = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE, + cwd=working_dir) + + else: + p = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE) + p.wait() + out, err = p.communicate() + if err: + raise Exception("Command: %s\n" % " ".join(cmd) + "\nErrors: " + err) + except Exception as e: + sublime.error_message("Fail to generate output.\n{0}".format(e)) + + def pandoc_args(self, target): + """ + Create a list of arguments for the pandoc command + depending on the target. + TODO: Actually do something sensible here + """ + # Merge the args in settings + args = self.setting.get("pandoc_args", []) + + if target == "pdf": + args += self.setting.get("pandoc_args_pdf", []) + if target == "html": + args += self.setting.get("pandoc_args_html", []) + ['-t', 'html5'] + if target == "docx": + args += self.setting.get("pandoc_args_docx", []) + ['-t', 'docx'] + return args + + def open_result(self, outfile, target): + if target == "html": + webbrowser.open_new_tab(outfile) + elif sys.platform == "win32": + os.startfile(outfile) + elif "mac" in sys.platform or "darwin" in sys.platform: + os.system("open %s" % outfile) + print(outfile) + elif "posix" in sys.platform or "linux" in sys.platform: + os.system("xdg-open %s" % outfile) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/readme.md b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/readme.md new file mode 100644 index 0000000..6b0d6a0 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/readme.md @@ -0,0 +1,59 @@ +# SmartMarkdown for Sublime Text 2 & 3 + +Author: Muchenxuan Tong (demon386@gmail.com) + +## Introduction +The plugin is aimed at making editing Markdown in Sublime Text 2 easier and more powerful. Ideally, I hope we can bring several amazing features of [Org-mode](http://org-mode.org) of Emacs into Sublime Text. + +## Done +- **Smart Headline folding / unfolding**. Right now you can fold / unfold headlines by pressing **TAB** on it. I assume you use the following formats: # Section; ## Subsection; ### Subsubsection ... +- **Global Headline Folding / unfolding**. **Shift+Tab** to Fold / Unfold all at any position. +- **Smart Order / Unordered list**. When editing lists, you can just press **ENTER** and this plugin will automatically continue the list. Once the content of the list becomes empty it will stop. +- **Move between headlines**. + - Use **Ctrl+c Ctrl+n** to move to the next headline (any level); **Ctrl+c Ctrl+p** to the previous one, for Mac. (**Ctrl+; Ctrl+n** and **Ctrl+; Ctrl+p** for Windows and Linux) + - Use **Ctrl+c Ctrl+f** to move to the next headline (same level or higher level); **Ctrl+c Ctrl+b** to the previous one, for Mac. (**Ctrl+; Ctrlf** and **Ctrl+; Ctrl+b** for Windows and Linux) +- **Adjust headline level** Added by [David Smith](https://github.com/djs070). + - **Super+Shift+,** for decreasing and **Super+Shift+.** for increasing headline levels. +- **Smart table** + - Currently, the smart table suppose only the Grid table format of [Pandoc](http://johnmacfarlane.net/pandoc/README.html). Use monospaced fonts, otherwise it would appear bizarre. + - The behavior is like the table in Org-mode. If you are unfamiliar with Org-mode, just use | (vertical line) to separate the column (e.g. | header1 | header 2 |), and use the **TAB** to reformat the table at point. Everything would fall into the place. Add +- and then press TAB for adding separator between rows. Add += and then press TAB for adding separator between header and the table body. Read the Grid tables section of [Pandoc Userg's Guide](http://johnmacfarlane.net/pandoc/README.html#tables) for more information. + - Use **TAB** to move forward a cell in table, **Shift+TAB** to move backward. + - Personally I plan to use grid table as a basis and add command for converting to other table formats if necessary. +- **Basic Pandoc integration with Pandoc** By integrating [SublimePandoc](https://github.com/jclement/SublimePandoc). Added by [DanielMe](https://github.com/DanielMe/). + - **Note**: If you need to generate PDF output, please make sure you have pdflatex available ([MacTeX](http://www.tug.org/mactex/2012/) for Mac, or TeX Live for other OS). Please also specify "tex_path" in the package settings (Preference - Package Settings - SmartMarkdown - Settings - User (see Settings - Default as an example.)) + +## Todo +- **Embeded R & Python Code for reproducible research** +- **Better Pandoc integration** Actual support for different Pandoc command line options etc. +- ... + +## What's new +### v0.2: Support for Sublime Text 3 (added by [UNOwen](https://github.com/UNOwen).) +### v0.1.6: Add support and bindings for headline level changing. (added by [David Smith](https://github.com/djs070).) The key bindings are: **Super+Shift+,** for decreasing and **Super+Shift+.** for increasing. +### v0.1.5: Basic smart table (grid table) support added. Basic Pandoc intergration (added by [DanielMe](https://github.com/DanielMe/).) +### v0.1.3: Add support for global headling folding / unfolding. +### v0.1.2: Move between headlines supported! +- Use **Ctrl+c Ctrl+n** to move to the next headline (any level); **Ctrl+c Ctrl+p** to the previous one. +- Use **Ctrl+c Ctrl+f** to move to the next headline (same level or higher level); **Ctrl+c Ctrl+b** to the previous one. +- Fixed a bug on bullet list. Thanks to quodlibet (fixed in v0.1.1). + +### v0.1.0: Created! +- Smart Headline folding / unfolding is supported. +- Smart Lists is supported. + +## For Developers +- Whenever possible, please obey the [PEP 8](http://www.python.org/dev/peps/pep-0008/) style guide. This can be checked easily with the plugin SublimeLinter. +- git-flow is recommended (but not enforced) as a development work flow. For instruction please read [Why aren't you using git-flow?](http://jeffkreeftmeijer.com/2010/why-arent-you-using-git-flow/). To adapt it, a command line tool [gitflow](https://github.com/nvie/gitflow/) is highly recommended. +- Please work on the develop branch, it's newer than master. the master branch is for users. + +# License +The plugin is licensed under the MIT license. + + +Copyright (C) <2012> Muchenxuan Tong <demon386@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/smart_folding.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/smart_folding.py new file mode 100644 index 0000000..350e11d --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/smart_folding.py @@ -0,0 +1,195 @@ +"""Smart folding is a feature borrowed from [Org-mode](http://org-mode.org). + +It enables folding / unfolding the headlines by simply pressing TAB on headlines. + +Global headline folding / unfolding is recommended to be trigged by Shift + TAB, +at anywhere. + +""" +# Author: Muchenxuan Tong <demon386@gmail.com> + +import re + +import sublime +import sublime_plugin + +try: + from . import headline + from .utilities import is_region_void +except ValueError: + import headline + from utilities import is_region_void + + +HEADLINE_PATTERN = re.compile(r'^(#+)\s.*') + + +class SmartFoldingCommand(sublime_plugin.TextCommand): + """Smart folding is used to fold / unfold headline at the point. + + It's designed to bind to TAB key, and if the current line is not + a headline, a \t would be inserted. + + """ + def run(self, edit): + ever_matched = False + for region in self.view.sel(): + matched = self.fold_or_unfold_headline_at_point(region.a) + if matched: + ever_matched = True + if not ever_matched: + for r in self.view.sel(): + self.view.insert(edit, r.a, '\t') + self.view.show(r) + + def fold_or_unfold_headline_at_point(self, from_point): + """Smart folding of the current headline. + + Unfold only when it's totally folded. Otherwise fold it. + + """ + _, level = headline.headline_and_level_at_point(self.view, + from_point) + # Not a headline, cancel + if level is None or not headline.is_scope_headline(self.view, from_point): + return False + + content_region = headline.region_of_content_of_headline_at_point(self.view, + from_point) + # If the content is empty, Nothing needs to be done. + if content_region is None: + # Return True because there is a headline anyway. + return True + + # Check if content region is folded to decide the action. + if self.is_region_totally_folded(content_region): + self.unfold_yet_fold_subheads(content_region, level) + else: + self.view.fold(content_region) + return True + + def is_region_totally_folded(self, region): + """Decide if the region is folded. Treat empty region as folded.""" + if (region is None) or (region.a == region.b): + return True + + for i in self.view.folded_regions(): + if i.contains(region): + return True + return False + + def unfold_yet_fold_subheads(self, region, level): + """Unfold the region while keeping the subheadlines folded.""" + ## First unfold all + self.view.unfold(region) + ## Fold subheads + child_headline_region, _ = headline.find_headline(self.view, region.a, level, True, \ + headline.MATCH_CHILD) + + while (not is_region_void(child_headline_region) and child_headline_region.b <= region.b): + child_content_region = headline.region_of_content_of_headline_at_point(self.view, + child_headline_region.a) + if child_content_region is not None: + self.view.fold(child_content_region) + search_start_point = child_content_region.b + else: + search_start_point = child_headline_region.b + + child_headline_region, _ = headline.find_headline(self.view, \ + search_start_point, level, True, \ + headline.MATCH_CHILD, + skip_headline_at_point=True) + + +class GlobalFoldingCommand(SmartFoldingCommand): + """Global folding / unfolding headlines at any point. + + Unfold only when top-level headlines are totally folded. + Otherwise fold. + + """ + def run(self, edit): + if self.is_global_folded(): + # Unfold all + self.unfold_all() + else: + self.fold_all() + + def is_global_folded(self): + """Check if all headlines are folded. + """ + region, level = headline.find_headline(self.view, 0, \ + headline.ANY_LEVEL, True) + # Treating no heeadline as folded, since unfolded all makes + # no harm in this situation. + if is_region_void(region): + return True + + point = region.a + # point can be zero + while (point is not None and region): + region = headline.region_of_content_of_headline_at_point(self.view, \ + point) + if not is_region_void(region): + point = region.b + if not self.is_region_totally_folded(region): + return False + else: + region, level = headline.find_headline(self.view, point, \ + headline.ANY_LEVEL, \ + True, + skip_headline_at_point=True) + if not is_region_void(region): + point = region.a + return True + + def unfold_all(self): + self.view.unfold(sublime.Region(0, self.view.size())) + self.view.show(self.view.sel()[0]) + + def fold_all(self): + region, level = headline.find_headline(self.view, \ + 0, \ + headline.ANY_LEVEL, \ + True) + + # At this point, headline region is sure to exist, otherwise it would be + # treated as gobal folded. (self.is_global_folded() would return True) + point = region.a + # point can be zero + while (point is not None and region): + region = headline.region_of_content_of_headline_at_point(self.view, \ + point) + if not is_region_void(region): + point = region.b + self.view.fold(region) + region, level = headline.find_headline(self.view, point, \ + headline.ANY_LEVEL, + True, \ + skip_headline_at_point=True) + if not is_region_void(region): + point = region.a + self.adjust_cursors_and_view() + + def adjust_cursors_and_view(self): + """After folder, adjust cursors and view. + + If the current point is inside the folded region, move it move + otherwise it's easy to perform some unintentional editing. + + """ + folded_regions = self.view.folded_regions() + new_sel = [] + + for r in self.view.sel(): + for folded in folded_regions: + if folded.contains(r): + new_sel.append(sublime.Region(folded.b, folded.b)) + break + else: + new_sel.append(r) + + self.view.sel().clear() + for r in new_sel: + self.view.sel().add(r) + self.view.show(r) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/smart_list.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/smart_list.py new file mode 100644 index 0000000..c1e229f --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/smart_list.py @@ -0,0 +1,57 @@ +"""Smart list is used to automatially continue the current list.""" +# Author: Muchenxuan Tong <demon386@gmail.com> + +import re + +import sublime +import sublime_plugin + + +ORDER_LIST_PATTERN = re.compile(r"(\s*)(\d+)(\.\s+)\S+") +UNORDER_LIST_PATTERN = re.compile(r"(\s*[-+\**]+)(\s+)\S+") +EMPTY_LIST_PATTERN = re.compile(r"(\s*([-+\**]|\d+\.+))\s+$") + + +class SmartListCommand(sublime_plugin.TextCommand): + def run(self, edit): + for region in self.view.sel(): + line_region = self.view.line(region) + # the content before point at the current line. + before_point_region = sublime.Region(line_region.a, + region.a) + before_point_content = self.view.substr(before_point_region) + + # Disable smart list when folded. + folded = False + for i in self.view.folded_regions(): + if i.contains(before_point_region): + self.view.insert(edit, region.a, '\n') + folded = True + if folded: + break + + match = EMPTY_LIST_PATTERN.match(before_point_content) + if match: + self.view.erase(edit, before_point_region) + break + + match = ORDER_LIST_PATTERN.match(before_point_content) + if match: + insert_text = match.group(1) + \ + str(int(match.group(2)) + 1) + \ + match.group(3) + self.view.insert(edit, region.a, "\n" + insert_text) + break + + match = UNORDER_LIST_PATTERN.match(before_point_content) + if match: + insert_text = match.group(1) + match.group(2) + self.view.insert(edit, region.a, "\n" + insert_text) + break + + self.view.insert(edit, region.a, '\n') + self.adjust_view() + + def adjust_view(self): + for region in self.view.sel(): + self.view.show(region) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/smart_table.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/smart_table.py new file mode 100644 index 0000000..51c6677 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/smart_table.py @@ -0,0 +1,87 @@ +"""Smart is inspired by the Table behavior of Org-mode. + +Markdown itself doesn't support grid table, yet pandoc does. + +@todo: add a key binding for converting grid table to the simple one +""" +# Author: Muchenxuan Tong <demon386@gmail.com> +# LICENSE: MIT + +import sublime +import sublime_plugin + +try: + from . import table +except ValueError: + import table + + +class SmartTable(sublime_plugin.TextCommand): + def run(self, edit, forward=True): + new_sel = [] + for r in self.view.sel(): + point = r.a + + for i in self.view.folded_regions(): + if i.contains(sublime.Region(point, point)): + return + t = table.convert_table_at_point_as_list(self.view, point) + t = table.reformat_table_list(t) + t_str = table.convert_table_list_to_str(t) + + # Both are 0-based + cur_row_num, cur_col_num = table.get_point_row_and_col(self.view, point) + table_row_num = len(t) + line_num, _ = self.view.rowcol(point) + start_line_num = line_num - cur_row_num + start_point = self.view.text_point(line_num - cur_row_num, 0) + end_line_num = line_num + table_row_num - cur_row_num - 1 + end_line_start_point = self.view.text_point(end_line_num, 0) + end_point = self.view.line(end_line_start_point).b + + # Erase the previous table region, use the new one for substitution. + self.view.erase(edit, sublime.Region(start_point, end_point)) + self.view.insert(edit, start_point, t_str) + + if forward: + if cur_col_num is None or cur_col_num >= len(t[0]) - 1: + line_num += 1 + while(table.is_line_separator(self.view, line_num)): + line_num += 1 + cur_col_num = 0 + else: + cur_col_num += 1 + else: + if cur_col_num is None or cur_col_num <= 0: + line_num -= 1 + while(table.is_line_separator(self.view, line_num)): + line_num -= 1 + cur_col_num = len(t[0]) - 1 + else: + cur_col_num -= 1 + + # Add a new line when at the end of the table. + if line_num < start_line_num or line_num > end_line_num: + col_pos = 0 + if line_num > end_line_num: + self.view.insert(edit, self.view.text_point(line_num, 0), "\n") + else: + col_pos = self.calculate_col_point(t, cur_col_num) + + new_sel.append(self.view.text_point(line_num, col_pos)) + + self.view.sel().clear() + for r in new_sel: + self.view.sel().add(r) + self.view.show(r) + + def calculate_col_point(self, formatted_table, col_num): + i = 0 + while table.SEPARATOR_PATTERN.match(formatted_table[i][0]): + i += 1 + + cols_length = [len(j) for j in formatted_table[i]] + point = 2 + for i in range(col_num): + point += cols_length[i] + 3 + return point diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/table.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/table.py new file mode 100644 index 0000000..41348c4 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/table.py @@ -0,0 +1,207 @@ +"""Utilities function for working with grid table of Pandoc + +Terminologies + +- Table list :: This is not a list of tables, but rather converting the table as +a nested python list. Each row is a sub-list in the table list. + +""" +# Author: Muchenxuan Tong <demon386@gmail.com> +# LICENSE: MIT + +import re +import copy + +import sublime + +try: + from . import utilities +except ValueError: + import utilities + +TABLE_PATTERN = re.compile(r"\s*\|") +SEPARATOR_PATTERN = re.compile(r"\s*(\+[=-])") + + +def convert_table_at_point_as_list(view, from_point): + """Get the table at the point. + Transform the table to python list. + + Returns + ------- + table: list + A nested list representing the table. + indent: "str" (@todo not impelmented yet) + String of indentation, used in every row. + + """ + table_above = convert_table_above_or_below_as_list(view, from_point, above=True) + table_below = convert_table_above_or_below_as_list(view, from_point, above=False) + row_at_point = convert_row_at_point_as_list(view, from_point) + + table = table_above + [row_at_point] + table_below + return table + + +def convert_table_above_or_below_as_list(view, from_point, above): + """Convert the table above the point as python list. + + Returns + ------- + table: list + A nested list representing the table. + + """ + line_num, _ = view.rowcol(from_point) + line_num += - 1 if above else 1 + + line_text = utilities.text_at_line(view, line_num) + table = [] + + while line_text and (TABLE_PATTERN.match(line_text) or + SEPARATOR_PATTERN.match(line_text)): + table.append(_convert_row_text_as_list(line_text)) + line_num += -1 if above else 1 + line_text = utilities.text_at_line(view, line_num) + + if above: + table = table[::-1] + + return table + + +def convert_row_at_point_as_list(view, from_point): + """Convert the row at point as a python list. + """ + line_num, _ = view.rowcol(from_point) + line_text = utilities.text_at_line(view, line_num) + + return _convert_row_text_as_list(line_text) + + +def _convert_row_text_as_list(row_text): + """Convert the text of a row into a python list. + + Paramters + --------- + row_text: str + The text of the row. + + Returns + ------- + lst: list + The converted list. + + """ + split_row = row_text.split("|") + + if len(split_row) > 2 and split_row[-1].strip() == "": + lst = split_row[1:-1] + else: + lst = split_row[1:] + + match = SEPARATOR_PATTERN.match(row_text) + if match: + lst = [match.group(1)] + + return [i.strip() for i in lst] + + +def reformat_table_list(table): + """Reformat & align the table list. + + After this, every column is of the same length, + and every row is of the same number of column. + + """ + cols_num = max([len(row) for row in table]) + cols_length = _get_cols_length(table, cols_num) + + new_table = [] + for row in table: + new_row = [] + if not SEPARATOR_PATTERN.match(row[0]): + for i in range(cols_num): + try: + col = row[i] + new_row.append(col + " " * (cols_length[i] - len(col))) + except: + new_row.append(" " * cols_length[i]) + else: + marker = row[0][1] + for i in range(cols_num): + new_row.append(marker * (cols_length[i] + 2)) + # Add a mark for recognization + new_row[0] = "+" + new_row[0] + new_table.append(new_row) + return new_table + + +def convert_table_list_to_str(table): + """Convert the python list to str for outputing. + + """ + table_str = "" + table = copy.deepcopy(table) + for row in table: + if SEPARATOR_PATTERN.match(row[0]): + row[0] = row[0][1:] # Remove the mark added in reformat_table_list + row_str = "+" + for col_str in row: + row_str += col_str + "+" + else: + row_str = "|" + for col_str in row: + row_str += " " + col_str + " " + "|" + table_str += row_str + "\n" + return table_str[:-1] + + +def _get_cols_length(table, cols_num): + """Return the max length of every columns. + """ + cols_length = [0] * cols_num + for row in table: + for (i, col) in enumerate(row): + col_len = len(col) + if col_len > cols_length[i]: + cols_length[i] = col_len + return cols_length + + +def get_point_row_and_col(view, from_point): + """Return the row and col the current point is in the table. + """ + line_num, _ = view.rowcol(from_point) + line_num -= 1 + + line_text = utilities.text_at_line(view, line_num) + row_num = 0 + while line_text and (TABLE_PATTERN.match(line_text) or + SEPARATOR_PATTERN.match(line_text)): + row_num += 1 + line_num -= 1 + line_text = utilities.text_at_line(view, line_num) + + line_start_point = view.line(from_point) + region = sublime.Region(line_start_point.a, from_point) + precedding_text = view.substr(region) + + split_row = precedding_text.split("|") + if len(split_row) >= 2: + col_num = len(split_row) - 2 + elif split_row[0].strip() == "": + col_num = -1 + else: + col_num = None + return (row_num, col_num) + + +def is_line_separator(view, line_num): + """Check if the current line is a separator. + """ + text = utilities.text_at_line(view, line_num) + if text and SEPARATOR_PATTERN.match(text): + return True + else: + return False diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/utilities.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/utilities.py new file mode 100644 index 0000000..2510143 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SmartMarkdown/utilities.py @@ -0,0 +1,23 @@ +"""Some utility functions for working with sublime. +""" + + +def text_at_line(view, line_num): + """Return the content at line. None if out of boundary.""" + if line_num < 0: + return None + + max_line_num, _ = view.rowcol(view.size()) + if line_num > max_line_num: + return None + + point = view.text_point(line_num, 0) + line_region = view.line(point) + return view.substr(line_region) + +def is_region_void(region): + if region == None: + return True + if region.a == -1 and region.b == -1: + return True + return False \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Solarized Color Scheme/README.mkd b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Solarized Color Scheme/README.mkd new file mode 100644 index 0000000..a0d0f05 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Solarized Color Scheme/README.mkd @@ -0,0 +1,80 @@ +Solarized Colorscheme for Sublime Text +============================= + +See the [Solarized homepage](http://ethanschoonover.com/solarized) for screenshots, +details and colorscheme versions for Vim, Mutt, popular terminal emulators and +other applications. + + +Downloads +--------- + +If you have come across this page via github please visit the main [Solarized homepage](http://ethanschoonover.com/solarized). The original [Solarized repository] has many more themes and a number of [test files]. + +[Solarized homepage]: http://ethanschoonover.com/solarized +[Solarized repository]: https://github.com/altercation/solarized +[test files]: https://github.com/altercation/solarized/tree/master/utils/tests + + +Sublime Text Support +--------- + +This theme is fully compatible with [Sublime Text][Sublime]! + +To install it, ensure that you have installed Sublime Package Control following [these instructions][SublimePackage] + +Open the Sublime command palette with `Ctrl + Shift + P`, type / select `Package Control: Install Package`, +then from the package control list, type / select `Solarized Color Scheme (TextMate)` + +Note that packages are auto-updating, so as new modifications are made they will automatically be installed. + +[Sublime]: http://www.sublimetext.com/dev +[SublimePackage]: http://wbond.net/sublime_packages/package_control/installation + + +Contribute +--------- + +The goal is to get as-close-as-possible to the rendering of the same file in Vim. If you would like to help, here's how to get involved. + + +1. Fork this repo and checkout your own copy. +2. Download the [sample documents](https://github.com/altercation/solarized/tree/master/utils/tests). +3. Checkout this guide to [TextMate theme development](http://manual.macromates.com/en/themes). +4. Check the [issues](https://github.com/deplorableword/textmate-solarized/issues) to see what needs working on. + +Contributors +--------- +* [deplorableword](https://github.com/deplorableword) +* [fentie](https://github.com/fentie) +* [bmthykm](https://github.com/bmthykm) +* [Zegnat](https://github.com/zegnat) +* [markstory](https://github.com/markstory) +* [rays](https://github.com/rays) +* [joshcarr](https://github.com/joshcarr) +* [thatRD](https://github.com/thatRD) +* [oesmith](https://github.com/oesmith) +* [evanmoran](https://github.com/evanmoran) +* [iristyle](https://github.com/iristyle) +* [braver](https://github.com/braver) + +License +------- + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Solarized Color Scheme/Solarized (dark).tmTheme b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Solarized Color Scheme/Solarized (dark).tmTheme new file mode 100644 index 0000000..b15208e --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Solarized Color Scheme/Solarized (dark).tmTheme @@ -0,0 +1,2142 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>name</key> + <string>Solarized (dark)</string> + <key>settings</key> + <array> + <dict> + <key>settings</key> + <dict> + <key>background</key> + <string>#002B36</string> + <key>caret</key> + <string>#839496</string> + <key>foreground</key> + <string>#839496</string> + <key>invisibles</key> + <string>#073642</string> + <key>lineHighlight</key> + <string>#073642</string> + <key>selection</key> + <string>#EEE8D5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Comment</string> + <key>scope</key> + <string>comment</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>String</string> + <key>scope</key> + <string>string</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>StringNumber</string> + <key>scope</key> + <string>string</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Regexp</string> + <key>scope</key> + <string>string.regexp</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Number</string> + <key>scope</key> + <string>constant.numeric</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#D33682</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Variable</string> + <key>scope</key> + <string>variable.language, variable.other</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Keyword</string> + <key>scope</key> + <string>keyword</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Storage</string> + <key>scope</key> + <string>storage</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Class name</string> + <key>scope</key> + <string>entity.name.class, entity.name.type.class</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Function name</string> + <key>scope</key> + <string>entity.name.function</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Variable start</string> + <key>scope</key> + <string>punctuation.definition.variable</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Embedded code markers</string> + <key>scope</key> + <string>punctuation.section.embedded.begin, punctuation.section.embedded.end</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Built-in constant</string> + <key>scope</key> + <string>constant.language, meta.preprocessor</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Support.construct</string> + <key>scope</key> + <string>support.function.construct, keyword.other.new</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>User-defined constant</string> + <key>scope</key> + <string>constant.character, constant.other</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Inherited class</string> + <key>scope</key> + <string>entity.other.inherited-class</string> + <key>settings</key> + <dict/> + </dict> + <dict> + <key>name</key> + <string>Function argument</string> + <key>scope</key> + <string>variable.parameter</string> + <key>settings</key> + <dict/> + </dict> + <dict> + <key>name</key> + <string>Tag name</string> + <key>scope</key> + <string>entity.name.tag</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>bold</string> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tag start/end</string> + <key>scope</key> + <string>punctuation.definition.tag.html, punctuation.definition.tag.begin, punctuation.definition.tag.end</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tag attribute</string> + <key>scope</key> + <string>entity.other.attribute-name</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#93A1A1</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Library function</string> + <key>scope</key> + <string>support.function</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Continuation</string> + <key>scope</key> + <string>punctuation.separator.continuation</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Library constant</string> + <key>scope</key> + <string>support.constant</string> + <key>settings</key> + <dict/> + </dict> + <dict> + <key>name</key> + <string>Library class/type</string> + <key>scope</key> + <string>support.type, support.class</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Library Exception</string> + <key>scope</key> + <string>support.type.exception</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Special</string> + <key>scope</key> + <string>keyword.other.special-method</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Library variable</string> + <key>scope</key> + <string>support.other.variable</string> + <key>settings</key> + <dict/> + </dict> + <dict> + <key>name</key> + <string>Invalid</string> + <key>scope</key> + <string>invalid</string> + <key>settings</key> + <dict/> + </dict> + <dict> + <key>name</key> + <string>Quoted String</string> + <key>scope</key> + <string>string.quoted.double, string.quoted.single</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Quotes</string> + <key>scope</key> + <string>punctuation.definition.string.begin, punctuation.definition.string.end</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: Property</string> + <key>scope</key> + <string>entity.name.tag.css, support.type.property-name.css, meta.property-name.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: @font-face</string> + <key>scope</key> + <string>source.css</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: Selector</string> + <key>scope</key> + <string>meta.selector.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: {}</string> + <key>scope</key> + <string>punctuation.section.property-list.css</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: Numeric Value</string> + <key>scope</key> + <string>meta.property-value.css constant.numeric.css, keyword.other.unit.css,constant.other.color.rgb-value.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: Value</string> + <key>scope</key> + <string>meta.property-value.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: !Important</string> + <key>scope</key> + <string>keyword.other.important.css</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: Standard Value</string> + <key>scope</key> + <string>support.constant.color</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#6C71C4</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: Tag</string> + <key>scope</key> + <string>entity.name.tag.css</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: : ,</string> + <key>scope</key> + <string>punctuation.separator.key-value.css, punctuation.terminator.rule.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS .class</string> + <key>scope</key> + <string>entity.other.attribute-name.class.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS :pseudo</string> + <key>scope</key> + <string>entity.other.attribute-name.pseudo-element.css, entity.other.attribute-name.pseudo-class.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: #id</string> + <key>scope</key> + <string>entity.other.attribute-name.id.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>bold</string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>JS: Function Name</string> + <key>scope</key> + <string>meta.function.js, entity.name.function.js, support.function.dom.js</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>JS: Source</string> + <key>scope</key> + <string>text.html.basic source.js.embedded.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>JS: Function</string> + <key>scope</key> + <string>storage.type.function.js</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>JS: Numeric Constant</string> + <key>scope</key> + <string>constant.numeric.js</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>JS: []</string> + <key>scope</key> + <string>meta.brace.square.js</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>JS: Storage Type</string> + <key>scope</key> + <string>storage.type.js</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>()</string> + <key>scope</key> + <string>meta.brace.round, punctuation.definition.parameters.begin.js, punctuation.definition.parameters.end.js</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#93A1A1</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>{}</string> + <key>scope</key> + <string>meta.brace.curly.js</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: Doctype</string> + <key>scope</key> + <string>entity.name.tag.doctype.html, meta.tag.sgml.html, string.quoted.double.doctype.identifiers-and-DTDs.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: Comment Block</string> + <key>scope</key> + <string>comment.block.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: Script</string> + <key>scope</key> + <string>entity.name.tag.script.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: Style</string> + <key>scope</key> + <string>source.css.embedded.html string.quoted.double.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: Text</string> + <key>scope</key> + <string>text.html.ruby</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>bold</string> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: =</string> + <key>scope</key> + <string>text.html.basic meta.tag.other.html, text.html.basic meta.tag.any.html, text.html.basic meta.tag.block.any, text.html.basic meta.tag.inline.any, text.html.basic meta.tag.structure.any.html, text.html.basic source.js.embedded.html, punctuation.separator.key-value.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#657B83</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: something=</string> + <key>scope</key> + <string>text.html.basic entity.other.attribute-name.html</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#657B83</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: "</string> + <key>scope</key> + <string>text.html.basic meta.tag.structure.any.html punctuation.definition.string.begin.html, punctuation.definition.string.begin.html, punctuation.definition.string.end.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: <tag></string> + <key>scope</key> + <string>entity.name.tag.block.any.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>bold</string> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: style</string> + <key>scope</key> + <string>source.css.embedded.html entity.name.tag.style.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: <style></string> + <key>scope</key> + <string>entity.name.tag.style.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: {}</string> + <key>scope</key> + <string>text.html.basic punctuation.section.property-list.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: Embeddable</string> + <key>scope</key> + <string>source.css.embedded.html, comment.block.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Variable definition</string> + <key>scope</key> + <string>punctuation.definition.variable.ruby</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Function Name</string> + <key>scope</key> + <string>meta.function.method.with-arguments.ruby</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#657B83</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Variable</string> + <key>scope</key> + <string>variable.language.ruby</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Function</string> + <key>scope</key> + <string>entity.name.function.ruby</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Keyword Control</string> + <key>scope</key> + <string>keyword.control.ruby, keyword.control.def.ruby</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>bold</string> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Class</string> + <key>scope</key> + <string>keyword.control.class.ruby, meta.class.ruby</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Class Name</string> + <key>scope</key> + <string>entity.name.type.class.ruby</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Keyword</string> + <key>scope</key> + <string>keyword.control.ruby</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Support Class</string> + <key>scope</key> + <string>support.class.ruby</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Special Method</string> + <key>scope</key> + <string>keyword.other.special-method.ruby</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Constant</string> + <key>scope</key> + <string>constant.language.ruby, constant.numeric.ruby</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Constant Other</string> + <key>scope</key> + <string>variable.other.constant.ruby</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: :symbol</string> + <key>scope</key> + <string>constant.other.symbol.ruby</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Punctuation Section ''</string> + <key>scope</key> + <string>punctuation.section.embedded.ruby, punctuation.definition.string.begin.ruby, punctuation.definition.string.end.ruby</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Special Method</string> + <key>scope</key> + <string>keyword.other.special-method.ruby</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Include</string> + <key>scope</key> + <string>keyword.control.import.include.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: erb =</string> + <key>scope</key> + <string>text.html.ruby meta.tag.inline.any.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: erb ""</string> + <key>scope</key> + <string>text.html.ruby punctuation.definition.string.begin, text.html.ruby punctuation.definition.string.end</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Quoted Single</string> + <key>scope</key> + <string>punctuation.definition.string.begin, punctuation.definition.string.end</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Class Names</string> + <key>scope</key> + <string>support.class.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#93A1A1</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: []</string> + <key>scope</key> + <string>keyword.operator.index-start.php, keyword.operator.index-end.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Array</string> + <key>scope</key> + <string>meta.array.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Array()</string> + <key>scope</key> + <string>meta.array.php support.function.construct.php, meta.array.empty.php support.function.construct.php</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Array Construct</string> + <key>scope</key> + <string>support.function.construct.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Array Begin</string> + <key>scope</key> + <string>punctuation.definition.array.begin, punctuation.definition.array.end</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Numeric Constant</string> + <key>scope</key> + <string>constant.numeric.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: New</string> + <key>scope</key> + <string>keyword.other.new.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: ::</string> + <key>scope</key> + <string>keyword.operator.class</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#93A1A1</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Other Property</string> + <key>scope</key> + <string>variable.other.property.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Class</string> + <key>scope</key> + <string>storage.modifier.extends.php, storage.type.class.php, keyword.operator.class.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Semicolon</string> + <key>scope</key> + <string>punctuation.terminator.expression.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Inherited Class</string> + <key>scope</key> + <string>meta.other.inherited-class.php</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Storage Type</string> + <key>scope</key> + <string>storage.type.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Function</string> + <key>scope</key> + <string>entity.name.function.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Function Construct</string> + <key>scope</key> + <string>support.function.construct.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Function Call</string> + <key>scope</key> + <string>entity.name.type.class.php, meta.function-call.php, meta.function-call.static.php, meta.function-call.object.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Comment</string> + <key>scope</key> + <string>keyword.other.phpdoc</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Source Emebedded</string> + <key>scope</key> + <string>source.php.embedded.block.html</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Storage Type Function</string> + <key>scope</key> + <string>storage.type.function.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C: constant</string> + <key>scope</key> + <string>constant.numeric.c</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C: Meta Preprocessor</string> + <key>scope</key> + <string>meta.preprocessor.c.include, meta.preprocessor.macro.c</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C: Keyword</string> + <key>scope</key> + <string>keyword.control.import.define.c, keyword.control.import.include.c</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C: Function Preprocessor</string> + <key>scope</key> + <string>entity.name.function.preprocessor.c</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C: include <something.c></string> + <key>scope</key> + <string>meta.preprocessor.c.include string.quoted.other.lt-gt.include.c, meta.preprocessor.c.include punctuation.definition.string.begin.c, meta.preprocessor.c.include punctuation.definition.string.end.c</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C: Function</string> + <key>scope</key> + <string>support.function.C99.c, support.function.any-method.c, entity.name.function.c</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C: "</string> + <key>scope</key> + <string>punctuation.definition.string.begin.c, punctuation.definition.string.end.c</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C: Storage Type</string> + <key>scope</key> + <string>storage.type.c</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>diff: header</string> + <key>scope</key> + <string>meta.diff, meta.diff.header</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#B58900</string> + <key>fontStyle</key> + <string>italic</string> + <key>foreground</key> + <string>#EEE8D5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>diff: deleted</string> + <key>scope</key> + <string>markup.deleted</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#EEE8D5</string> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>diff: changed</string> + <key>scope</key> + <string>markup.changed</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#EEE8D5</string> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>diff: inserted</string> + <key>scope</key> + <string>markup.inserted</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#EEE8D5</string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Linebreak</string> + <key>scope</key> + <string>text.html.markdown meta.dummy.line-break</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#B58900</string> + <key>foreground</key> + <string>#EEE8D5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Raw</string> + <key>scope</key> + <string>text.html.markdown markup.raw.inline</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>reST raw</string> + <key>scope</key> + <string>text.restructuredtext markup.raw</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Other: Removal</string> + <key>scope</key> + <string>other.package.exclude, other.remove</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Other: Add</string> + <key>scope</key> + <string>other.add</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: {}</string> + <key>scope</key> + <string>punctuation.section.group.tex , punctuation.definition.arguments.begin.latex, punctuation.definition.arguments.end.latex, punctuation.definition.arguments.latex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: {text}</string> + <key>scope</key> + <string>meta.group.braces.tex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: Other Math</string> + <key>scope</key> + <string>string.other.math.tex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: {var}</string> + <key>scope</key> + <string>variable.parameter.function.latex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: Math \\</string> + <key>scope</key> + <string>punctuation.definition.constant.math.tex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: Constant Math</string> + <key>scope</key> + <string>text.tex.latex constant.other.math.tex, constant.other.general.math.tex, constant.other.general.math.tex, constant.character.math.tex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: Other Math String</string> + <key>scope</key> + <string>string.other.math.tex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: $</string> + <key>scope</key> + <string>punctuation.definition.string.begin.tex, punctuation.definition.string.end.tex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: \label</string> + <key>scope</key> + <string>keyword.control.label.latex, text.tex.latex constant.other.general.math.tex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: \label { }</string> + <key>scope</key> + <string>variable.parameter.definition.label.latex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: Function</string> + <key>scope</key> + <string>support.function.be.latex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: Support Function Section</string> + <key>scope</key> + <string>support.function.section.latex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: Support Function</string> + <key>scope</key> + <string>support.function.general.tex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: Comment</string> + <key>scope</key> + <string>punctuation.definition.comment.tex, comment.line.percentage.tex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: Reference Label</string> + <key>scope</key> + <string>keyword.control.ref.latex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Python: storage</string> + <key>scope</key> + <string>storage.type.class.python, storage.type.function.python, storage.modifier.global.python</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Python: import</string> + <key>scope</key> + <string>keyword.control.import.python, keyword.control.import.from.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Python: Support.exception</string> + <key>scope</key> + <string>support.type.exception.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell: builtin</string> + <key>scope</key> + <string>support.function.builtin.shell</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell: variable</string> + <key>scope</key> + <string>variable.other.normal.shell</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell: DOT_FILES</string> + <key>scope</key> + <string>source.shell</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell: meta scope in loop</string> + <key>scope</key> + <string>meta.scope.for-in-loop.shell, variable.other.loop.shell</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell: ""</string> + <key>scope</key> + <string>punctuation.definition.string.end.shell, punctuation.definition.string.begin.shell</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell: Meta Block</string> + <key>scope</key> + <string>meta.scope.case-block.shell, meta.scope.case-body.shell</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell: []</string> + <key>scope</key> + <string>punctuation.definition.logical-expression.shell</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell: Comment</string> + <key>scope</key> + <string>comment.line.number-sign.shell</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Java: import</string> + <key>scope</key> + <string>keyword.other.import.java</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Java: meta-import</string> + <key>scope</key> + <string>storage.modifier.import.java</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Java: Class</string> + <key>scope</key> + <string>meta.class.java storage.modifier.java</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Java: /* comment */</string> + <key>scope</key> + <string>source.java comment.block</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Java: /* @param */</string> + <key>scope</key> + <string>comment.block meta.documentation.tag.param.javadoc keyword.other.documentation.param.javadoc</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Perl: variables</string> + <key>scope</key> + <string>punctuation.definition.variable.perl, variable.other.readwrite.global.perl, variable.other.predefined.perl, keyword.operator.comparison.perl</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Perl: functions</string> + <key>scope</key> + <string>support.function.perl</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Perl: comments</string> + <key>scope</key> + <string>comment.line.number-sign.perl</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Perl: quotes</string> + <key>scope</key> + <string>punctuation.definition.string.begin.perl, punctuation.definition.string.end.perl</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Perl: \char</string> + <key>scope</key> + <string>constant.character.escape.perl</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + + <dict> + <key>name</key> + <string>Markdown: Headings</string> + <key>scope</key> + <string>markup.heading.markdown, markup.heading.1.markdown, markup.heading.2.markdown, markup.heading.3.markdown, markup.heading.4.markdown, markup.heading.5.markdown, markup.heading.6.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Bold</string> + <key>scope</key> + <string>markup.bold.markdown</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>bold</string> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Italic</string> + <key>scope</key> + <string>markup.italic.markdown</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Punctuation for Bold, Italic, and Inline Block</string> + <key>scope</key> + <string>punctuation.definition.bold.markdown, punctuation.definition.italic.markdown, punctuation.definition.raw.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Bulleted List</string> + <key>scope</key> + <string>markup.list.unnumbered.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Numbered List</string> + <key>scope</key> + <string>markup.list.numbered.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Block and Inline Block</string> + <key>scope</key> + <string>markup.raw.block.markdown, markup.raw.inline.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Quote Block and Punctuation</string> + <key>scope</key> + <string>markup.quote.markdown, punctuation.definition.blockquote.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#6C71C4</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Seperator</string> + <key>scope</key> + <string>meta.separator.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#D33682</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Link and Reference URL</string> + <key>scope</key> + <string>meta.image.inline.markdown, markup.underline.link.markdown</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Link Title, Image Description</string> + <key>scope</key> + <string>string.other.link.title.markdown, string.other.link.description.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#93A1A1</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Angle Brakets on Link and Image</string> + <key>scope</key> + <string>punctuation.definition.link.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Parens on Link and Image </string> + <key>scope</key> + <string>punctuation.definition.metadata.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Square Brakets on Link, Image, and Reference</string> + <key>scope</key> + <string>punctuation.definition.string.begin.markdown, punctuation.definition.string.end.markdown, punctuation.definition.constant.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>SublimeLinter Annotations</string> + <key>scope</key> + <string>sublimelinter.notes</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#eee8d5</string> + <key>foreground</key> + <string>#eee8d5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>SublimeLinter Error Outline</string> + <key>scope</key> + <string>sublimelinter.outline.illegal</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#93a1a1</string> + <key>foreground</key> + <string>#93a1a1</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>SublimeLinter Error Underline</string> + <key>scope</key> + <string>sublimelinter.underline.illegal</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#dc322f</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>SublimeLinter Warning Outline</string> + <key>scope</key> + <string>sublimelinter.outline.warning</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#839496</string> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>SublimeLinter Warning Underline</string> + <key>scope</key> + <string>sublimelinter.underline.warning</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#b58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>SublimeLinter Violation Outline</string> + <key>scope</key> + <string>sublimelinter.outline.violation</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#657b83</string> + <key>foreground</key> + <string>#657b83</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>SublimeLinter Violation Underline</string> + <key>scope</key> + <string>sublimelinter.underline.violation</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#cb4b16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>SublimeBracketHighlighter</string> + <key>scope</key> + <string>brackethighlighter.all</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#002b36</string> + <key>foreground</key> + <string>#cb4b16</string> + </dict> + </dict> + </array> + <key>uuid</key> + <string>A4299D9B-1DE5-4BC4-87F6-A757E71B1597</string> +</dict> +</plist> diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Solarized Color Scheme/Solarized (light).tmTheme b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Solarized Color Scheme/Solarized (light).tmTheme new file mode 100644 index 0000000..b51af59 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Solarized Color Scheme/Solarized (light).tmTheme @@ -0,0 +1,2146 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>name</key> + <string>Solarized (light)</string> + <key>settings</key> + <array> + <dict> + <key>settings</key> + <dict> + <key>background</key> + <string>#FDF6E3</string> + <key>caret</key> + <string>#002B36</string> + <key>foreground</key> + <string>#586E75</string> + <key>invisibles</key> + <string>#EEE8D5</string> + <key>lineHighlight</key> + <string>#EEE8D5</string> + <key>selection</key> + <string>#073642</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Comment</string> + <key>scope</key> + <string>comment</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#93A1A1</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>String</string> + <key>scope</key> + <string>string</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>StringNumber</string> + <key>scope</key> + <string>string</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Regexp</string> + <key>scope</key> + <string>string.regexp</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Number</string> + <key>scope</key> + <string>constant.numeric</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#D33682</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Variable</string> + <key>scope</key> + <string>variable.language, variable.other</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Keyword</string> + <key>scope</key> + <string>keyword</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Storage</string> + <key>scope</key> + <string>storage</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Class name</string> + <key>scope</key> + <string>entity.name.class, entity.name.type.class</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Function name</string> + <key>scope</key> + <string>entity.name.function</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Variable start</string> + <key>scope</key> + <string>punctuation.definition.variable</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Embedded code markers</string> + <key>scope</key> + <string>punctuation.section.embedded.begin, punctuation.section.embedded.end</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Built-in constant</string> + <key>scope</key> + <string>constant.language, meta.preprocessor</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Support.construct</string> + <key>scope</key> + <string>support.function.construct, keyword.other.new</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>User-defined constant</string> + <key>scope</key> + <string>constant.character, constant.other</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Inherited class</string> + <key>scope</key> + <string>entity.other.inherited-class</string> + <key>settings</key> + <dict/> + </dict> + <dict> + <key>name</key> + <string>Function argument</string> + <key>scope</key> + <string>variable.parameter</string> + <key>settings</key> + <dict/> + </dict> + <dict> + <key>name</key> + <string>Tag name</string> + <key>scope</key> + <string>entity.name.tag</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>bold</string> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tag start/end</string> + <key>scope</key> + <string>punctuation.definition.tag.html, punctuation.definition.tag.begin, punctuation.definition.tag.end</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#93A1A1</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tag attribute</string> + <key>scope</key> + <string>entity.other.attribute-name</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#93A1A1</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Library function</string> + <key>scope</key> + <string>support.function</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Continuation</string> + <key>scope</key> + <string>punctuation.separator.continuation</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Library constant</string> + <key>scope</key> + <string>support.constant</string> + <key>settings</key> + <dict/> + </dict> + <dict> + <key>name</key> + <string>Library class/type</string> + <key>scope</key> + <string>support.type, support.class</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Library Exception</string> + <key>scope</key> + <string>support.type.exception</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Special</string> + <key>scope</key> + <string>keyword.other.special-method</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Library variable</string> + <key>scope</key> + <string>support.other.variable</string> + <key>settings</key> + <dict/> + </dict> + <dict> + <key>name</key> + <string>Invalid</string> + <key>scope</key> + <string>invalid</string> + <key>settings</key> + <dict/> + </dict> + <dict> + <key>name</key> + <string>Quoted String</string> + <key>scope</key> + <string>string.quoted.double, string.quoted.single</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Quotes</string> + <key>scope</key> + <string>punctuation.definition.string.begin, punctuation.definition.string.end</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: Property</string> + <key>scope</key> + <string>entity.name.tag.css, support.type.property-name.css, meta.property-name.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#657B83</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: @font-face</string> + <key>scope</key> + <string>source.css</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: Selector</string> + <key>scope</key> + <string>meta.selector.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#657B83</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: {}</string> + <key>scope</key> + <string>punctuation.section.property-list.css</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: Numeric Value</string> + <key>scope</key> + <string>meta.property-value.css constant.numeric.css, keyword.other.unit.css,constant.other.color.rgb-value.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: Value</string> + <key>scope</key> + <string>meta.property-value.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: !Important</string> + <key>scope</key> + <string>keyword.other.important.css</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: Standard Value</string> + <key>scope</key> + <string>support.constant.color</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#6C71C4</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: Tag</string> + <key>scope</key> + <string>entity.name.tag.css</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: : ,</string> + <key>scope</key> + <string>punctuation.separator.key-value.css, punctuation.terminator.rule.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#657B83</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS .class</string> + <key>scope</key> + <string>entity.other.attribute-name.class.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS :pseudo</string> + <key>scope</key> + <string>entity.other.attribute-name.pseudo-element.css, entity.other.attribute-name.pseudo-class.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>CSS: #id</string> + <key>scope</key> + <string>entity.other.attribute-name.id.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>bold</string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>JS: Function Name</string> + <key>scope</key> + <string>meta.function.js, entity.name.function.js, support.function.dom.js</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>JS: Source</string> + <key>scope</key> + <string>text.html.basic source.js.embedded.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>JS: Function</string> + <key>scope</key> + <string>storage.type.function.js</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>JS: Numeric Constant</string> + <key>scope</key> + <string>constant.numeric.js</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>JS: []</string> + <key>scope</key> + <string>meta.brace.square.js</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>JS: Storage Type</string> + <key>scope</key> + <string>storage.type.js</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>()</string> + <key>scope</key> + <string>meta.brace.round, punctuation.definition.parameters.begin.js, punctuation.definition.parameters.end.js</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#93A1A1</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>{}</string> + <key>scope</key> + <string>meta.brace.curly.js</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: Doctype</string> + <key>scope</key> + <string>entity.name.tag.doctype.html, meta.tag.sgml.html, string.quoted.double.doctype.identifiers-and-DTDs.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: Comment Block</string> + <key>scope</key> + <string>comment.block.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: Script</string> + <key>scope</key> + <string>entity.name.tag.script.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: Style</string> + <key>scope</key> + <string>source.css.embedded.html string.quoted.double.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: Text</string> + <key>scope</key> + <string>text.html.ruby</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>bold</string> + <key>foreground</key> + <string>#657b83</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: =</string> + <key>scope</key> + <string>text.html.basic meta.tag.other.html, text.html.basic meta.tag.any.html, text.html.basic meta.tag.block.any, text.html.basic meta.tag.inline.any, text.html.basic meta.tag.structure.any.html, text.html.basic source.js.embedded.html, punctuation.separator.key-value.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#657B83</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: something=</string> + <key>scope</key> + <string>text.html.basic entity.other.attribute-name.html</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#657B83</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: "</string> + <key>scope</key> + <string>text.html.basic meta.tag.structure.any.html punctuation.definition.string.begin.html, punctuation.definition.string.begin.html, punctuation.definition.string.end.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: <tag></string> + <key>scope</key> + <string>entity.name.tag.block.any.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>bold</string> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: style</string> + <key>scope</key> + <string>source.css.embedded.html entity.name.tag.style.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: <style></string> + <key>scope</key> + <string>entity.name.tag.style.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: {}</string> + <key>scope</key> + <string>text.html.basic punctuation.section.property-list.css</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + </dict> + </dict> + <dict> + <key>name</key> + <string>HTML: Embeddable</string> + <key>scope</key> + <string>source.css.embedded.html, comment.block.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Variable definition</string> + <key>scope</key> + <string>punctuation.definition.variable.ruby</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Function Name</string> + <key>scope</key> + <string>meta.function.method.with-arguments.ruby</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#657B83</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Variable</string> + <key>scope</key> + <string>variable.language.ruby</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Function</string> + <key>scope</key> + <string>entity.name.function.ruby</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Keyword Control</string> + <key>scope</key> + <string>keyword.control.ruby, keyword.control.def.ruby</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>bold</string> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Class</string> + <key>scope</key> + <string>keyword.control.class.ruby, meta.class.ruby</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Class Name</string> + <key>scope</key> + <string>entity.name.type.class.ruby</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Keyword</string> + <key>scope</key> + <string>keyword.control.ruby</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Support Class</string> + <key>scope</key> + <string>support.class.ruby</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Special Method</string> + <key>scope</key> + <string>keyword.other.special-method.ruby</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Constant</string> + <key>scope</key> + <string>constant.language.ruby, constant.numeric.ruby</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Constant Other</string> + <key>scope</key> + <string>variable.other.constant.ruby</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: :symbol</string> + <key>scope</key> + <string>constant.other.symbol.ruby</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Punctuation Section ''</string> + <key>scope</key> + <string>punctuation.section.embedded.ruby, punctuation.definition.string.begin.ruby, punctuation.definition.string.end.ruby</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: Special Method</string> + <key>scope</key> + <string>keyword.other.special-method.ruby</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Include</string> + <key>scope</key> + <string>keyword.control.import.include.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: erb =</string> + <key>scope</key> + <string>text.html.ruby meta.tag.inline.any.html</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Ruby: erb ""</string> + <key>scope</key> + <string>text.html.ruby punctuation.definition.string.begin, text.html.ruby punctuation.definition.string.end</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Quoted Single</string> + <key>scope</key> + <string>punctuation.definition.string.begin, punctuation.definition.string.end</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Class Names</string> + <key>scope</key> + <string>support.class.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: []</string> + <key>scope</key> + <string>keyword.operator.index-start.php, keyword.operator.index-end.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Array</string> + <key>scope</key> + <string>meta.array.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Array()</string> + <key>scope</key> + <string>meta.array.php support.function.construct.php, meta.array.empty.php support.function.construct.php</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Array Construct</string> + <key>scope</key> + <string>support.function.construct.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Array Begin</string> + <key>scope</key> + <string>punctuation.definition.array.begin, punctuation.definition.array.end</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Numeric Constant</string> + <key>scope</key> + <string>constant.numeric.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: New</string> + <key>scope</key> + <string>keyword.other.new.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: ::</string> + <key>scope</key> + <string>keyword.operator.class</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Other Property</string> + <key>scope</key> + <string>variable.other.property.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Class</string> + <key>scope</key> + <string>storage.modifier.extends.php, storage.type.class.php, keyword.operator.class.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Semicolon</string> + <key>scope</key> + <string>punctuation.terminator.expression.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#657B83</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Inherited Class</string> + <key>scope</key> + <string>meta.other.inherited-class.php</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Storage Type</string> + <key>scope</key> + <string>storage.type.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Function</string> + <key>scope</key> + <string>entity.name.function.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Function Construct</string> + <key>scope</key> + <string>support.function.construct.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Function Call</string> + <key>scope</key> + <string>entity.name.type.class.php, meta.function-call.php, meta.function-call.static.php, meta.function-call.object.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Comment</string> + <key>scope</key> + <string>keyword.other.phpdoc</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Source Emebedded</string> + <key>scope</key> + <string>source.php.embedded.block.html</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>PHP: Storage Type Function</string> + <key>scope</key> + <string>storage.type.function.php</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C: constant</string> + <key>scope</key> + <string>constant.numeric.c</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C: Meta Preprocessor</string> + <key>scope</key> + <string>meta.preprocessor.c.include, meta.preprocessor.macro.c</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C: Keyword</string> + <key>scope</key> + <string>keyword.control.import.define.c, keyword.control.import.include.c</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C: Function Preprocessor</string> + <key>scope</key> + <string>entity.name.function.preprocessor.c</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C: include <something.c></string> + <key>scope</key> + <string>meta.preprocessor.c.include string.quoted.other.lt-gt.include.c, meta.preprocessor.c.include punctuation.definition.string.begin.c, meta.preprocessor.c.include punctuation.definition.string.end.c</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C: Function</string> + <key>scope</key> + <string>support.function.C99.c, support.function.any-method.c, entity.name.function.c</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C: "</string> + <key>scope</key> + <string>punctuation.definition.string.begin.c, punctuation.definition.string.end.c</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>C: Storage Type</string> + <key>scope</key> + <string>storage.type.c</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>diff: header</string> + <key>scope</key> + <string>meta.diff, meta.diff.header</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#B58900</string> + <key>fontStyle</key> + <string>italic</string> + <key>foreground</key> + <string>#EEE8D5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>diff: deleted</string> + <key>scope</key> + <string>markup.deleted</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#EEE8D5</string> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>diff: changed</string> + <key>scope</key> + <string>markup.changed</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#EEE8D5</string> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>diff: inserted</string> + <key>scope</key> + <string>markup.inserted</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#EEE8D5</string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Linebreak</string> + <key>scope</key> + <string>text.html.markdown meta.dummy.line-break</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#B58900</string> + <key>foreground</key> + <string>#EEE8D5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Raw</string> + <key>scope</key> + <string>text.html.markdown markup.raw.inline</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>reST raw</string> + <key>scope</key> + <string>text.restructuredtext markup.raw</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Other: Removal</string> + <key>scope</key> + <string>other.package.exclude, other.remove</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Other: Add</string> + <key>scope</key> + <string>other.add</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: {}</string> + <key>scope</key> + <string>punctuation.section.group.tex , punctuation.definition.arguments.begin.latex, punctuation.definition.arguments.end.latex, punctuation.definition.arguments.latex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: {text}</string> + <key>scope</key> + <string>meta.group.braces.tex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: Other Math</string> + <key>scope</key> + <string>string.other.math.tex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: {var}</string> + <key>scope</key> + <string>variable.parameter.function.latex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: Math \\</string> + <key>scope</key> + <string>punctuation.definition.constant.math.tex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: Constant Math</string> + <key>scope</key> + <string>text.tex.latex constant.other.math.tex, constant.other.general.math.tex, constant.other.general.math.tex, constant.character.math.tex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: Other Math String</string> + <key>scope</key> + <string>string.other.math.tex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: $</string> + <key>scope</key> + <string>punctuation.definition.string.begin.tex, punctuation.definition.string.end.tex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: \label</string> + <key>scope</key> + <string>keyword.control.label.latex, text.tex.latex constant.other.general.math.tex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: \label { }</string> + <key>scope</key> + <string>variable.parameter.definition.label.latex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: Function</string> + <key>scope</key> + <string>support.function.be.latex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: Support Function Section</string> + <key>scope</key> + <string>support.function.section.latex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: Support Function</string> + <key>scope</key> + <string>support.function.general.tex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: Comment</string> + <key>scope</key> + <string>punctuation.definition.comment.tex, comment.line.percentage.tex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Tex: Reference Label</string> + <key>scope</key> + <string>keyword.control.ref.latex</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Python: storage</string> + <key>scope</key> + <string>storage.type.class.python, storage.type.function.python, storage.modifier.global.python</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Python: import</string> + <key>scope</key> + <string>keyword.control.import.python, keyword.control.import.from.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Python: Support.exception</string> + <key>scope</key> + <string>support.type.exception.python</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell: builtin</string> + <key>scope</key> + <string>support.function.builtin.shell</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell: variable</string> + <key>scope</key> + <string>variable.other.normal.shell</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell: DOT_FILES</string> + <key>scope</key> + <string>source.shell</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell: meta scope in loop</string> + <key>scope</key> + <string>meta.scope.for-in-loop.shell, variable.other.loop.shell</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell: ""</string> + <key>scope</key> + <string>punctuation.definition.string.end.shell, punctuation.definition.string.begin.shell</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell: Meta Block</string> + <key>scope</key> + <string>meta.scope.case-block.shell, meta.scope.case-body.shell</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell: []</string> + <key>scope</key> + <string>punctuation.definition.logical-expression.shell</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Shell: Comment</string> + <key>scope</key> + <string>comment.line.number-sign.shell</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Java: import</string> + <key>scope</key> + <string>keyword.other.import.java</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#CB4B16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Java: meta-import</string> + <key>scope</key> + <string>storage.modifier.import.java</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Java: Class</string> + <key>scope</key> + <string>meta.class.java storage.modifier.java</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Java: /* comment */</string> + <key>scope</key> + <string>source.java comment.block</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Java: /* @param */</string> + <key>scope</key> + <string>comment.block meta.documentation.tag.param.javadoc keyword.other.documentation.param.javadoc</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Perl: variables</string> + <key>scope</key> + <string>punctuation.definition.variable.perl, variable.other.readwrite.global.perl, variable.other.predefined.perl, keyword.operator.comparison.perl</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Perl: functions</string> + <key>scope</key> + <string>support.function.perl</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Perl: comments</string> + <key>scope</key> + <string>comment.line.number-sign.perl</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Perl: quotes</string> + <key>scope</key> + <string>punctuation.definition.string.begin.perl, punctuation.definition.string.end.perl</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Perl: \char</string> + <key>scope</key> + <string>constant.character.escape.perl</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + + <dict> + <key>name</key> + <string>Markdown: Headings</string> + <key>scope</key> + <string>markup.heading.markdown, markup.heading.1.markdown, markup.heading.2.markdown, markup.heading.3.markdown, markup.heading.4.markdown, markup.heading.5.markdown, markup.heading.6.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#268BD2</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Bold</string> + <key>scope</key> + <string>markup.bold.markdown</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>bold</string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Italic</string> + <key>scope</key> + <string>markup.italic.markdown</string> + <key>settings</key> + <dict> + <key>fontStyle</key> + <string>italic</string> + <key>foreground</key> + <string>#586E75</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Punctuation for Bold, Italic, and Inline Block</string> + <key>scope</key> + <string>punctuation.definition.bold.markdown, punctuation.definition.italic.markdown, punctuation.definition.raw.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Bulleted List</string> + <key>scope</key> + <string>markup.list.unnumbered.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#B58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Numbered List</string> + <key>scope</key> + <string>markup.list.numbered.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#859900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>Markdown: Block and Inline Block</string> + <key>scope</key> + <string>markup.raw.block.markdown, markup.raw.inline.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#2AA198</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>markup.quote.markdown</string> + <key>scope</key> + <string>markup.quote.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#6C71C4</string> + </dict> + </dict> + + <dict> + <key>name</key> + <string>punctuation.definition.blockquote.markdown</string> + <key>scope</key> + <string>punctuation.definition.blockquote.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#6C71C4</string> + </dict> + </dict> + + <dict> + <key>name</key> + <string>Markdown: Seperator</string> + <key>scope</key> + <string>meta.separator.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#D33682</string> + </dict> + </dict> + + <dict> + <key>name</key> + <string>Markdown: Link URL, Reference</string> + <key>scope</key> + <string>markup.underline.link.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + + <dict> + <key>name</key> + <string>Markdown: Link Title</string> + <key>scope</key> + <string>markup.underline.link.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + + <dict> + <key>name</key> + <string>Markdown: Link Punctuation</string> + <key>scope</key> + <string>meta.link.inet.markdown, meta.link.email.lt-gt.markdown, punctuation.definition.string.begin.markdown, punctuation.definition.string.end.markdown, punctuation.definition.link.markdown</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#DC322F</string> + </dict> + </dict> + + <dict> + <key>name</key> + <string>text plain</string> + <key>scope</key> + <string>text.plain</string> + <key>settings</key> + <dict> + <key>foreground</key> + <string>#657B83</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>SublimeLinter Annotations</string> + <key>scope</key> + <string>sublimelinter.notes</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#eee8d5</string> + <key>foreground</key> + <string>#eee8d5</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>SublimeLinter Error Outline</string> + <key>scope</key> + <string>sublimelinter.outline.illegal</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#93a1a1</string> + <key>foreground</key> + <string>#93a1a1</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>SublimeLinter Error Underline</string> + <key>scope</key> + <string>sublimelinter.underline.illegal</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#dc322f</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>SublimeLinter Warning Outline</string> + <key>scope</key> + <string>sublimelinter.outline.warning</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#839496</string> + <key>foreground</key> + <string>#839496</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>SublimeLinter Warning Underline</string> + <key>scope</key> + <string>sublimelinter.underline.warning</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#b58900</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>SublimeLinter Violation Outline</string> + <key>scope</key> + <string>sublimelinter.outline.violation</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#657b83</string> + <key>foreground</key> + <string>#657b83</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>SublimeLinter Violation Underline</string> + <key>scope</key> + <string>sublimelinter.underline.violation</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#cb4b16</string> + </dict> + </dict> + <dict> + <key>name</key> + <string>SublimeBracketHighlighter</string> + <key>scope</key> + <string>brackethighlighter.all</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#FDF6E3</string> + <key>foreground</key> + <string>#cb4b16</string> + </dict> + </dict> + </array> + <key>uuid</key> + <string>38E819D9-AE02-452F-9231-ECC3B204AFD7</string> +</dict> +</plist> diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Solarized Color Scheme/package-metadata.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Solarized Color Scheme/package-metadata.json new file mode 100644 index 0000000..8806a64 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/Solarized Color Scheme/package-metadata.json @@ -0,0 +1 @@ +{"url": "https://github.com/SublimeColors/Solarized", "version": "2012.11.02.15.06.04", "description": "A port of the popular Solarized Theme for Sublime Text 2"} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/Default.sublime-commands b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/Default.sublime-commands new file mode 100644 index 0000000..e98178c --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/Default.sublime-commands @@ -0,0 +1,66 @@ +[ + { + "caption": "StringEncode: HTML Entitize", + "command": "html_entitize" + }, + { + "caption": "StringEncode: HTML Deentitize", + "command": "html_deentitize" + }, + { + "caption": "StringEncode: XML Entitize", + "command": "xml_entitize" + }, + { + "caption": "StringEncode: XML Deentitize", + "command": "xml_deentitize" + }, + { + "caption": "StringEncode: Safe HTML Entitize", + "command": "safe_html_entitize" + }, + { + "caption": "StringEncode: Safe HTML Deentitize", + "command": "safe_html_deentitize" + }, + { + "caption": "StringEncode: JSON Escape", + "command": "json_escape" + }, + { + "caption": "StringEncode: JSON Unescape", + "command": "json_unescape" + }, + { + "caption": "StringEncode: URL Encode", + "command": "url_encode" + }, + { + "caption": "StringEncode: URL Decode", + "command": "url_decode" + }, + { + "caption": "StringEncode: Base64 Encode", + "command": "base64_encode" + }, + { + "caption": "StringEncode: Base64 Decode", + "command": "base64_decode" + }, + { + "caption": "StringEncode: Escape regex", + "command": "escape_regex" + }, + { + "caption": "StringEncode: Escape LIKE", + "command": "escape_like" + }, + { + "caption": "StringEncode: Decimal to Hexadecimal", + "command": "dec_hex" + }, + { + "caption": "StringEncode: Hexadecimal to Decimal", + "command": "hex_dec" + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/Example.sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/Example.sublime-keymap new file mode 100644 index 0000000..84dce04 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/Example.sublime-keymap @@ -0,0 +1,14 @@ +[ + { "keys": ["super+shift+7"], "command": "xml_entitize", "scope": "text.xml" }, + { "keys": ["super+ctrl+7"], "command": "xml_deentitize", "scope": "text.xml" }, + { "keys": ["super+shift+7"], "command": "html_entitize" }, + { "keys": ["super+ctrl+7"], "command": "html_deentitize" }, + { "keys": ["super+shift+8"], "command": "json_escape" }, + { "keys": ["super+ctrl+8"], "command": "json_unescape" }, + { "keys": ["super+shift+6"], "command": "base64_encode" }, + { "keys": ["super+ctrl+6"], "command": "base64_decode" }, + { "keys": ["super+shift+5"], "command": "url_encode" }, + { "keys": ["super+ctrl+5"], "command": "url_decode" }, + { "keys": ["ctrl+shift+r"], "command": "escape_regex" }, + { "keys": ["super+ctrl+3"], "command": "hex_dec" } +] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/README.md b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/README.md new file mode 100644 index 0000000..18aa332 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/README.md @@ -0,0 +1,52 @@ +StringEncode plugin for Sublime Text 2 +====================================== + +Converts characters from one "encoding" to another using a transformation (think HTML entities, not character encodings). + +Installation +------------ + +1. Using Package Control, install "StringEncode" + +Or: + +1. Open the Sublime Text 2 Packages folder + - OS X: ~/Library/Application Support/Sublime Text 2/Packages/ + - Windows: %APPDATA%/Sublime Text 2/Packages/ + - Linux: ~/.Sublime Text 2/Packages/ +2. clone this repo +3. Install keymaps for the commands (see Example.sublime-keymap for my preferred keys) + +Commands +-------- + +`html_entitize`: Converts characters to their HTML entity + +`html_deentitize`: Converts HTML entities to a character + +`url_encode`: Uses urllib.quote to escape special URL characters + +`url_decode`: Uses urllib.unquote to convert escaped URL characters + +`json_escape`: Escapes a string and surrounds it in quotes, according to the JSON encoding. + +`json_unescape`: Unescapes a string (include the quotes!) according to JSON encoding. + +`base64_encode`: Uses base64 to encode into base64 + +`base64_decode`: Uses base64 to decode from base64 + +`escape_regex`: Escapes regex meta characters + +`escape_like`: Escapes SQL-LIKE meta characters + +`safe_html_entitize`: Converts characters to their HTML entity, but preserves HTML reserved characters + +`safe_html_deentitize`: Converts HTML entities to a character, but preserves HTML reserved characters + +TODO +---- + +`xml_entitize`: Converts characters to their XML entity + +`xml_deentitize`: Converts XML entities to a character diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/package-metadata.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/package-metadata.json new file mode 100644 index 0000000..f32ec30 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/package-metadata.json @@ -0,0 +1 @@ +{"url": "https://github.com/colinta/SublimeStringEncode", "version": "1.5.1", "description": "Converts characters from one encoding to another using a transformation (think HTML entities, not character encodings)."} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/package.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/package.json new file mode 100644 index 0000000..1da7f07 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/package.json @@ -0,0 +1,7 @@ +{ + "repo": "SublimeStringEncode", + "name": "StringEncode", + "description": "Converts characters from one encoding to another using a transformation (think HTML entities, not character encodings).", + "author": "Colin Thomas-Arnold (colinta)", + "homepage": "https://github.com/colinta/SublimeStringEncode" +} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/string_encode.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/string_encode.py new file mode 100644 index 0000000..efadd24 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/StringEncode/string_encode.py @@ -0,0 +1,165 @@ +# coding: utf8 + +import sublime_plugin +import urllib +import base64 +import re +import json + + +class StringEncode(sublime_plugin.TextCommand): + def run(self, edit): + e = self.view.begin_edit('encode') + regions = [region for region in self.view.sel()] + + # sort by region.end() DESC + def get_end(region): + return region.end() + regions.sort(key=get_end, reverse=True) + + for region in regions: + if region.empty(): + continue + text = self.view.substr(region) + replacement = self.encode(text) + self.view.replace(edit, region, replacement) + self.view.end_edit(e) + + +html_escape_table = { + u"\"": """, u"'": "'", u"<": "<", u">": ">", u"¡": "¡", u"¢": "¢", u"£": "£", u"¤": "¤", u"¥": "¥", u"¦": "¦", u"§": "§", u"¨": "¨", u"©": "©", u"ª": "ª", u"«": "«", u"¬": "¬", u"®": "®", u"¯": "¯", u"°": "°", u"±": "±", u"²": "²", u"³": "³", u"´": "´", u"µ": "µ", u"¶": "¶", u"·": "·", u"¸": "¸", u"¹": "¹", u"º": "º", u"»": "»", u"¼": "¼", u"½": "½", u"¾": "¾", u"¿": "¿", u"À": "À", u"Á": "Á", u"Â": "Â", u"Ã": "Ã", u"Ä": "Ä", u"Å": "Å", u"Æ": "Æ", u"Ç": "Ç", u"È": "È", u"É": "É", u"Ê": "Ê", u"Ë": "Ë", u"Ì": "Ì", u"Í": "Í", u"Î": "Î", u"Ï": "Ï", u"Ð": "Ð", u"Ñ": "Ñ", u"Ò": "Ò", u"Ó": "Ó", u"Ô": "Ô", u"Õ": "Õ", u"Ö": "Ö", u"×": "×", u"Ø": "Ø", u"Ù": "Ù", u"Ú": "Ú", u"Û": "Û", u"Ü": "Ü", u"Ý": "Ý", u"Þ": "Þ", u"ß": "ß", u"à": "à", u"á": "á", u"â": "â", u"ã": "ã", u"ä": "ä", u"å": "å", u"æ": "æ", u"ç": "ç", u"è": "è", u"é": "é", u"ê": "ê", u"ë": "ë", u"ì": "ì", u"í": "í", u"î": "î", u"ï": "ï", u"ð": "ð", u"ñ": "ñ", u"ò": "ò", u"ó": "ó", u"ô": "ô", u"õ": "õ", u"ö": "ö", u"÷": "÷", u"ø": "ø", u"ù": "ù", u"ú": "ú", u"û": "û", u"ü": "ü", u"ý": "ý", u"þ": "þ", u"ÿ": "ÿ", u"Œ": "Œ", u"œ": "œ", u"Š": "Š", u"š": "š", u"Ÿ": "Ÿ", u"ƒ": "ƒ", u"ˆ": "ˆ", u"˜": "˜", u"Α": "Α", u"Β": "Β", u"Γ": "Γ", u"Δ": "Δ", u"Ε": "Ε", u"Ζ": "Ζ", u"Η": "Η", u"Θ": "Θ", u"Ι": "Ι", u"Κ": "Κ", u"Λ": "Λ", u"Μ": "Μ", u"Ν": "Ν", u"Ξ": "Ξ", u"Ο": "Ο", u"Π": "Π", u"Ρ": "Ρ", u"Σ": "Σ", u"Τ": "Τ", u"Υ": "Υ", u"Φ": "Φ", u"Χ": "Χ", u"Ψ": "Ψ", u"Ω": "Ω", u"α": "α", u"β": "β", u"γ": "γ", u"δ": "δ", u"ε": "ε", u"ζ": "ζ", u"η": "η", u"θ": "θ", u"ι": "ι", u"κ": "κ", u"λ": "λ", u"μ": "μ", u"ν": "ν", u"ξ": "ξ", u"ο": "ο", u"π": "π", u"ρ": "ρ", u"ς": "ς", u"σ": "σ", u"τ": "τ", u"υ": "υ", u"φ": "φ", u"χ": "χ", u"ψ": "ψ", u"ω": "ω", u"ϑ": "ϑ", u"ϒ": "ϒ", u"ϖ": "ϖ", u"–": "–", u"—": "—", u"‘": "‘", u"’": "’", u"‚": "‚", u"“": "“", u"”": "”", u"„": "„", u"†": "†", u"‡": "‡", u"•": "•", u"…": "…", u"‰": "‰", u"′": "′", u"″": "″", u"‹": "‹", u"›": "›", u"‾": "‾", u"⁄": "⁄", u"€": "€", u"ℑ": "ℑ", u"℘": "℘", u"ℜ": "ℜ", u"™": "™", u"ℵ": "ℵ", u"←": "←", u"↑": "↑", u"→": "→", u"↓": "↓", u"↔": "↔", u"↵": "↵", u"⇐": "⇐", u"⇑": "⇑", u"⇒": "⇒", u"⇓": "⇓", u"⇔": "⇔", u"∀": "∀", u"∂": "∂", u"∃": "∃", u"∅": "∅", u"∇": "∇", u"∈": "∈", u"∉": "∉", u"∋": "∋", u"∏": "∏", u"∑": "∑", u"−": "−", u"∗": "∗", u"√": "√", u"∝": "∝", u"∞": "∞", u"∠": "∠", u"∧": "∧", u"∨": "∨", u"∩": "∩", u"∪": "∪", u"∫": "∫", u"∴": "∴", u"∼": "∼", u"≅": "≅", u"≈": "≈", u"≠": "≠", u"≡": "≡", u"≤": "≤", u"≥": "≥", u"⊂": "⊂", u"⊃": "⊃", u"⊄": "⊄", u"⊆": "⊆", u"⊇": "⊇", u"⊕": "⊕", u"⊗": "⊗", u"⊥": "⊥", u"⋅": "⋅", u"⌈": "⌈", u"⌉": "⌉", u"⌊": "⌊", u"⌋": "⌋", u"〈": "⟨", u"〉": "⟩", u"◊": "◊", u"♠": "♠", u"♣": "♣", u"♥": "♥", u"♦": "♦", +} +xml_escape_table = { + u"\"": """, u"'": "'", u"<": "<", u">": ">" +} +html_reserved_list = (u"\"", u"'", u"<", u">", u"&") + + +class HtmlEntitizeCommand(StringEncode): + def encode(self, text): + text = text.replace('&', '&') + for k in html_escape_table: + v = html_escape_table[k] + text = text.replace(k, v) + ret = '' + for i, c in enumerate(text): + if ord(c) > 127: + ret += hex(ord(c)).replace('0x', '&#x') + ';' + else: + ret += c + return ret + + +class HtmlDeentitizeCommand(StringEncode): + def encode(self, text): + for k in html_escape_table: + v = html_escape_table[k] + text = text.replace(v, k) + while re.search('&#[xX][a-fA-F0-9]+;', text): + match = re.search('&#[xX]([a-fA-F0-9]+);', text) + text = text.replace(match.group(0), unichr(int('0x' + match.group(1), 16))) + text = text.replace('&', '&') + return text + + +class SafeHtmlEntitizeCommand(StringEncode): + def encode(self, text): + for k in html_escape_table: + # skip HTML reserved characters + if k in html_reserved_list: + continue + v = html_escape_table[k] + text = text.replace(k, v) + ret = '' + for i, c in enumerate(text): + if ord(c) > 127: + ret += hex(ord(c)).replace('0x', '&#x') + ';' + else: + ret += c + return ret + + +class SafeHtmlDeentitizeCommand(StringEncode): + def encode(self, text): + for k in html_escape_table: + # skip HTML reserved characters + if k in html_reserved_list: + continue + v = html_escape_table[k] + text = text.replace(v, k) + while re.search('&#[xX][a-fA-F0-9]+;', text): + match = re.search('&#[xX]([a-fA-F0-9]+);', text) + text = text.replace(match.group(0), unichr(int('0x' + match.group(1), 16))) + text = text.replace('&', '&') + return text + + +class XmlEntitizeCommand(StringEncode): + def encode(self, text): + text = text.replace('&', '&') + for k in xml_escape_table: + v = xml_escape_table[k] + text = text.replace(k, v) + return text + + +class XmlDeentitizeCommand(StringEncode): + def encode(self, text): + for k in xml_escape_table: + v = xml_escape_table[k] + text = text.replace(v, k) + text = text.replace('&', '&') + return text + + +class JsonEscapeCommand(StringEncode): + def encode(self, text): + return json.dumps(text) + + +class JsonUnescapeCommand(StringEncode): + def encode(self, text): + return json.loads(text) + + +class UrlEncodeCommand(StringEncode): + def encode(self, text): + return urllib.quote(text) + + +class UrlDecodeCommand(StringEncode): + def encode(self, text): + return urllib.unquote(text) + + +class Base64EncodeCommand(StringEncode): + def encode(self, text): + return base64.b64encode(text) + + +class Base64DecodeCommand(StringEncode): + def encode(self, text): + return base64.b64decode(text) + + +class Escaper(StringEncode): + def encode(self, text): + return re.sub(r'(?<!\\)(%s)' % self.meta, r'\\\1', text) + + +class EscapeRegexCommand(Escaper): + meta = r'[\\*.+^$()\[\]\{\}]' + + +class EscapeLikeCommand(Escaper): + meta = r'[%_]' + + +class HexDecCommand(StringEncode): + def encode(self, text): + return str(int(text, 16)) + + +class DecHexCommand(StringEncode): + def encode(self, text): + return hex(int(text)) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (Linux).sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (Linux).sublime-keymap new file mode 100644 index 0000000..d050399 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (Linux).sublime-keymap @@ -0,0 +1,80 @@ +[ + + // { + // "keys": ["n"], "command": "goto_next_result", + // "context": [ + // { "key": "setting.todo_results", "operator": "equal", "operand": true } + // ] + // } + + { + "keys": ["n"], "command": "navigate_results", + "context": [ + {"key": "setting.command_mode", "operand": true} + // {"key": "setting.todo_results"} + ], + "args": {"direction": "forward"} + }, + + { + "keys": ["down"], "command": "navigate_results", + "context": [ + {"key": "setting.command_mode", "operand": true} + // {"key": "setting.todo_results"} + ], + "args": {"direction": "forward"} + }, + + { + "keys": ["j"], "command": "navigate_results", + "context": [ + {"key": "setting.command_mode", "operand": true} + // {"key": "setting.todo_results"} + ], + "args": {"direction": "forward"} + }, + + { + "keys": ["p"], "command": "navigate_results", + "context": [ + {"key": "setting.command_mode", "operand": true} + // {"key": "setting.todo_results"} + ], + "args": {"direction": "backward"} + }, + + { + "keys": ["up"], "command": "navigate_results", + "context": [ + {"key": "setting.command_mode", "operand": true} + //{"key": "setting.todo_results"} + ], + "args": {"direction": "backward"} + }, + + { + "keys": ["k"], "command": "navigate_results", + "context": [ + {"key": "setting.command_mode", "operand": true} + //{"key": "setting.todo_results"} + ], + "args": {"direction": "backward"} + }, + + { + "keys": ["c"], "command": "clear_selection", + "context": [ + {"key": "setting.command_mode", "operand": true} + // {"key": "setting.todo_results"} + ] + }, + + { + "keys": ["enter"], "command": "goto_comment", + "context": [ + {"key": "setting.command_mode", "operand": true} + // {"key": "setting.todo_results"} + ] + } + +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (Linux).sublime-mousemap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (Linux).sublime-mousemap new file mode 100644 index 0000000..f3868a5 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (Linux).sublime-mousemap @@ -0,0 +1,6 @@ +[ + { + "button": "button1", "count": 2, "modifiers": ["shift"], + "command": "mouse_goto_comment" + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (OSX).sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (OSX).sublime-keymap new file mode 100644 index 0000000..d050399 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (OSX).sublime-keymap @@ -0,0 +1,80 @@ +[ + + // { + // "keys": ["n"], "command": "goto_next_result", + // "context": [ + // { "key": "setting.todo_results", "operator": "equal", "operand": true } + // ] + // } + + { + "keys": ["n"], "command": "navigate_results", + "context": [ + {"key": "setting.command_mode", "operand": true} + // {"key": "setting.todo_results"} + ], + "args": {"direction": "forward"} + }, + + { + "keys": ["down"], "command": "navigate_results", + "context": [ + {"key": "setting.command_mode", "operand": true} + // {"key": "setting.todo_results"} + ], + "args": {"direction": "forward"} + }, + + { + "keys": ["j"], "command": "navigate_results", + "context": [ + {"key": "setting.command_mode", "operand": true} + // {"key": "setting.todo_results"} + ], + "args": {"direction": "forward"} + }, + + { + "keys": ["p"], "command": "navigate_results", + "context": [ + {"key": "setting.command_mode", "operand": true} + // {"key": "setting.todo_results"} + ], + "args": {"direction": "backward"} + }, + + { + "keys": ["up"], "command": "navigate_results", + "context": [ + {"key": "setting.command_mode", "operand": true} + //{"key": "setting.todo_results"} + ], + "args": {"direction": "backward"} + }, + + { + "keys": ["k"], "command": "navigate_results", + "context": [ + {"key": "setting.command_mode", "operand": true} + //{"key": "setting.todo_results"} + ], + "args": {"direction": "backward"} + }, + + { + "keys": ["c"], "command": "clear_selection", + "context": [ + {"key": "setting.command_mode", "operand": true} + // {"key": "setting.todo_results"} + ] + }, + + { + "keys": ["enter"], "command": "goto_comment", + "context": [ + {"key": "setting.command_mode", "operand": true} + // {"key": "setting.todo_results"} + ] + } + +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (OSX).sublime-mousemap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (OSX).sublime-mousemap new file mode 100644 index 0000000..41485cf --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (OSX).sublime-mousemap @@ -0,0 +1,6 @@ +[ + { + "button": "button1", "count": 2, "modifiers": ["alt"], + "command": "mouse_goto_comment" + } +] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (Windows).sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (Windows).sublime-keymap new file mode 100644 index 0000000..d050399 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (Windows).sublime-keymap @@ -0,0 +1,80 @@ +[ + + // { + // "keys": ["n"], "command": "goto_next_result", + // "context": [ + // { "key": "setting.todo_results", "operator": "equal", "operand": true } + // ] + // } + + { + "keys": ["n"], "command": "navigate_results", + "context": [ + {"key": "setting.command_mode", "operand": true} + // {"key": "setting.todo_results"} + ], + "args": {"direction": "forward"} + }, + + { + "keys": ["down"], "command": "navigate_results", + "context": [ + {"key": "setting.command_mode", "operand": true} + // {"key": "setting.todo_results"} + ], + "args": {"direction": "forward"} + }, + + { + "keys": ["j"], "command": "navigate_results", + "context": [ + {"key": "setting.command_mode", "operand": true} + // {"key": "setting.todo_results"} + ], + "args": {"direction": "forward"} + }, + + { + "keys": ["p"], "command": "navigate_results", + "context": [ + {"key": "setting.command_mode", "operand": true} + // {"key": "setting.todo_results"} + ], + "args": {"direction": "backward"} + }, + + { + "keys": ["up"], "command": "navigate_results", + "context": [ + {"key": "setting.command_mode", "operand": true} + //{"key": "setting.todo_results"} + ], + "args": {"direction": "backward"} + }, + + { + "keys": ["k"], "command": "navigate_results", + "context": [ + {"key": "setting.command_mode", "operand": true} + //{"key": "setting.todo_results"} + ], + "args": {"direction": "backward"} + }, + + { + "keys": ["c"], "command": "clear_selection", + "context": [ + {"key": "setting.command_mode", "operand": true} + // {"key": "setting.todo_results"} + ] + }, + + { + "keys": ["enter"], "command": "goto_comment", + "context": [ + {"key": "setting.command_mode", "operand": true} + // {"key": "setting.todo_results"} + ] + } + +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (Windows).sublime-mousemap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (Windows).sublime-mousemap new file mode 100644 index 0000000..41485cf --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default (Windows).sublime-mousemap @@ -0,0 +1,6 @@ +[ + { + "button": "button1", "count": 2, "modifiers": ["alt"], + "command": "mouse_goto_comment" + } +] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default.sublime-commands b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default.sublime-commands new file mode 100644 index 0000000..8e033e9 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/Default.sublime-commands @@ -0,0 +1,10 @@ +[ + { + "caption": "Show TODOs: Project and open files", + "command": "todo" + }, + { + "caption": "Show TODOs: Open files only", + "command": "todo", "args": {"open_files_only": true} + } +] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/README.markdown b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/README.markdown new file mode 100644 index 0000000..509343b --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/README.markdown @@ -0,0 +1,117 @@ +# Sublime TODOs + +A Sublime Text 2 plugin to extract and list TODO comments from open files and +project folders. + +Take a look at [this screencast](http://webdesign.tutsplus.com/tutorials/applications/quick-tip-streamline-your-todo-lists-in-sublime-text-2/) (courtesy of Shannon Huffman) for an overview. + + +# Install + +The preferred method is to use the [Sublime Package Manager](http://wbond.net/sublime_packages/package_control). Alternatively, checkout from github: + +```sh +$ cd Sublime Text 2/Packages +$ git clone https://robcowie@github.com/robcowie/SublimeTODO.git +``` + +# Config + +All plugin configuration must be placed in user or project-specific settings inside a `todo` object, for example; + +```javascript +{ + // other user config ... + "todo": { + "patterns": {} + } +} +``` + +See an example user settings file [here](https://gist.github.com/2049887). + + +## Adding comment patterns + +Extraction uses regular expressions that return one match group +representing the message. Default patterns are provided for `TODO`, `NOTE`, `FIXME` +and `CHANGED` comments. +To override or provide more patterns, add `patterns` to user settings, e.g. + +```javascript +"patterns": { + "TODO": "TODO[\\s]*?:+(?P<todo>.*)$", + "NOTE": "NOTE[\\s]*?:+(?P<note>.*)$", + "FIXME": "FIX ?ME[\\s]*?:+(?P<fixme>\\S.*)$", + "CHANGED": "CHANGED[\\s]*?:+(?P<changed>\\S.*)$" +} +``` + +Note that the pattern _must_ provide at least one named group which will be used to group the comments in results. + +By default, searching is not case sensitive. You can change this behaviour by adding + + "case_sensitive": true + +to the todo settings object. + + +## Excluding files and folders + +Global settings `folder_exclude_patterns`, `file_exclude_patterns` and `binary_file_patterns` are excluded from search results. + +To exclude further directories, add directory names (not glob pattern or regexp) to `folder_exclude_patterns` in todo settings: + +```javascript +"todo": { + "folder_exclude_patterns": [ + "vendor", + "tmp" + ] +} +``` + +To add file excludes, add glob patterns to `file_exclude_patterns`: + +```javascript +"file_exclude_patterns": [ + "*.css" +] +``` + + +## Results title + +Override the results view title by setting `result_title` + +```javascript +"result_title": "TODO Results" +``` + +# Usage + +`Show TODOs: Project and open files` scans all files in your project +`Show TODOs: Open files only` scans only open, saved files +Both are triggered from the command palette. No default key bindings are provided. + +## Navigating results + +Results can be navigated by keyboard and mouse: + + * `n`ext, `p`revious, `c`lear, `enter` + * `alt-double click` (`shift-double click` in Linux) + + Note that due to the lack of support for context in mousemaps right now, + alt-double click will trigger in _any_ document, though it should be a no-op. + +# License + +All of SublimeTODO is licensed under the MIT license. + +Copyright (c) 2012 Rob Cowie <szaz@mac.com> + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/messages.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/messages.json new file mode 100644 index 0000000..ce3c122 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/messages.json @@ -0,0 +1,3 @@ +{ + "1.1.0": "messages/1.1.0.txt" +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/messages/1.1.0.txt b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/messages/1.1.0.txt new file mode 100644 index 0000000..647fe56 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/messages/1.1.0.txt @@ -0,0 +1,6 @@ +SublimeTODO 1.1.0 Changelog: + + - Repo reorg to support explicit versioning and Package Control upgrade messages. + + - [LINUX] alt + dblclick is replaced by shift + dblclick to prevent clash + with common alt+click+drag method of moving windows. diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/package-metadata.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/package-metadata.json new file mode 100644 index 0000000..83e3860 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/package-metadata.json @@ -0,0 +1 @@ +{"url": "https://github.com/robcowie/SublimeTODO", "version": "1.1.3", "description": "Extract TODO-type comments from open files and project folders"} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/packages.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/packages.json new file mode 100644 index 0000000..423b3cc --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/packages.json @@ -0,0 +1,20 @@ +{ + "schema_version": "1.2", + "packages": [ + { + "name": "SublimeTODO", + "description": "Extract TODO-type comments from open files and project folders", + "author": "Rob Cowie", + "homepage": "https://github.com/robcowie/SublimeTODO", + "last_modified": "2012-11-12 10:16:00", + "platforms": { + "*": [ + { + "version": "1.1.2", + "url": "https://nodeload.github.com/robcowie/SublimeTODO/zipball/1.1.2" + } + ] + } + } + ] +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/todo.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/todo.py new file mode 100644 index 0000000..40c15c0 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/todo.py @@ -0,0 +1,441 @@ +# -*- coding: utf-8 -*- + +## TODO: Implement TODO_IGNORE setting (http://mdeering.com/posts/004-get-your-textmate-todos-and-fixmes-under-control) +## TODO: Make the output clickable (å la find results) +## TODO: Occasional NoneType bug +## todo: Make the sections foldable (define them as regions?) + +"""""" + +from collections import namedtuple +from datetime import datetime +import functools +import fnmatch +from itertools import groupby +import logging +from os import path, walk +import re +import threading + +import sublime +import sublime_plugin + + +DEBUG = True + +DEFAULT_SETTINGS = { + 'result_title': 'TODO Results', + + 'core_patterns': { + 'TODO': r'TODO[\s]*?:+(?P<todo>.*)$', + 'NOTE': r'NOTE[\s]*?:+(?P<note>.*)$', + 'FIXME': r'FIX ?ME[\s]*?:+(?P<fixme>.*)$', + 'CHANGED': r'CHANGED[\s]*?:+(?P<changed>.*)$' + }, + + 'patterns': {} +} + +Message = namedtuple('Message', 'type, msg') + +## LOGGING SETUP +try: + from logging import NullHandler +except ImportError: + class NullHandler(logging.Handler): + def handle(self, record): + pass + + def emit(self, record): + pass + + def createLock(self): + self.lock = None + +log = logging.getLogger('SublimeTODO') +log.handlers = [] ## hack to prevent extraneous handlers on ST2 auto-reload +log.addHandler(NullHandler()) +log.setLevel(logging.INFO) +if DEBUG: + log.addHandler(logging.StreamHandler()) + log.setLevel(logging.DEBUG) + + +def do_when(conditional, callback, *args, **kwargs): + if conditional(): + return callback(*args, **kwargs) + sublime.set_timeout(functools.partial(do_when, conditional, callback, *args, **kwargs), 50) + + +class Settings(dict): + """Combine default and user settings""" + def __init__(self, user_settings): + settings = DEFAULT_SETTINGS.copy() + settings.update(user_settings) + ## Combine core_patterns and patterns + settings['core_patterns'].update(settings['patterns']) + settings['patterns'] = settings.pop('core_patterns') + super(Settings, self).__init__(settings) + + +class ThreadProgress(object): + def __init__(self, thread, message, success_message, file_counter): + self.thread = thread + self.message = message + self.success_message = success_message + self.file_counter = file_counter + self.addend = 1 + self.size = 8 + sublime.set_timeout(lambda: self.run(0), 100) + + def run(self, i): + if not self.thread.is_alive(): + if hasattr(self.thread, 'result') and not self.thread.result: + sublime.status_message('') + return + sublime.status_message(self.success_message) + return + + before = i % self.size + after = (self.size - 1) - before + sublime.status_message('%s [%s=%s] (%s files scanned)' % \ + (self.message, ' ' * before, ' ' * after, self.file_counter)) + if not after: + self.addend = -1 + if not before: + self.addend = 1 + i += self.addend + sublime.set_timeout(lambda: self.run(i), 100) + + + +class TodoExtractor(object): + def __init__(self, settings, filepaths, dirpaths, ignored_dirs, ignored_file_patterns, + file_counter): + self.filepaths = filepaths + self.dirpaths = dirpaths + self.patterns = settings['patterns'] + self.settings = settings + self.file_counter = file_counter + self.ignored_dirs = ignored_dirs + self.ignored_files = ignored_file_patterns + self.log = logging.getLogger('SublimeTODO.extractor') + + def iter_files(self): + """""" + seen_paths_ = [] + files = self.filepaths + dirs = self.dirpaths + exclude_dirs = self.ignored_dirs + + for filepath in files: + pth = path.realpath(path.abspath(filepath)) + if pth not in seen_paths_: + seen_paths_.append(pth) + yield pth + + for dirpath in dirs: + dirpath = path.abspath(dirpath) + for dirpath, dirnames, filenames in walk(dirpath): + ## remove excluded dirs + ## TODO: These are not patterns. Consider making them glob patterns + for dir in exclude_dirs: + if dir in dirnames: + self.log.debug(u'Ignoring dir: {0}'.format(dir)) + dirnames.remove(dir) + + for filepath in filenames: + pth = path.join(dirpath, filepath) + pth = path.realpath(path.abspath(pth)) + if pth not in seen_paths_: + seen_paths_.append(pth) + yield pth + + def filter_files(self, files): + """""" + exclude_patterns = [re.compile(patt) for patt in self.ignored_files] + for filepath in files: + if any(patt.match(filepath) for patt in exclude_patterns): + continue + yield filepath + + def search_targets(self): + """Yield filtered filepaths for message extraction""" + return self.filter_files(self.iter_files()) + + def extract(self): + """""" + message_patterns = '|'.join(self.patterns.values()) + case_sensitivity = 0 if self.settings.get('case_sensitive', False) else re.IGNORECASE + patt = re.compile(message_patterns, case_sensitivity) + for filepath in self.search_targets(): + try: + f = open(filepath) + self.log.debug(u'Scanning {0}'.format(filepath)) + for linenum, line in enumerate(f): + for mo in patt.finditer(line): + ## Remove the non-matched groups + matches = [Message(msg_type, msg) for msg_type, msg in mo.groupdict().iteritems() if msg] + for match in matches: + yield {'filepath': filepath, 'linenum': linenum + 1, 'match': match} + except IOError: + ## Probably a broken symlink + f = None + finally: + self.file_counter.increment() + if f is not None: + f.close() + + +class TodoRenderer(object): + def __init__(self, settings, window, file_counter): + self.window = window + self.settings = settings + self.file_counter = file_counter + + @property + def view_name(self): + """The name of the new results view. Defined in settings.""" + return self.settings['result_title'] + + @property + def header(self): + hr = u'+ {0} +'.format('-' * 76) + return u'{hr}\n| TODOS @ {0:<68} |\n| {1:<76} |\n{hr}\n'.format( + datetime.now().strftime('%A %d %B %Y %H:%M').decode("utf-8"), + u'{0} files scanned'.format(self.file_counter), + hr=hr) + + @property + def view(self): + existing_results = [v for v in self.window.views() + if v.name() == self.view_name and v.is_scratch()] + if existing_results: + v = existing_results[0] + else: + v = self.window.new_file() + v.set_name(self.view_name) + v.set_scratch(True) + v.settings().set('todo_results', True) + return v + + def format(self, messages): + """Yield lines for rendering into results view. Includes headers and + blank lines. + Lines are returned in the form (type, content, [data]) where type is either + 'header', 'whitespace' or 'result' + """ + key_func = lambda m: m['match'].type + messages = sorted(messages, key=key_func) + + for message_type, matches in groupby(messages, key=key_func): + matches = list(matches) + if matches: + yield ('header', u'\n## {0} ({1})'.format(message_type.upper().decode('utf8', 'ignore'), len(matches)), {}) + for idx, m in enumerate(matches, 1): + msg = m['match'].msg.decode('utf8', 'ignore') ## Don't know the file encoding + filepath = path.basename(m['filepath']) + line = u"{idx}. {filepath}:{linenum} {msg}".format( + idx=idx, filepath=filepath, linenum=m['linenum'], msg=msg) + yield ('result', line, m) + + def render_to_view(self, formatted_results): + """This blocks the main thread, so make it quick""" + ## Header + result_view = self.view + edit = result_view.begin_edit() + result_view.erase(edit, sublime.Region(0, result_view.size())) + result_view.insert(edit, result_view.size(), self.header) + result_view.end_edit(edit) + + ## Region : match_dicts + regions = {} + + ## Result sections + for linetype, line, data in formatted_results: + edit = result_view.begin_edit() + insert_point = result_view.size() + result_view.insert(edit, insert_point, line) + if linetype == 'result': + rgn = sublime.Region(insert_point, result_view.size()) + regions[rgn] = data + result_view.insert(edit, result_view.size(), u'\n') + result_view.end_edit(edit) + + result_view.add_regions('results', regions.keys(), '') + + ## Store {Region : data} map in settings + ## TODO: Abstract this out to a storage class Storage.get(region) ==> data dict + ## Region() cannot be stored in settings, so convert to a primitive type + # d_ = regions + d_ = dict(('{0},{1}'.format(k.a, k.b), v) for k, v in regions.iteritems()) + result_view.settings().set('result_regions', d_) + + ## Set syntax and settings + result_view.set_syntax_file('Packages/SublimeTODO/todo_results.hidden-tmLanguage') + result_view.settings().set('line_padding_bottom', 2) + result_view.settings().set('line_padding_top', 2) + result_view.settings().set('word_wrap', False) + result_view.settings().set('command_mode', True) + self.window.focus_view(result_view) + + +class WorkerThread(threading.Thread): + def __init__(self, extractor, renderer): + self.extractor = extractor + self.renderer = renderer + threading.Thread.__init__(self) + + def run(self): + ## Extract in this thread + todos = self.extractor.extract() + rendered = list(self.renderer.format(todos)) + + ## Render into new window in main thread + def render(): + self.renderer.render_to_view(rendered) + sublime.set_timeout(render, 10) + + +class FileScanCounter(object): + """Thread-safe counter used to update the status bar""" + def __init__(self): + self.ct = 0 + self.lock = threading.RLock() + self.log = logging.getLogger('SublimeTODO') + + def __call__(self, filepath): + self.log.debug(u'Scanning %s' % filepath) + self.increment() + + def __str__(self): + with self.lock: + return '%d' % self.ct + + def increment(self): + with self.lock: + self.ct += 1 + + def reset(self): + with self.lock: + self.ct = 0 + + +class TodoCommand(sublime_plugin.TextCommand): + + def search_paths(self, window, open_files_only=False): + """Return (filepaths, dirpaths)""" + return ( + [view.file_name() for view in window.views() if view.file_name()], + window.folders() if not open_files_only else [] + ) + + def run(self, edit, open_files_only=False): + window = self.view.window() + settings = Settings(self.view.settings().get('todo', {})) + + + ## TODO: Cleanup this init code. Maybe move it to the settings object + filepaths, dirpaths = self.search_paths(window, open_files_only=open_files_only) + + ignored_dirs = settings.get('folder_exclude_patterns', []) + ## Get exclude patterns from global settings + ## Is there really no better way to access global settings? + global_settings = sublime.load_settings('Global.sublime-settings') + ignored_dirs.extend(global_settings.get('folder_exclude_patterns', [])) + + exclude_file_patterns = settings.get('file_exclude_patterns', []) + exclude_file_patterns.extend(global_settings.get('file_exclude_patterns', [])) + exclude_file_patterns.extend(global_settings.get('binary_file_patterns', [])) + exclude_file_patterns = [fnmatch.translate(patt) for patt in exclude_file_patterns] + + file_counter = FileScanCounter() + extractor = TodoExtractor(settings, filepaths, dirpaths, ignored_dirs, + exclude_file_patterns, file_counter) + renderer = TodoRenderer(settings, window, file_counter) + + worker_thread = WorkerThread(extractor, renderer) + worker_thread.start() + ThreadProgress(worker_thread, 'Finding TODOs', '', file_counter) + + +class NavigateResults(sublime_plugin.TextCommand): + DIRECTION = {'forward': 1, 'backward': -1} + STARTING_POINT = {'forward': -1, 'backward': 0} + + def __init__(self, view): + super(NavigateResults, self).__init__(view) + + def run(self, edit, direction): + view = self.view + settings = view.settings() + results = self.view.get_regions('results') + if not results: + sublime.status_message('No results to navigate') + return + + ##NOTE: numbers stored in settings are coerced to floats or longs + selection = int(settings.get('selected_result', self.STARTING_POINT[direction])) + selection = selection + self.DIRECTION[direction] + try: + target = results[selection] + except IndexError: + target = results[0] + selection = 0 + + settings.set('selected_result', selection) + ## Create a new region for highlighting + target = target.cover(target) + view.add_regions('selection', [target], 'selected', 'dot') + view.show(target) + + +class ClearSelection(sublime_plugin.TextCommand): + def run(self, edit): + self.view.erase_regions('selection') + self.view.settings().erase('selected_result') + + +class GotoComment(sublime_plugin.TextCommand): + def __init__(self, *args): + self.log = logging.getLogger('SublimeTODO.nav') + super(GotoComment, self).__init__(*args) + + def run(self, edit): + ## Get the idx of selected result region + selection = int(self.view.settings().get('selected_result', -1)) + ## Get the region + selected_region = self.view.get_regions('results')[selection] + ## Convert region to key used in result_regions (this is tedious, but + ## there is no other way to store regions with associated data) + data = self.view.settings().get('result_regions')['{0},{1}'.format(selected_region.a, selected_region.b)] + self.log.debug(u'Goto comment at {filepath}:{linenum}'.format(**data)) + new_view = self.view.window().open_file(data['filepath']) + do_when(lambda: not new_view.is_loading(), lambda: new_view.run_command("goto_line", {"line": data['linenum']})) + + +class MouseGotoComment(sublime_plugin.TextCommand): + def __init__(self, *args): + self.log = logging.getLogger('SublimeTODO.nav') + super(MouseGotoComment, self).__init__(*args) + + def highlight(self, region): + target = region.cover(region) + self.view.add_regions('selection', [target], 'selected', 'dot') + self.view.show(target) + + def get_result_region(self, pos): + line = self.view.line(pos) + return line + + def run(self, edit): + if not self.view.settings().get('result_regions'): + return + ## get selected line + pos = self.view.sel()[0].end() + result = self.get_result_region(pos) + self.highlight(result) + data = self.view.settings().get('result_regions')['{0},{1}'.format(result.a, result.b)] + self.log.debug(u'Goto comment at {filepath}:{linenum}'.format(**data)) + new_view = self.view.window().open_file(data['filepath']) + do_when(lambda: not new_view.is_loading(), lambda: new_view.run_command("goto_line", {"line": data['linenum']})) diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/todo_results.hidden-tmLanguage b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/todo_results.hidden-tmLanguage new file mode 100644 index 0000000..e0cd6b4 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/SublimeTODO/todo_results.hidden-tmLanguage @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>name</key> + <string>TODO Results</string> + + <key>patterns</key> + <array> + <dict> + <key>match</key> + <!-- <string>^[\s]{0,2}([\d]+\.) ([^\(]+) \(([\d]+)\)(:) (.*)$</string> --> + <string>^[\s]{0,2}([\d]+\.) ([^:]+)(:)([\d]+) (.*)$</string> + <key>captures</key> + <dict> + <key>1</key> + <dict> + <key>name</key> + <string>constant.numeric.item-number.todo-list</string> + </dict> + <key>2</key> + <dict> + <key>name</key> + <string>entity.name.filename.todo-list</string> + </dict> + <key>3</key> + <dict> + <key>name</key> + <string>punctuation.definition.delimiter</string> + </dict> + <key>4</key> + <dict> + <key>name</key> + <string>constant.numeric.line-number.todo-list</string> + </dict> + <key>5</key> + <dict> + <key>name</key> + <string>entity.name.match.todo-list</string> + </dict> + </dict> + </dict> + </array> + <key>scopeName</key> + <string>text.todo-list</string> + <key>uuid</key> + <string>2ce8c28e-9c29-4f7f-bc65-7c5311732d29</string> +</dict> +</plist> diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/.gitignore b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/.gitignore new file mode 100644 index 0000000..3aa864b --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/.gitignore @@ -0,0 +1 @@ +TODO.md diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/Default.sublime-commands b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/Default.sublime-commands new file mode 100644 index 0000000..dd4df34 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/Default.sublime-commands @@ -0,0 +1,10 @@ +[ + { + "caption": "Trailing Spaces: Toggle Trailing Spaces Highlighting", + "command": "toggle_trailing_spaces" + }, + { + "caption": "Trailing Spaces: Delete Trailing Spaces", + "command": "delete_trailing_spaces" + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/MIT-LICENSE b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/MIT-LICENSE new file mode 100644 index 0000000..7104d54 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/MIT-LICENSE @@ -0,0 +1,20 @@ +Copyright 2010 Jean-Denis Vauguet + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/Main.sublime-menu b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/Main.sublime-menu new file mode 100644 index 0000000..3d966e3 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/Main.sublime-menu @@ -0,0 +1,123 @@ +[ + { + "id": "edit", + "children": + [ + { + "caption": "Trailing Spaces", + "id": "trailing-spaces", + "children": + [ + { + "command": "delete_trailing_spaces", + "caption": "Delete" + }, + { "caption": "-" }, + { + "command": "toggle_trailing_spaces_modified_lines_only", + "caption": "Modified Lines Only", + "checkbox": true + }, + { + "command": "toggle_trailing_spaces", + "caption": "Highlight Regions", + "checkbox": true + } + ] + } + ] + }, + { + "id": "preferences", + "children": + [ + { + "caption": "Package Settings", + "mnemonic": "P", + "id": "package-settings", + "children": + [ + { + "caption": "Trailing Spaces", + "children": + [ + { + "command": "open_file", + "args": { + "file": "${packages}/TrailingSpaces/README.md", + "platform": "Windows" + }, + "caption": "Help" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/TrailingSpaces/README.md", + "platform": "OSX" + }, + "caption": "Help" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/TrailingSpaces/README.md", + "platform": "Linux" + }, + "caption": "Help" + }, + { "caption": "-" }, + { + "command": "open_file", + "args": { + "file": "${packages}/TrailingSpaces/trailing_spaces.sublime-settings", + "platform": "Windows" + }, + "caption": "Settings - Default" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/TrailingSpaces/trailing_spaces.sublime-settings", + "platform": "OSX" + }, + "caption": "Settings - Default" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/TrailingSpaces/trailing_spaces.sublime-settings", + "platform": "Linux" + }, + "caption": "Settings - Default" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/User/trailing_spaces.sublime-settings", + "platform": "Windows" + }, + "caption": "Settings - User" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/User/trailing_spaces.sublime-settings", + "platform": "OSX" + }, + "caption": "Settings - User" + }, + { + "command": "open_file", + "args": { + "file": "${packages}/User/trailing_spaces.sublime-settings", + "platform": "Linux" + }, + "caption": "Settings - User" + } + ] + } + ] + } + ] + } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/README.md b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/README.md new file mode 100644 index 0000000..0627b55 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/README.md @@ -0,0 +1,313 @@ +Trailing Spaces +=============== + +A [Sublime Text 2](http://www.sublimetext.com/2) and +[3](http://www.sublimetext.com/3) plugin that allows you to… + +**highlight trailing spaces and delete them in a flash!** + +Synopsis +-------- + +Sublime Text provides a way to automate deletion of trailing spaces *upon file +saving* (more on this at the end of this file). Depending on your settings, it +may be more handy to just highlight them and/or delete them by hand, at any +time. This plugin provides just that, and a *lot* of options to fine-tune the +way you want to decimate trailing spaces. + +Installation +------------ + +It is available through +[Sublime Package Contol](http://wbond.net/sublime_packages/package_control) and +this is the recommended way of installation (brings configuration instructions, +automatic updates with changelogs…). + +### Alternative installation methods + +#### From github + +You can install from github if you want, although Package Control automates +just that. Go to your `Packages` subdirectory under ST2's data directory: + +* Windows: `%APPDATA%\Sublime Text 2` +* OS X: `~/Library/Application Support/Sublime Text 2` +* Linux: `~/.config/sublime-text-2` +* Portable Installation: `Sublime Text 2/Data` + +Then clone this repository: + + git clone git://github.com/SublimeText/TrailingSpaces.git + +#### Manually + +[Download](https://github.com/SublimeText/TrailingSpaces/archive/master.zip) +the plugin as a zip. Copy the *Trailing Spaces* directory to its location +(see prior section). + +Usage +----- + +### Deletion + +The main feature you gain from using this plugin is that of deleting all +trailing spaces in the currently edited document. In order to use this +deletion feature, you may either: + +* click on "Edit / Trailing Spaces / Delete"; +* bind the deletion command to a keyboard shortcut: + +To add a key binding, open "Preferences / Key Bindings - User" and add: + +``` js +{ "keys": ["ctrl+shift+t"], "command": "delete_trailing_spaces" } +``` + +With this setting, pressing <kbd>Ctrl + Shift + t</kbd> will delete all +trailing spaces at once in the current file! For OSX users, quoting wbond: +"When porting a key binding across OSes, it is common for the ctrl key on +Windows and Linux to be swapped out for super on OS X" +(eg. use "super+ctrl+t" instead). + +*Beware*: the binding from this example overrides the default ST's mapping +for reopening last closed file. You can look at the default bindings in +"Preferences / Key Bindings - Default". + +### Toggling highlighting + +At any time, you can toggle highlighting on and off. You may either: + +- click on "Edit / Trailing Spaces / Highlight Regions" +- bind the toggling command to a keyboard shortcut: + +``` js +// I like "d", as in "detect" (overrides a default binding, though). +{ "keys": ["ctrl+shift+d"], "command": "toggle_trailing_spaces" } +``` + +Options +------- + +Several options are available to customize the plugin's behavior. Those +settings are stored in a configuration file, as JSON. You must use a specific +file: Go to "Preferences / Package Settings / Trailing Spaces / Settings +- User" to add you custom settings. You can look at the default values in +"Settings - Default", in the same menu. + +A few of them are also accessible through the "Edit / Trailing Spaces" menu. +Sometimes, editing a setting will require a fresh Sublime Text to be applied +properly, so try relaunching ST before reporting an issue ;) + +All settings are global (ie. applied to all opened documents). + +### Changing the highlighting color + +*Default: "invalid"* + +You may change the highlighting color, providing a color scope name such as + "error", "comment"… just like that: + +``` js +{ "trailing_spaces_highlight_color": "comment" } +``` + +The scope should be defined in your current theme file. Here is a dummy, +fully-fledged example (feel free to cut irrelevant pieces for your settings) +of such a custom color scope: + +``` xml +<dict> + <key>name</key> + <string>Invalid - Illegal</string> + <key>scope</key> + <string>invalid.illegal</string> + <key>settings</key> + <dict> + <key>background</key> + <string>#F93232</string> + <key>fontStyle</key> + <string></string> + <key>foreground</key> + <string>#F9F2CE</string> + </dict> +</dict> +``` + +You would then use the value of "invalid.illegal". + +### Keeping trailing spaces invisible + +You can make trailing spaces "invisible" yet still rely on the deletion +command. To do that, set the highlight scope to an empty string: + +``` js +{ "trailing_spaces_highlight_color": "" } +``` + +Beware: this is **not** the same as *disabling* the highlighting (see "On- +Demand Matching" below). With this setting, the plugin still runs when opening +a file, and in the background afterwards; you just won't see the trailing +spaces (they are being highlighted with a "transparent" color). + +### Include Current Line + +*Default: true* + +Highlighting of trailing spaces in the currently edited line can be annoying: +each time you are about to start a new word, the space you type is matched as +a trailing spaces. Currently edited line can thus be ignored: + +``` js +{ "trailing_spaces_include_current_line": false } +``` + +Even though the trailing spaces are not highlighted on this line, they are +still internally matched and will be delete when firing the deletion command. + +### Include Empty Lines + +*Default: true* + +When firing the deletion command, empty lines are matched as trailing regions, +and end up being deleted. You can specifically ignore them: + +``` js +{ "trailing_spaces_include_empty_lines": false } +``` + +They will not be highlighted either. + +### Modified Lines Only + +*Default: false (reopen ST to update)* + +When firing the deletion command, trailing regions *in the entire document* are +deleted. There are some use-cases when deleting trailing spaces *only on lines +you edited* is smarter; for instance when commiting changes to some third-party +source code. + +At any time, you can change which area is covered when deleting trailing +regions. You may either: + +- click on "Edit / Trailing Spaces / Modified Lines Only" +- specify as a setting: + +``` js +{ "trailing_spaces_modified_lines_only": true } +``` + +There is also a command to toggle this feature on and off. You may thus define +a key binding: + +``` js +{ "keys": ["pick+a+shortcut"], "command": "toggle_trailing_spaces_modified_lines_only" } +``` + +### Trim On Save + +*Default: false* + +Setting this to `true` will ensure trailing spaces are deleted when you save +your document. It abides by the other settings, such as *Modified Lines Only*. + +``` js +{ "trailing_spaces_trim_on_save": true } +``` + +### Save After Trim + +*Default: false* + +You may not want to always trim trailing spaces on save, but the other way +around could prove useful. Setting this to `true` will automatically save your +document after you fire the deletion command: + +``` js +{ "trailing_spaces_save_after_trim": true } +``` + +It is obviously ignored if *Trim On Save* is on. + +### Live Matching vs On-demand Matching + +*Default: true (reopen ST to update)* + +By default, trailing regions are matched every time you edit the document, and +when you open it. + +This feature is entirely optional and you may set it off: firing the deletion +command will cause the trailing spaces to be deleted as expected even though +they were not matched prior to your request. If you are afraid of the plugin +to cause slowness (for instance, you already installed several *heavy* +plugins), you can disable live matching: + +``` js +{ "trailing_spaces_enabled": false } +``` + +In this case, for no trailing regions are matched until you request them to be +deleted, no highlighting occurs—it is in fact disabled, regardless of your +"scope" setting. If you want to check the trailing spaces regions, you can +toggle highlighting on and off. In this case, it may come in handy to define +a binding for the toggling command. When "On-demand Matching" is on and some +trailing spaces are highlighted, added ones will obviously not be. Toggling +highlight off and on will refresh them. + +### For power-users only! + +#### Disabled for large files + +The plugin is disabled altogether for large files, for it may cause slowness. +The default threshold is around 1 million of characters. This is +configurable (in "File Settings - User") and the unit is number of chars: + +``` js +{ "trailing_spaces_file_max_size": 1000} +``` + +#### The matching pattern + +*Default: [ \t]+* + +Trailing spaces are line-ending regions containing at least one simple space, +tabs, or both. This pattern should be all you ever need, but if you *do* want +to abide by another definition to cover edge-cases, go ahead: + +``` js +// *danger* will match newline chars and many other folks +"trailing_spaces_regexp": "[\\s]+" +``` + +About Sublime Text's built-in features +-------------------------------------- + +Trailing Spaces is designed to be a drop-in replacement of the limited +*Trim Whitespace On Save* built-in feature. ST is indeed able to delete +trailing spaces upon saving files, and maybe that's all you need! + +In order to enable this behavior, edit "Preferences / Settings - User" +to add the following: + +``` js +{ "trim_trailing_white_space_on_save": true } +``` + +As Trailing Spaces bypasses this setting, you will have to uninstall it to +benefit from this setting. + +Made a little less obvious in the documentation are settings to showcase +whitespaces (*not only trailing ones!*): + +``` js +{ "draw_white_space": "all" } +``` + +and to ensure a newline is kept at end of file upon saving: + +``` js +{ "ensure_newline_at_eof_on_save": true } +``` + +The former will display *all* whitespaces in your files. There is another value +of "selection" which display whitespaces under (you got it) your current text +selection. diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/messages.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/messages.json new file mode 100644 index 0000000..efaff86 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/messages.json @@ -0,0 +1,4 @@ +{ + "install": "messages/install.txt", + "v1.0.0": "messages/v1.0.0.txt" +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/messages/install.txt b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/messages/install.txt new file mode 100644 index 0000000..141f30c --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/messages/install.txt @@ -0,0 +1,40 @@ + + + Thank you for installing Trailing Spaces + ---------------------------------------- + + You're now ready to give trailing spaces *a hard time*! + + Wait… I guess Package Control just introduced some of them :( + Why don't you try clicking "Edit / Trailing Spaces / Delete"? + + + +Documentation +============= + +Although the usage of this plugin is dead simple, it comes with several options. All +details are available in the documentation, and you can read it by clicking on +"Preferences / Package Settings / Trailing Spaces / Help", or in a prettier form, by +browsing https://github.com/SublimeText/TrailingSpaces. + +Key Binding +=========== + +This plugin does not come with a default key binding for the deletion command. You can +pick your own key binding and define it in "Preferences / Key Bindings - User", or just +stick to using the menu entry under "Edit". Check the help for advice on this. + +Upgrades & Issues +================= + +Package Control will automatically update all packages every time the editor is started, +so there is nothing for you to worry about. If you however do find the plugin not to work +as it used to, head to the issues tracker (see links below) to report the problem. + +Useful Links +============ + +* Documentation & Code: https://github.com/SublimeText/TrailingSpaces +* Report issues / Request New Features / Roadmap: https://github.com/SublimeText/TrailingSpaces/issues +* Follow me on twitter: @jdvauguet diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/messages/v1.0.0.txt b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/messages/v1.0.0.txt new file mode 100644 index 0000000..adb1f3b --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/messages/v1.0.0.txt @@ -0,0 +1,65 @@ + + + Trailing Spaces update [v1.0.0] + ------------------------------- + + Hope you've been happy gaving trailin' a hard time so far. + + I added several features to help you in this honorable quest… + + All details accessible through: + + "Preferences / Package Settings / Trailing Spaces / Help" + + + +New feature: Modified Lines Only +================================ + +As proposed by a fellow user, it is now possible to target only the lines +modified by You and You Only when deleting trailing spaces. + +This feature will certainly please coders who edit third-party code filled +with trailing spaces but do not want to commit giant diffs, just their little +fix, while keeping it clean. + +New feature: Trim On Save +========================= + +This option allows for automatic deletion upon saving. No more lost trailing +spaces! A perfect combo to the "Modified Lines Only" setting I guess. + +New feature: Save After Trim +============================ + +A different kind of automation: many users just want those trailings out and +forget 'bout them. It is now made even easier with this auto-saving hook. Fire +the deletion command, and your document is clean on the hard drive! + +At the current time, "Trim On Save" and "Save After Trim" cannot be both +enabled (the former wins), but this is on the roadmap. + +New Menu +======== + +Some of the settings seemed a bit more important than the others. Along the +deletion command, the toggling command/state and the "Modified Lines Only" +setting have been elected first-class citizens of the new "Edit / Trailing +Spaces" menu. Any change made by click here is live, persistent and reflected +in the JSON settings file. Settings are global to all open documents. + +Improvements & Misc. +==================== + +- Support for custom matching patterns (danger!). +- Performance improvements (reduced overhead, with some room for further + improvements). +- Better documentation (both code & user doc). +- Lazy "On-demand" matching improved. + +Useful Links +============ + +* Documentation & Code: https://github.com/SublimeText/TrailingSpaces +* Report issues / Request New Features / Roadmap: https://github.com/SublimeText/TrailingSpaces/issues +* Follow me on twitter: @jdvauguet diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/package-metadata.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/package-metadata.json new file mode 100644 index 0000000..52d06dd --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/package-metadata.json @@ -0,0 +1 @@ +{"url": "https://github.com/SublimeText/TrailingSpaces", "version": "2013.03.07.20.09.52", "description": "Highlight trailing spaces and delete them in a flash."} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/packages.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/packages.json new file mode 100644 index 0000000..f7bb205 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/packages.json @@ -0,0 +1,20 @@ +{ + "schema_version": "1.2", + "packages": [ + { + "name": "TrailingSpaces", + "description": "Highlight trailing spaces and delete them in a flash.", + "author": "Jean-Denis Vauguet", + "homepage": "https://github.com/SublimeText/TrailingSpaces/", + "last_modified": "2013-03-08 01:00:00", + "platforms": { + "*": [ + { + "version": "1.0.0", + "url": "https://nodeload.github.com/SublimeText/TrailingSpaces/zip/v1.0.0" + } + ] + } + } + ] +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/trailing_spaces.py b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/trailing_spaces.py new file mode 100644 index 0000000..57a8902 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/trailing_spaces.py @@ -0,0 +1,453 @@ +''' +Provides both a trailing spaces highlighter and a deletion command. + +See README.md for details. + +@author: Jean-Denis Vauguet <jd@vauguet.fr>, Oktay Acikalin <ok@ryotic.de> +@license: MIT (http://www.opensource.org/licenses/mit-license.php) +@since: 2011-02-25 +''' + +import sublime +import sublime_plugin +import difflib +import codecs + +DEFAULT_MAX_FILE_SIZE = 1048576 +DEFAULT_IS_ENABLED = True +DEFAULT_MODIFIED_LINES_ONLY = False + +# Global settings object and flags. +# Flags duplicate some of the (core) JSON settings, in case the settings file has +# been corrupted or is empty (ST2 really dislikes that!) +ts_settings_filename = "trailing_spaces.sublime-settings" +ts_settings = None +trailing_spaces_live_matching = DEFAULT_IS_ENABLED +trim_modified_lines_only = DEFAULT_MODIFIED_LINES_ONLY +startup_queue = [] +on_disk = None + + +# Private: Loads settings and sets whether the plugin (live matching) is enabled. +# +# Returns nothing. +def plugin_loaded(): + global ts_settings_filename, ts_settings, trailing_spaces_live_matching + global current_highlighting_scope, trim_modified_lines_only, startup_queue + global DEFAULT_COLOR_SCOPE_NAME, on_disk + + ts_settings = sublime.load_settings(ts_settings_filename) + trailing_spaces_live_matching = bool(ts_settings.get("trailing_spaces_enabled", + DEFAULT_IS_ENABLED)) + current_highlighting_scope = ts_settings.get("trailing_spaces_highlight_color", + "invalid") + DEFAULT_COLOR_SCOPE_NAME = current_highlighting_scope + trim_modified_lines_only = bool(ts_settings.get("trailing_spaces_modified_lines_only", + DEFAULT_MODIFIED_LINES_ONLY)) + + if trailing_spaces_live_matching: + for view in startup_queue: + match_trailing_spaces(view) + else: + current_highlighting_scope = "" + if ts_settings.get("trailing_spaces_highlight_color") != current_highlighting_scope: + persist_settings() + + +# Private: Updates user's settings with in-memory values. +# +# Allows for persistent settings from the menu. +# +# Returns nothing. +def persist_settings(): + sublime.save_settings(ts_settings_filename) + + +# Private: Determine if the view is a "Find results" view. +# +# view - the view, you know +# +# Returns True or False. +def is_find_results(view): + return view.settings().get('syntax') and "Find Results" in view.settings().get('syntax') + + +# Private: Get the regions matching trailing spaces. +# +# As the core regexp matches lines, the regions are, well, "per lines". +# +# view - the view, you know +# +# Returns both the list of regions which map to trailing spaces and the list of +# regions which are to be highlighted, as a list [matched, highlightable]. +def find_trailing_spaces(view): + sel = view.sel()[0] + line = view.line(sel.b) + include_empty_lines = bool(ts_settings.get("trailing_spaces_include_empty_lines", + DEFAULT_IS_ENABLED)) + include_current_line = bool(ts_settings.get("trailing_spaces_include_current_line", + DEFAULT_IS_ENABLED)) + regexp = ts_settings.get("trailing_spaces_regexp") + "$" + no_empty_lines_regexp = "(?<=\S)%s$" % regexp + + offending_lines = view.find_all(regexp if include_empty_lines else no_empty_lines_regexp) + + if include_current_line: + return [offending_lines, offending_lines] + else: + current_offender = view.find(regexp if include_empty_lines else no_empty_lines_regexp, line.a) + removal = False if current_offender == None else line.intersects(current_offender) + highlightable = [i for i in offending_lines if i != current_offender] if removal else offending_lines + return [offending_lines, highlightable] + + +# Private: Find the fraking trailing spaces in the view and flags them as such! +# +# It will refresh highlighted regions as well. Does not execute if the +# document's size exceeds the file_max_size setting, or if the fired in a view +# which is not a legacy document (helper/build views and so on). +# +# view - the view, you know +# +# Returns nothing. +def match_trailing_spaces(view): + if ts_settings is None: + startup_queue.append(view) + return + + # Silently pass if file is too big. + if max_size_exceeded(view): + return + + if not is_find_results(view): + (matched, highlightable) = find_trailing_spaces(view) + add_trailing_spaces_regions(view, matched) + highlight_trailing_spaces_regions(view, highlightable) + + +# Private: Checks whether the document is bigger than the max_size setting. +# +# view - the view, you know +# +# Returns True or False. +def max_size_exceeded(view): + return view.size() > ts_settings.get('trailing_spaces_file_max_size', + DEFAULT_MAX_FILE_SIZE) + + +# Private: Marks specified regions as trailing spaces. +# +# view - the view, you know +# regions - regions qualified as trailing spaces +# +# Returns nothing. +def add_trailing_spaces_regions(view, regions): + view.erase_regions('TrailingSpacesMatchedRegions') + view.add_regions('TrailingSpacesMatchedRegions', + regions, + "", + "", + sublime.HIDE_ON_MINIMAP) + + +# Private: Highlights specified regions as trailing spaces. +# +# It will use the scope enforced by the state of the toggable highlighting. +# +# view - the view, you know +# regions - regions qualified as trailing spaces +# +# Returns nothing. +def highlight_trailing_spaces_regions(view, regions): + view.erase_regions("TrailingSpacesHighlightedRegions") + view.add_regions('TrailingSpacesHighlightedRegions', + regions, + current_highlighting_scope or "", + "", + sublime.HIDE_ON_MINIMAP) + + +# Private: Toggles highlighting of all trailing spaces in the view. +# +# It has no effect is the plugin is disabled. +# +# view - the view, you know +# +# Returns True (highlighting was turned on) or False (turned off). +def toggle_highlighting(view): + global current_highlighting_scope + + # If the scope is that of an invisible, there is nothing to toggle. + if DEFAULT_COLOR_SCOPE_NAME == "": + return "disabled!" + + # If performing live, highlighted trailing regions must be updated + # internally. + if not trailing_spaces_live_matching: + (matched, highlightable) = find_trailing_spaces(view) + highlight_trailing_spaces_regions(view, highlightable) + + scope = DEFAULT_COLOR_SCOPE_NAME if current_highlighting_scope == "" else "" + current_highlighting_scope = scope + highlight_trailing_spaces_regions(view, view.get_regions('TrailingSpacesHighlightedRegions')) + return "off" if current_highlighting_scope == "" else "on" + + +# Clear all the highlighted regions in all views. +# +# FIXME: this is not used! Delete? +# +# window - the window, you know +# +# Returns nothing. +def clear_trailing_spaces_highlight(window): + for view in window.views(): + view.erase_regions('TrailingSpacesMatchedRegions') + + +# Find edited lines since last save, as line numbers, based on diff. +# +# It uses a Differ object to compute the diff between the file as red on the +# disk, and the current buffer (which may differ from the disk's state). See +# http://docs.python.org/2/library/difflib.html for details about diff codes. +# +# It relies on a full diff, so it may be expensive computation for very large +# files (diff generation + looping through all lines). +# +# old - a buffer of lines, as in "old version" +# new - a buffer of lines, as in "new version" +# +# Returns the list of edited line numbers. +def modified_lines_as_numbers(old, new): + d = difflib.Differ() + diffs = d.compare(old, new) + + # Pretty Naive Algorithm (tm): + # - split off the "Differ code", to check whether: + # - the line is in either in both files or just b: increment the line number + # - the line is only in b: it qualifies as an edited line! + # Starting from -1 as ST2 is internally 0-based for lines. + lineNum = -1 + edited_lines = [] + for line in diffs: + code = line[:2] + # those lines with "? " are not real! watch out! + if code in (" ", "+ "): + lineNum += 1 + if code == "+ ": + edited_lines.append(lineNum) + + return False if not edited_lines else edited_lines + + +# Private: Find the dirty lines. +# +# view - the view, you know +# +# Returns the list of regions matching dirty lines. +def get_modified_lines(view): + try: + on_disk + on_buffer = view.substr(sublime.Region(0, view.size())).splitlines() + except UnicodeDecodeError: + sublime.status_message("File format incompatible with this feature (UTF-8 files only)") + return + + lines = [] + line_numbers = modified_lines_as_numbers(on_disk, on_buffer) + if line_numbers: + lines = [view.full_line(view.text_point(number,0)) for number in line_numbers] + return lines + + +# Private: Finds the trailing spaces regions to be deleted. +# +# It abides by the user settings: while in mode "Only Modified Lines", it returns +# the subset of trailing spaces regions which are within dirty lines; otherwise, it +# returns all trailing spaces regions for the document. +# +# view - the view, you know +# +# Returns a list of regions to be deleted. +def find_regions_to_delete(view): + # If the plugin has been running in the background, regions have been matched. + # Otherwise, we must find trailing spaces right now! + if trailing_spaces_live_matching: + regions = view.get_regions('TrailingSpacesMatchedRegions') + else: + (regions, highlightable) = find_trailing_spaces(view) + + # Filtering is required in case triming is restricted to dirty regions only. + if trim_modified_lines_only: + modified_lines = get_modified_lines(view) + + # If there are no dirty lines, don't do nothing. + if not modified_lines: + return + + # Super-private: filters trailing spaces regions to dirty lines only. + # + # As one cannot perform a smart find_all within arbitrary boundaries, we must do some + # extra work: + # - we want to loop through the modified lines set, not the whole trailing regions + # - but we need a way to match modified lines with trailings to those very regions + # + # Hence the reversed dict on regions: keys are the text_point of the begining of + # each region, values are the region's actual boundaries. As a Region is unhashable, + # trailing regions are being recreated later on from those two values. + # + # We loop then loop through the modified lines: for each line, we get its begining + # text_point, and check whether it matches a line with trailing spaces in the + # reversed dict. If so, this is a match (a modified line with trailing spaces), so + # we can re-create and store a Region for the relevant trailing spaces boundaries. + # + # Returns the filtered list of trailing spaces regions for the modified lines set. + def only_those_with_trailing_spaces(): + regions_by_begin = {} + matches = [] + for region in regions: + begin = view.line(region).begin() + regions_by_begin[begin] = (region.begin(), region.end()) + + for line in modified_lines: + text_point = line.begin() + if text_point in regions_by_begin: + matches.append(sublime.Region(regions_by_begin[text_point][0], regions_by_begin[text_point][1])) + + return matches + + regions = only_those_with_trailing_spaces() + + return regions + +# Private: Deletes the trailing spaces regions. +# +# view - the view, you know +# edit - the Edit object spawned by the deletion command +# +# Returns the number of deleted regions. +def delete_trailing_regions(view, edit): + regions = find_regions_to_delete(view) + + if regions: + # Trick: reversing the regions takes care of the growing offset while + # deleting the successive regions. + regions.reverse() + for r in regions: + view.erase(edit, r) + return len(regions) + else: + return 0 + + +# Public: Toggles the highlighting on or off. +class ToggleTrailingSpacesCommand(sublime_plugin.WindowCommand): + def run(self): + view = self.window.active_view() + if max_size_exceeded(view): + sublime.status_message("File is too big, trailing spaces handling disabled.") + return + + state = toggle_highlighting(view) + ts_settings.set("trailing_spaces_highlight_color", current_highlighting_scope) + persist_settings() + sublime.status_message('Highlighting of trailing spaces is %s' % state) + + def is_checked(self): + return current_highlighting_scope != "" + + +# Public: Toggles "Modified Lines Only" mode on or off. +class ToggleTrailingSpacesModifiedLinesOnlyCommand(sublime_plugin.WindowCommand): + def run(self): + global trim_modified_lines_only + + was_on = ts_settings.get("trailing_spaces_modified_lines_only") + ts_settings.set("trailing_spaces_modified_lines_only", not was_on) + persist_settings() + + # TODO: use ts_settings.add_on_change() when it lands in ST3 + trim_modified_lines_only = ts_settings.get('trailing_spaces_modified_lines_only') + message = "Let's trim trailing spaces everywhere" if was_on \ + else "Let's trim trailing spaces only on modified lines" + sublime.status_message(message) + + def is_checked(self): + return ts_settings.get("trailing_spaces_modified_lines_only") + + +# Public: Matches and highlights trailing spaces on key events, according to the +# current settings. +class TrailingSpacesListener(sublime_plugin.EventListener): + def on_modified(self, view): + if trailing_spaces_live_matching: + match_trailing_spaces(view) + + def on_activated(self, view): + if trailing_spaces_live_matching: + match_trailing_spaces(view) + + def on_selection_modified(self, view): + if trailing_spaces_live_matching: + match_trailing_spaces(view) + + def on_activated(self, view): + self.freeze_last_version(view) + if trailing_spaces_live_matching: + match_trailing_spaces(view) + + def on_pre_save(self, view): + self.freeze_last_version(view) + if ts_settings.get("trailing_spaces_trim_on_save"): + view.run_command("delete_trailing_spaces") + + # Toggling messes with what is red from the disk, and it breaks the diff + # used when modified_lines_only is true. Honestly, I don't know why (yet). + # Anyway, let's cache the persisted version of the document's buffer for + # later use on specific event, so that we always have a decent version of + # "what's on the disk" to work with. + def freeze_last_version(self, view): + global on_disk + + file_name = view.file_name() + # For some reasons, the on_activated hook gets fired on a ghost document + # from time to time. + if file_name: + on_disk = codecs.open(file_name, "r", "utf-8").read().splitlines() + + +# Public: Deletes the trailing spaces. +class DeleteTrailingSpacesCommand(sublime_plugin.TextCommand): + def run(self, edit): + if max_size_exceeded(self.view): + sublime.status_message("File is too big, trailing spaces handling disabled.") + return + + deleted = delete_trailing_regions(self.view, edit) + + if deleted: + if ts_settings.get("trailing_spaces_save_after_trim") \ + and not ts_settings.get("trailing_spaces_trim_on_save"): + sublime.set_timeout(lambda: self.save(self.view), 10) + + msg_parts = {"nbRegions": deleted, + "plural": 's' if deleted > 1 else ''} + message = "Deleted %(nbRegions)s trailing spaces region%(plural)s" % msg_parts + else: + message = "No trailing spaces to delete!" + + sublime.status_message(message) + + def save(self, view): + if view.file_name() is None: + view.run_command('prompt_save_as') + else: + view.run_command('save') + + +# ST3 features a plugin_loaded hook which is called when ST's API is ready. +# +# We must therefore call our init callback manually on ST2. It must be the last +# thing in this plugin (thanks, beloved contributors!). +if not int(sublime.version()) > 3000: + plugin_loaded() diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/trailing_spaces.sublime-settings b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/trailing_spaces.sublime-settings new file mode 100644 index 0000000..235c739 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/TrailingSpaces/trailing_spaces.sublime-settings @@ -0,0 +1,56 @@ +// Trailing Spaces' default settings. +// +// In order to tweak the settings, you should NOT edit this file, but instead +// the user-specific, empty-by-default version under "Preferences / Package +// Settings / Trailing Spaces / Settings - User". +// +// See Trailing Spaces' README for detailled instructions. +{ + // By default, Trailing Spaces is "live". It means the trailing spaces + // regions will be matched in the background, and highlighted if a color + // scope is defined, when the document is opened and edited. + // Set to false to disable live matching and highlighting (the deletion + // command remains available, so-called "lazy matching"). + "trailing_spaces_enabled" : true, + + // Highlight color is specified as a scope. You may define and use a custom + // scope to better fit your colorscheme. + "trailing_spaces_highlight_color" : "invalid", + + // By default, empty lines are cleared as well when calling the deletion + // command. + // Set to false to ignore empty lines upon deletion. + "trailing_spaces_include_empty_lines" : true, + + // By default, the line being currently edited will have its trailing + // spaces highlighted. + // Set to false to ignore trailing spaces on the edited line. + "trailing_spaces_include_current_line" : true, + + // By default, trailing spaces are deleted within the whole document. + // Set to true to affect only the lines you edited since last save. + // Trailing spaces will still be searched for and highlighted in the whole + // document. + "trailing_spaces_modified_lines_only": false, + + // By default, nothing happens on save. + // Set to true to trim trailing spaces before saving, with respect to the + // other settings. + "trailing_spaces_trim_on_save": false, + + // By default, deleting trailing spaces does not cause the document to be + // saved. + // Set to true to force saving after trailing spaces have been deleted. + // This setting is irrelevant and will be ignored if trim_on_save is true. + "trailing_spaces_save_after_trim": false, + + // ---- NEXT SETTINGS ARE FOR POWER USERS ONLY! ---- + + // Highlighting will be disabled if the edited file's size is larger than + // this. + // Adjust the value (in number of chars) to whatever fits your performance. + "trailing_spaces_file_max_size" : 1048576, + + // By default, only simple spaces and tabs are matched as "trailing spaces". + "trailing_spaces_regexp": "[ \t]+" +} diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Editor/Default (Windows).sublime-keymap b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Editor/Default (Windows).sublime-keymap new file mode 100644 index 0000000..adac1a1 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Editor/Default (Windows).sublime-keymap @@ -0,0 +1,522 @@ + [ + // since we assign Ctrl+Shift+V to clipboard manager, use paste / indent with default Ctrl+v + { "keys": ["ctrl+v"], "command": "paste_and_indent" }, + + //HACK: override the vertical column selections to use Ctrl+Shift in RDP + { "keys": ["ctrl+shift+up"], "command": "select_lines", "args": {"forward": false} }, + { "keys": ["ctrl+shift+down"], "command": "select_lines", "args": {"forward": true} }, + + //HACK: restore default shift+tab keybindings because SmartMarkdown messes with them too much + { "keys": ["shift+tab"], "command": "insert", "args": {"characters": "\t"} }, + { "keys": ["shift+tab"], "command": "unindent", "context": + [ + { "key": "setting.shift_tab_unindent", "operator": "equal", "operand": true } + ] + }, + { "keys": ["shift+tab"], "command": "unindent", "context": + [ + { "key": "preceding_text", "operator": "regex_match", "operand": "^[\t ]*" } + ] + }, + { "keys": ["shift+tab"], "command": "unindent", "context": + [ + { "key": "text", "operator": "regex_contains", "operand": "\n" } + ] + }, + { "keys": ["shift+tab"], "command": "prev_field", "context": + [ + { "key": "has_prev_field", "operator": "equal", "operand": true } + ] + }, + + //Abacus - https://github.com/khiltd/Abacus + { "keys": ["ctrl+alt+\\"], "command": "abacus" }, + + // Bracketeer - https://github.com/colinta/SublimeBracketeer + { "keys": ["super+]"], "command": "bracketeer_indent" }, + { "keys": ["ctrl+shift+["], "command": "bracketeer_select" }, + { "keys": ["ctrl+["], "command": "bracketeer_goto", "args": { "goto": "left" } }, + { "keys": ["ctrl+]"], "command": "bracketeer_goto", "args": { "goto": "right" } }, + { "keys": ["ctrl+alt+["], "command": "bracketeer_goto", "args": { "goto": "both" } }, + { "keys": ["ctrl+alt+]"], "command": "bracketeer_goto", "args": { "goto": "both" } }, + //| + //| BRACKETEER + //| + { "keys": ["{"], "command": "bracketeer", "args": { "braces": "{}", "unindent": true } }, + { "keys": ["}"], "command": "bracketeer", "args": { "braces": "{}", "pressed": "}", "unindent": true } }, + { "keys": ["["], "command": "bracketeer", "args": { "braces": "[]" } }, + { "keys": ["]"], "command": "bracketeer", "args": { "braces": "[]", "pressed": "]" } }, + { "keys": ["("], "command": "bracketeer", "args": { "braces": "()" } }, + { "keys": [")"], "command": "bracketeer", "args": { "braces": "()", "pressed": ")" } }, + //| reStructured Text + { "keys": ["alt+`"], "command": "bracketeer", "args": { "braces": "````", "pressed": "``" }, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.restructuredtext" } + ] + }, + { "keys": ["*"], "command": "bracketeer", "args": { "braces": "**", "pressed": "*" }, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.restructuredtext" } + ] + }, + //| DJANGO CURLIES + // For django, liquid, jinja. All the grammars *I* have list 'source.smarty' as + // when the cursor is inside "{}"s + { "keys": ["{"], "command": "bracketeer", "args": { "braces": "{ }" }, "context": + [{ "key": "selector", "operator": "equal", "operand": "source.smarty" }] + }, + { "keys": ["{"], "command": "bracketeer", "args": { "braces": "{ }" }, "context": + [{ "key": "selector", "operator": "equal", "operand": "meta.brace.curly" }] + }, + { "keys": ["%"], "command": "bracketeer", "args": { "braces": "% %" }, "context": + [{ "key": "selector", "operator": "equal", "operand": "source.smarty" }] + }, + { "keys": ["%"], "command": "bracketeer", "args": { "braces": "% %" }, "context": + [ + { "key": "selector", "operator": "equal", "operand": "meta.brace.curly" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "<$", "match_all": true } + ] + }, + { "keys": ["%"], "command": "insert_snippet", "args": { "contents": " $1 %>$0" }, "context": + [ + { "key": "selector", "operator": "equal", "operand": "source.ruby" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "<%$", "match_all": true } + ] + }, + { "keys": [">"], "command": "insert_snippet", "args": { "contents": ">$1<% $0" }, "context": + [ + { "key": "selector", "operator": "equal", "operand": "source.ruby" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "%$", "match_all": true } + ] + }, + { "keys": ["="], "command": "insert_snippet", "args": { "contents": "= $1 %>$0" }, "context": + [ + { "key": "selector", "operator": "equal", "operand": "source.ruby" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "<%$", "match_all": true } + ] + }, + { "keys": ["-"], "command": "insert_snippet", "args": { "contents": "- $1 %>$0" }, "context": + [ + { "key": "selector", "operator": "equal", "operand": "source.ruby" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "<%$", "match_all": true } + ] + }, + { "keys": ["#"], "command": "bracketeer", "args": { "braces": "# #" }, "context": + [{ "key": "selector", "operator": "equal", "operand": "source.smarty" }] + }, + //| QUOTES + { "keys": ["\""], "command": "bracketeer", "args": { "braces": "\"\"", "pressed": "\"" } }, + { "keys": ["ctrl+'","ctrl+'"], "command": "bracketeer", "args": { "braces": "\"\"\"\n\n\"\"\"" } }, + { "keys": ["'"], "command": "bracketeer", "args": { "braces": "''", "pressed": "'" } }, + { "keys": ["ctrl+'","'"], "command": "bracketeer", "args": { "braces": "'''\n\n'''" } }, + { "keys": ["`"], "command": "bracketeer", "args": { "braces": "``", "pressed": "`" } }, + { "keys": ["ctrl+'","`"], "command": "insert_snippet", "args": { "contents": "```${1:syntax}\n$0\n```" } }, + { "keys": ["«"], "command": "bracketeer", "args": { "braces": "«»" } }, + { "keys": ["»"], "command": "bracketeer", "args": { "braces": "«»", "pressed": "»" } }, + { "keys": ["‹"], "command": "bracketeer", "args": { "braces": "‹›" } }, + { "keys": ["›"], "command": "bracketeer", "args": { "braces": "‹›", "pressed": "›" } }, + { "keys": ["“"], "command": "bracketeer", "args": { "braces": "“”" } }, + { "keys": ["”"], "command": "bracketeer", "args": { "braces": "“”", "pressed": "”" } }, + { "keys": ["‘"], "command": "bracketeer", "args": { "braces": "‘’" } }, + { "keys": ["’"], "command": "bracketeer", "args": { "braces": "‘’", "pressed": "’" } }, + //| + //| AUTO DELETE MATCHING '', "", [], etc. + //| + { "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Left Right.sublime-macro"}, "context": + [ + { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, + { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "\"$" }, + { "key": "following_text", "operator": "regex_contains", "operand": "^\"" } + ] + }, + { "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Left Right.sublime-macro"}, "context": + [ + { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, + { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "'$" }, + { "key": "following_text", "operator": "regex_contains", "operand": "^'" } + ] + }, + { "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Left Right.sublime-macro"}, "context": + [ + { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, + { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "`$" }, + { "key": "following_text", "operator": "regex_contains", "operand": "^`" } + ] + }, + { "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Left Right.sublime-macro"}, "context": + [ + { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, + { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "«$" }, + { "key": "following_text", "operator": "regex_contains", "operand": "^»" } + ] + }, + { "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Left Right.sublime-macro"}, "context": + [ + { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, + { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "‹$" }, + { "key": "following_text", "operator": "regex_contains", "operand": "^›" } + ] + }, + { "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Left Right.sublime-macro"}, "context": + [ + { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, + { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "“$" }, + { "key": "following_text", "operator": "regex_contains", "operand": "^”" } + ] + }, + { "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Left Right.sublime-macro"}, "context": + [ + { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, + { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "‘$" }, + { "key": "following_text", "operator": "regex_contains", "operand": "^’" } + ] + }, + { "keys": ["backspace"], "command": "run_macro_file", "args": {"file": "Packages/Default/Delete Left Right.sublime-macro"}, "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.restructuredtext" }, + { "key": "setting.auto_match_enabled", "operator": "equal", "operand": true }, + { "key": "selection_empty", "operator": "equal", "operand": true, "match_all": true }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "\\*$" }, + { "key": "following_text", "operator": "regex_contains", "operand": "^\\*" } + ] + }, + //| + //| Bracket and select + //| + { "keys": ["ctrl+alt+[", "backspace"], "command": "bracketeer", "args": { "braces": "", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "{"], "command": "bracketeer", "args": { "braces": "{}", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "{"], "command": "bracketeer", "args": { "braces": "{}", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", " "], "command": "bracketeer", "args": { "braces": " ", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", " "], "command": "bracketeer", "args": { "braces": " ", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "["], "command": "bracketeer", "args": { "braces": "[]", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "["], "command": "bracketeer", "args": { "braces": "[]", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "("], "command": "bracketeer", "args": { "braces": "()", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "("], "command": "bracketeer", "args": { "braces": "()", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "\""], "command": "bracketeer", "args": { "braces": "\"\"", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "\""], "command": "bracketeer", "args": { "braces": "\"\"", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "ctrl+shift+'"], "command": "bracketeer", "args": { "braces": "\"\"\"\"\"\"", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "ctrl+shift+'"], "command": "bracketeer", "args": { "braces": "\"\"\"\"\"\"", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "'"], "command": "bracketeer", "args": { "braces": "''", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "'"], "command": "bracketeer", "args": { "braces": "''", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "ctrl+'"], "command": "bracketeer", "args": { "braces": "''''''", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "ctrl+'"], "command": "bracketeer", "args": { "braces": "''''''", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "`"], "command": "bracketeer", "args": { "braces": "``", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "`"], "command": "bracketeer", "args": { "braces": "``", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "ctrl+`"], "command": "bracketeer", "args": { "braces": "``````", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "ctrl+`"], "command": "bracketeer", "args": { "braces": "``````", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "«"], "command": "bracketeer", "args": { "braces": "«»", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "«"], "command": "bracketeer", "args": { "braces": "«»", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "‹"], "command": "bracketeer", "args": { "braces": "‹›", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "‹"], "command": "bracketeer", "args": { "braces": "‹›", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "“"], "command": "bracketeer", "args": { "braces": "“”", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "“"], "command": "bracketeer", "args": { "braces": "“”", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "‘"], "command": "bracketeer", "args": { "braces": "‘’", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "‘"], "command": "bracketeer", "args": { "braces": "‘’", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "alt+`"], "command": "bracketeer", "args": { "braces": "````", "select": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+alt+[", "alt+`"], "command": "bracketeer", "args": { "braces": "````", "select": true, "replace": true }, "context": + [{ "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }] + }, + { "keys": ["ctrl+[", "*"], "command": "bracketeer", "args": { "braces": "**", "select": true }, "context": + [ + { "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }, + { "key": "selector", "operator": "equal", "operand": "text.restructuredtext" } + ] + }, + { "keys": ["ctrl+alt+[", "*"], "command": "bracketeer", "args": { "braces": "**", "select": true, "replace": true }, "context": + [ + { "key": "selection_empty", "operator": "equal", "operand": false, "match_all": true }, + { "key": "selector", "operator": "equal", "operand": "text.restructuredtext" } + ] + }, + + //BracketHighlighter - https://github.com/facelessuser/BracketHighlighter/ + { + "keys": ["ctrl+shift+b", "ctrl+shift+b"], "command": "bracket_highlighter_key", + "args": {"lines": true} + }, + { + "keys": ["alt+up"], "command": "bracket_highlighter_key", + "args": + { + "lines" : true, + "plugin": + { + "type": ["quote","bracket","tag"], + "command": "bracket_plugins.select_bracket", + "args": {"select": "left"} + } + } + }, + { + "keys": ["alt+down"], "command": "bracket_highlighter_key", + "args": + { + "lines" : true, + "plugin": + { + "type": ["quote","bracket","tag"], + "command": "bracket_plugins.select_bracket", + "args": {"select": "right"} + } + } + }, + { + "keys": ["ctrl+shift+b","ctrl+shift+s"], "command": "bracket_highlighter_key", + "args": + { + "lines" : true, + "plugin": + { + "type": ["quote","bracket","tag"], + "command": "bracket_plugins.select_bracket" + } + } + }, + { + "keys": ["ctrl+shift+b","ctrl+shift+t"], "command": "bracket_highlighter_key", + "args": + { + "ignore": ["quote"], + "plugin": + { + "type": ["tag"], + "command": "bracket_plugins.select_tag" + } + } + }, + { + "keys": ["ctrl+shift+b","ctrl+shift+n"], "command": "bracket_highlighter_key", + "args": + { + "ignore": ["quote"], + "plugin": + { + "type": ["tag"], + "command": "bracket_plugins.select_attr", + "args": {"direction": "right"} + } + } + }, + { + "keys": ["ctrl+shift+b","ctrl+shift+q"], "command": "bracket_highlighter_key", + "args": + { + "plugin": {"type": ["quote"],"command" : "bracket_plugins.swap_quotes"} + } + }, + { + "keys": ["ctrl+shift+b","ctrl+shift+j"], "command": "swap_brackets" + }, + { + "keys": ["ctrl+shift+b","ctrl+shift+["], "command": "bracket_highlighter_key", + "args": + { + "plugin": {"type": ["quote", "tag", "bracket"],"command" : "bracket_plugins.fold_bracket"} + } + }, + + // ClipboardManager - https://github.com/colinta/SublimeClipboardManager + { "keys": ["ctrl+x"], "command": "clipboard_manager_cut" }, + { "keys": ["ctrl+c"], "command": "clipboard_manager_copy" }, + { "keys": ["ctrl+v"], "command": "clipboard_manager_paste", "args": { "indent": true } }, + { "keys": ["ctrl+alt+v"], "command": "clipboard_manager_next_and_paste" }, + { "keys": ["ctrl+shift+alt+v"], "command": "clipboard_manager_previous_and_paste" }, + //{ "keys": ["ctrl+pageup"], "command": "clipboard_manager_next" }, + //{ "keys": ["ctrl+pagedown"], "command": "clipboard_manager_previous" }, + //{ "keys": ["ctrl+home"], "command": "clipboard_manager_show" }, + { "keys": ["ctrl+shift+v"], "command": "clipboard_manager_choose_and_paste" }, + + // EasyMotion - https://github.com/tednaleid/sublime-EasyMotion + { + "keys": ["ctrl+.", "<character>"], + "command": "easy_motion", "args": {"select_text": false} + }, + { + "keys": ["ctrl+.", "enter"], + "command": "easy_motion", "args": {"select_text": false, "character": "enter"} + }, + { + "keys": ["ctrl+shift+.", "<character>"], + "command": "easy_motion", "args": {"select_text": true} + }, + { + "keys": ["ctrl+shift+.", "enter"], + "command": "easy_motion", "args": {"select_text": true, "character": "enter"} + }, + + // ExportHTML - https://github.com/facelessuser/ExportHtml + // use defaults + + // FileDiffs - https://github.com/colinta/SublimeFileDiffs + { "keys": ["ctrl+shift+d"], "command": "file_diff_menu" }, + + // MarkDown Preview - https://github.com/revolunet/sublimetext-markdown-preview + { "keys": ["ctrl+alt+m"], "command": "markdown_preview", "args": {"target": "browser"} }, + + // Smart Markdown - https://github.com/demon386/SmartMarkdown + // accept all the defaults for building lists and tables + { "keys": ["tab"], "command": "smart_folding", "context": + [{ "key": "selector", "operator": "equal", "operand": "markup.heading.markdown" }] + }, + { "keys": ["enter"], "command": "smart_list", "context": [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "^\\s*([-+\\**]|\\d+\\.+)\\s+" } + ]}, + { "keys": ["enter"], "command": "smart_list", "context": [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" }, + { "key": "selector", "operator": "equal", "operand": "markup.list" } + ]}, + { "keys": ["tab"], "command": "smart_table", "args": {"forward": true}, "context": [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "^\\s*(\\||\\+[-=])", "match_all": true} + ]}, + { "keys": ["tab"], "command": "smart_table", "args": {"forward": true}, "context": [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "^\\s*" }, + { "key": "following_text", "operator": "regex_contains", "operand": "\\s*(\\||\\+[-=])", "match_all": true} + ]}, + { "keys": ["shift+tab"], "command": "smart_table", "args": {"forward": false}, "context": [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "^\\s*(\\||\\+[-=])", + "match_all": true} + ]}, + { "keys": ["shift+tab"], "command": "smart_table", "args": {"forward": false}, "context": [ + { "key": "selector", "operator": "equal", "operand": "text.html.markdown" }, + { "key": "preceding_text", "operator": "regex_contains", "operand": "^\\s*" }, + { "key": "following_text", "operator": "regex_contains", "operand": "\\s*(\\||\\+[-=])", "match_all": true} + ]}, + { + "keys": ["super+shift+."], "command": "change_heading_level", "args": {"up": true}, "context": [ + {"key": "selector", "operator": "equal", "operand": "text.html.markdown"} + ]}, + { + "keys": ["super+shift+,"], "command": "change_heading_level", "args": {"up": false}, "context": [ + {"key": "selector", "operator": "equal", "operand": "text.html.markdown"} + ]}, + + //Sublime Text 2 - Related Files - https://github.com/fabiokr/sublime-related-files + { "keys": ["ctrl+alt+t"], "command": "related_files" }, + + //SublimeTODO - https://github.com/robcowie/SublimeTODO + // { + // "keys": ["n"], "command": "goto_next_result", + // "context": [{ "key": "setting.todo_results", "operator": "equal", "operand": true }] + // } + { + "keys": ["n"], "command": "navigate_results", + //// {"key": "setting.todo_results"} + "context": [ {"key": "setting.command_mode", "operand": true}], + "args": {"direction": "forward"} + }, + { + "keys": ["down"], "command": "navigate_results", + // {"key": "setting.todo_results"} + "context": [{"key": "setting.command_mode", "operand": true}], + "args": {"direction": "forward"} + }, + { + "keys": ["j"], "command": "navigate_results", + "context": [{"key": "setting.command_mode", "operand": true}], + // {"key": "setting.todo_results"} + "args": {"direction": "forward"} + }, + { + "keys": ["p"], "command": "navigate_results", + // {"key": "setting.todo_results"} + "context": [{"key": "setting.command_mode", "operand": true}], + "args": {"direction": "backward"} + }, + { + "keys": ["up"], "command": "navigate_results", + //{"key": "setting.todo_results"} + "context": [{"key": "setting.command_mode", "operand": true}], + "args": {"direction": "backward"} + }, + { + "keys": ["k"], "command": "navigate_results", + "context": [{"key": "setting.command_mode", "operand": true}], + //{"key": "setting.todo_results"} + "args": {"direction": "backward"} + }, + { + "keys": ["c"], "command": "clear_selection", + // {"key": "setting.todo_results"} + "context": [{"key": "setting.command_mode", "operand": true}] + }, + { + "keys": ["enter"], "command": "goto_comment", + // {"key": "setting.todo_results"} + "context": [{"key": "setting.command_mode", "operand": true}] + }, + + //Snippet Browsing Keybindings + { "keys": ["shift+f1"], "command": "show_overlay", + "args": {"overlay": "command_palette", "text": "snippet"} } +] diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Editor/README.md b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Editor/README.md new file mode 100644 index 0000000..94d0340 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Editor/README.md @@ -0,0 +1,38 @@ +SublimeKeyMap.Editor +==================== + +A simple repository used to host / share my customized Sublime Text 2 key bindings for Sublime editor enhancement plugins + +Designed to be incorporated into `Package Control.sublime-settings` like: + +```json +{ + "installed_packages": + [ + "Abacus", + "Block Cursor Everywhere", + "Bracketeer", + "BracketHighlighter", + "Clipboard Manager", + "EasyMotion", + "ExportHtml", + "FileDiffs", + "Markdown Preview", + "Missing Palette Commands", + "Related Files", + "SmartMarkdown", + "Solarized Color Scheme", + "StringEncode", + "SublimeTODO", + "TrailingSpaces", + ], + "package_name_map": { + "SublimeKeyMap.Editor": "ZZZ.EthanBrown.SublimeKeyMap.Editor" + }, + "repositories": + [ + "https://github.com/abrookins/OpenSearchResult", + "https://github.com/Iristyle/SublimeKeyMap.Editor" + ] +} +``` diff --git a/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Editor/package-metadata.json b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Editor/package-metadata.json new file mode 100644 index 0000000..2745b25 --- /dev/null +++ b/EthanBrown.SublimeText2.EditorPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Editor/package-metadata.json @@ -0,0 +1 @@ +{"url": "https://github.com/Iristyle/SublimeKeyMap.Editor", "version": "2013.03.26.11.48.08", "description": "A simple repository used to host / share my customized Sublime Text 2 key bindings for Sublime editor enhancement plugins"} \ No newline at end of file