From a000ce8acc90959e397939c542ec0b5ea9bdc172 Mon Sep 17 00:00:00 2001 From: Iristyle Date: Mon, 16 Sep 2013 22:35:46 -0400 Subject: [PATCH] feat(ST2.UtilPackages): bump up all packages - Refresh PackageCache with latest versions of everything --- .../AdvancedNewFile/AdvancedNewFile.py | 587 +- .../AdvancedNewFile.sublime-settings | 32 +- .../PackageCache/AdvancedNewFile/CHANGELOG.md | 27 + .../Default (Linux).sublime-keymap | 13 +- .../Default (OSX).sublime-keymap | 13 +- .../Default (Windows).sublime-keymap | 14 +- .../PackageCache/AdvancedNewFile/LICENSE.txt | 2 +- .../PackageCache/AdvancedNewFile/README.md | 38 +- .../AdvancedNewFile/Side Bar.sublime-menu | 3 + .../AdvancedNewFile/messages.json | 7 +- .../AdvancedNewFile/messages/5.txt | 4 + .../AdvancedNewFile/messages/6.txt | 1 + .../AdvancedNewFile/messages/7.txt | 6 + .../AdvancedNewFile/messages/install.txt | 2 +- .../AdvancedNewFile/package-metadata.json | 2 +- .../examples/flask_application_screenshot.png | Bin 0 -> 39113 bytes .../examples/hello_sublime_screenshot.png | Bin 0 -> 32538 bytes .../PackageCache/EncodingHelper/readme.md | 2 + .../KeymapManager/package-metadata.json | 2 +- .../MoveTab/Default (Linux).sublime-keymap | 21 +- .../MoveTab/Default (OSX).sublime-keymap | 21 +- .../MoveTab/Default (Windows).sublime-keymap | 21 +- .../tools/PackageCache/MoveTab/README.md | 25 +- .../tools/PackageCache/MoveTab/move_tab.py | 2 +- .../MoveTab/package-metadata.json | 2 +- .../Navigation History/package-metadata.json | 2 +- .../Open Recent Files/package-metadata.json | 2 +- .../Base File (OSX).sublime-settings | 5 +- .../OpenSearchResult/Default.sublime-keymap | 10 +- .../PackageCache/OpenSearchResult/README.md | 3 + .../OpenSearchResult/open_search_result.py | 42 +- .../OpenSearchResult/package-metadata.json | 2 +- .../PackageCache/OpenSearchResult/util.py | 2 +- .../Package Control/Default.sublime-commands | 6 +- .../Package Control/Package Control.ca-bundle | 43 + .../Package Control/Package Control.ca-list | 4 + .../Package Control/Package Control.py | 4873 +---------------- .../Package Control.sublime-settings | 80 +- .../certs/1c5282418e2cb4989cd6beddcdbab0b5 | 113 - .../certs/7f4f8622b4fd001c7f648e09aae7edaa | 165 - .../certs/897abe0b41fd2f64e9e2e351cbc36d76 | 285 - .../Package Control/certs/ca-bundle.crt | 563 -- .../certs/d867a7b2aecc46f9c31afc4f2f50de05 | 197 - .../Package Control/example-channel.json | 64 + .../Package Control/example-packages.json | 54 - .../Package Control/example-repositories.json | 60 - .../Package Control/example-repository.json | 275 + .../Package Control/lib/all/semver.py | 86 - .../Package Control/lib/windows/ntlm/U32.py | 113 - .../Package Control/lib/windows/ntlm/des.py | 92 - .../Package Control/lib/windows/ntlm/des_c.py | 328 -- .../lib/windows/ntlm/des_data.py | 348 -- .../Package Control/lib/windows/ntlm/ntlm.py | 466 -- .../Package Control/messages.json | 5 +- .../Package Control/messages/2.0.0.txt | 64 + .../Package Control/package-metadata.json | 6 +- .../package_control/__init__.py | 2 + .../package_control/automatic_upgrader.py | 215 + .../package_control/ca_certs.py | 378 ++ .../Package Control/package_control/cache.py | 173 + .../package_control/clear_directory.py | 37 + .../clients}/__init__.py | 0 .../clients/bitbucket_client.py | 257 + .../clients/client_exception.py | 5 + .../package_control/clients/github_client.py | 292 + .../clients/json_api_client.py | 64 + .../package_control/clients/readme_client.py | 83 + .../Package Control/package_control/cmd.py | 167 + .../package_control/commands/__init__.py | 39 + .../commands/add_channel_command.py | 46 + .../commands/add_repository_command.py | 46 + .../commands/create_binary_package_command.py | 35 + .../commands/create_package_command.py | 32 + .../commands/disable_package_command.py | 48 + .../commands/discover_packages_command.py | 11 + .../commands/enable_package_command.py | 40 + .../commands/existing_packages_command.py | 69 + .../commands/grab_certs_command.py | 109 + .../commands/install_package_command.py | 50 + .../commands/list_packages_command.py | 63 + .../commands/package_message_command.py | 11 + .../commands/remove_package_command.py | 88 + .../commands/upgrade_all_packages_command.py | 77 + .../commands/upgrade_package_command.py | 81 + .../package_control/console_write.py | 20 + .../package_control/download_manager.py | 231 + .../package_control/downloaders/__init__.py | 11 + .../downloaders/background_downloader.py | 62 + .../downloaders/binary_not_found_error.py | 4 + .../downloaders/caching_downloader.py | 185 + .../downloaders/cert_provider.py | 203 + .../downloaders/cli_downloader.py | 81 + .../downloaders/curl_downloader.py | 267 + .../downloaders/decoding_downloader.py | 24 + .../downloaders/downloader_exception.py | 5 + .../package_control/downloaders/http_error.py | 9 + .../downloaders/limiting_downloader.py | 36 + .../downloaders/no_ca_cert_exception.py | 11 + .../downloaders/non_clean_exit_error.py | 13 + .../downloaders/non_http_error.py | 5 + .../downloaders/rate_limit_exception.py | 13 + .../downloaders/urllib_downloader.py | 291 + .../downloaders/wget_downloader.py | 347 ++ .../downloaders/wininet_downloader.py | 652 +++ .../package_control/file_not_found_error.py | 4 + .../package_control/http/__init__.py | 65 + .../http/debuggable_http_connection.py | 72 + .../http/debuggable_http_handler.py | 35 + .../http/debuggable_http_response.py | 66 + .../http/debuggable_https_response.py | 9 + .../http/invalid_certificate_exception.py | 25 + .../http/persistent_handler.py | 116 + .../http/validating_https_connection.py | 345 ++ .../http/validating_https_handler.py | 59 + .../package_control/http_cache.py | 75 + .../package_control/open_compat.py | 27 + .../package_control/package_cleanup.py | 107 + .../package_control/package_creator.py | 39 + .../package_control/package_installer.py | 247 + .../package_control/package_io.py | 126 + .../package_control/package_manager.py | 1024 ++++ .../package_control/package_renamer.py | 117 + .../package_control/preferences_filename.py | 11 + .../package_control/providers/__init__.py | 12 + .../bitbucket_repository_provider.py | 163 + .../providers/channel_provider.py | 312 ++ .../providers/github_repository_provider.py | 169 + .../providers/github_user_provider.py | 172 + .../providers/provider_exception.py | 5 + .../providers/release_selector.py | 125 + .../providers/repository_provider.py | 454 ++ .../package_control/reloader.py | 130 + .../Package Control/package_control/semver.py | 833 +++ .../package_control/show_error.py | 12 + .../package_control/sys_path.py | 27 + .../package_control/thread_progress.py | 46 + .../package_control/unicode.py | 49 + .../package_control/upgraders/__init__.py | 0 .../package_control/upgraders/git_upgrader.py | 106 + .../package_control/upgraders/hg_upgrader.py | 74 + .../package_control/upgraders/vcs_upgrader.py | 27 + .../package_control/versions.py | 81 + .../Package Control/readme.creole | 37 +- .../RecentActiveFiles/package-metadata.json | 2 +- .../Search Anywhere/package-metadata.json | 2 +- .../Commands.sublime-commands | 4 + .../SideBarEnhancements/SideBar.py | 29 +- .../SideBarEnhancements/package-metadata.json | 2 +- .../SideBarEnhancements/readme.md | 193 +- .../PackageCache/Sublime Files/README.md | 6 + .../Sublime Files/package-metadata.json | 2 +- .../Sublime Files/sublime_files.py | 26 +- .../tools/PackageCache/Theme - Soda/README.md | 42 +- .../Theme - Soda/Soda Dark 3.sublime-theme | 1154 ++++ .../Theme - Soda/Soda Dark.sublime-theme | 45 + .../Widget - Soda Dark 3.sublime-settings | 4 + .../Soda Dark/Widget - Soda Dark.stTheme | 59 +- .../Soda Dark/btn-group-left-on.png | Bin 298 -> 310 bytes .../Soda Dark/btn-group-left-on@2x.png | Bin 721 -> 727 bytes .../Theme - Soda/Soda Dark/btn-group-left.png | Bin 325 -> 330 bytes .../Soda Dark/btn-group-left@2x.png | Bin 721 -> 714 bytes .../Soda Dark/btn-group-middle-on@2x.png | Bin 345 -> 347 bytes .../Soda Dark/btn-group-middle.png | Bin 206 -> 206 bytes .../Soda Dark/btn-group-middle@2x.png | Bin 352 -> 356 bytes .../Soda Dark/btn-group-right-on.png | Bin 328 -> 318 bytes .../Soda Dark/btn-group-right-on@2x.png | Bin 718 -> 715 bytes .../Soda Dark/btn-group-right.png | Bin 334 -> 331 bytes .../Soda Dark/btn-group-right@2x.png | Bin 748 -> 746 bytes .../Theme - Soda/Soda Dark/btn-large-on.png | Bin 387 -> 400 bytes .../Soda Dark/btn-large-on@2x.png | Bin 1072 -> 1052 bytes .../Theme - Soda/Soda Dark/btn-large.png | Bin 458 -> 451 bytes .../Theme - Soda/Soda Dark/btn-large@2x.png | Bin 1116 -> 1108 bytes .../Theme - Soda/Soda Dark/btn-small-on.png | Bin 425 -> 432 bytes .../Soda Dark/btn-small-on@2x.png | Bin 1046 -> 1054 bytes .../Theme - Soda/Soda Dark/btn-small.png | Bin 458 -> 451 bytes .../Theme - Soda/Soda Dark/btn-small@2x.png | Bin 1116 -> 1108 bytes .../Soda Dark/classic/tab-active.png | Bin 886 -> 884 bytes .../Soda Dark/classic/tab-active@2x.png | Bin 2018 -> 1992 bytes .../Soda Dark/classic/tab-hover.png | Bin 797 -> 806 bytes .../Soda Dark/classic/tab-hover@2x.png | Bin 1827 -> 1859 bytes .../Soda Dark/classic/tab-inactive.png | Bin 802 -> 800 bytes .../Soda Dark/classic/tab-inactive@2x.png | Bin 1801 -> 1795 bytes .../Soda Dark/file-close-selected.png | Bin 370 -> 367 bytes .../Soda Dark/file-close-selected@2x.png | Bin 868 -> 863 bytes .../Theme - Soda/Soda Dark/file-close.png | Bin 425 -> 429 bytes .../Theme - Soda/Soda Dark/file-close@2x.png | Bin 828 -> 817 bytes .../Soda Dark/file-dirty-selected.png | Bin 349 -> 341 bytes .../Soda Dark/file-dirty-selected@2x.png | Bin 713 -> 723 bytes .../Soda Dark/fold-closed-hover.png | Bin 319 -> 319 bytes .../Soda Dark/fold-closed-hover@2x.png | Bin 658 -> 658 bytes .../Theme - Soda/Soda Dark/fold-closed.png | Bin 311 -> 319 bytes .../Theme - Soda/Soda Dark/fold-closed@2x.png | Bin 658 -> 647 bytes .../Soda Dark/fold-open-hover.png | Bin 305 -> 302 bytes .../Soda Dark/fold-open-hover@2x.png | Bin 578 -> 591 bytes .../Theme - Soda/Soda Dark/fold-open.png | Bin 293 -> 297 bytes .../Theme - Soda/Soda Dark/fold-open@2x.png | Bin 592 -> 583 bytes .../Soda Dark/folder-closed-hover.png | Bin 0 -> 250 bytes .../Soda Dark/folder-closed-hover@2x.png | Bin 0 -> 403 bytes .../Soda Dark/folder-closed-selected.png | Bin 0 -> 266 bytes .../Soda Dark/folder-closed-selected@2x.png | Bin 0 -> 506 bytes .../Theme - Soda/Soda Dark/folder-closed.png | Bin 0 -> 250 bytes .../Soda Dark/folder-closed@2x.png | Bin 0 -> 389 bytes .../Soda Dark/folder-open-hover.png | Bin 0 -> 332 bytes .../Soda Dark/folder-open-hover@2x.png | Bin 0 -> 622 bytes .../Soda Dark/folder-open-selected.png | Bin 0 -> 377 bytes .../Soda Dark/folder-open-selected@2x.png | Bin 0 -> 821 bytes .../Theme - Soda/Soda Dark/folder-open.png | Bin 0 -> 334 bytes .../Theme - Soda/Soda Dark/folder-open@2x.png | Bin 0 -> 606 bytes .../Soda Dark/group-closed-hover.png | Bin 356 -> 355 bytes .../Soda Dark/group-closed-hover@2x.png | Bin 705 -> 710 bytes .../Soda Dark/group-closed-selected.png | Bin 415 -> 410 bytes .../Soda Dark/group-closed-selected@2x.png | Bin 821 -> 822 bytes .../Theme - Soda/Soda Dark/group-closed.png | Bin 340 -> 340 bytes .../Soda Dark/group-closed@2x.png | Bin 740 -> 742 bytes .../Soda Dark/group-open-hover.png | Bin 313 -> 308 bytes .../Soda Dark/group-open-hover@2x.png | Bin 628 -> 623 bytes .../Soda Dark/group-open-selected.png | Bin 380 -> 383 bytes .../Soda Dark/group-open-selected@2x.png | Bin 853 -> 866 bytes .../Theme - Soda/Soda Dark/group-open.png | Bin 311 -> 313 bytes .../Theme - Soda/Soda Dark/group-open@2x.png | Bin 612 -> 606 bytes .../Soda Dark/icon-buffer-off.png | Bin 423 -> 420 bytes .../Soda Dark/icon-buffer-off@2x.png | Bin 950 -> 951 bytes .../Theme - Soda/Soda Dark/icon-buffer-on.png | Bin 524 -> 526 bytes .../Soda Dark/icon-buffer-on@2x.png | Bin 1215 -> 1224 bytes .../Theme - Soda/Soda Dark/icon-case-off.png | Bin 556 -> 543 bytes .../Soda Dark/icon-case-off@2x.png | Bin 1328 -> 1351 bytes .../Theme - Soda/Soda Dark/icon-case-on.png | Bin 685 -> 701 bytes .../Soda Dark/icon-case-on@2x.png | Bin 1721 -> 1711 bytes .../Soda Dark/icon-context-on.png | Bin 310 -> 310 bytes .../Soda Dark/icon-context-on@2x.png | Bin 642 -> 594 bytes .../Soda Dark/icon-highlight-on.png | Bin 422 -> 420 bytes .../Soda Dark/icon-highlight-on@2x.png | Bin 827 -> 810 bytes .../Soda Dark/icon-preserve-on.png | Bin 498 -> 502 bytes .../Soda Dark/icon-preserve-on@2x.png | Bin 1077 -> 1064 bytes .../Theme - Soda/Soda Dark/icon-regex-off.png | Bin 486 -> 486 bytes .../Soda Dark/icon-regex-off@2x.png | Bin 1084 -> 1084 bytes .../Theme - Soda/Soda Dark/icon-regex-on.png | Bin 594 -> 581 bytes .../Soda Dark/icon-regex-on@2x.png | Bin 1390 -> 1396 bytes .../Soda Dark/icon-reverse-on.png | Bin 461 -> 455 bytes .../Soda Dark/icon-reverse-on@2x.png | Bin 1003 -> 989 bytes .../Soda Dark/icon-selection-off@2x.png | Bin 601 -> 601 bytes .../Soda Dark/icon-selection-on.png | Bin 385 -> 379 bytes .../Soda Dark/icon-selection-on@2x.png | Bin 755 -> 745 bytes .../Theme - Soda/Soda Dark/icon-word-off.png | Bin 332 -> 328 bytes .../Soda Dark/icon-word-off@2x.png | Bin 696 -> 708 bytes .../Theme - Soda/Soda Dark/icon-word-on.png | Bin 410 -> 407 bytes .../Soda Dark/icon-word-on@2x.png | Bin 898 -> 871 bytes .../Theme - Soda/Soda Dark/icon-wrap-off.png | Bin 502 -> 498 bytes .../Soda Dark/icon-wrap-off@2x.png | Bin 1170 -> 1159 bytes .../Theme - Soda/Soda Dark/icon-wrap-on.png | Bin 588 -> 599 bytes .../Soda Dark/icon-wrap-on@2x.png | Bin 1473 -> 1481 bytes .../overlay-scrollbar-horizontal.png | Bin 243 -> 236 bytes .../overlay-scrollbar-horizontal@2x.png | Bin 479 -> 488 bytes .../Soda Dark/overlay-scrollbar-vertical.png | Bin 238 -> 238 bytes .../overlay-scrollbar-vertical@2x.png | Bin 465 -> 472 bytes .../Soda Dark/panel-close-pressed.png | Bin 0 -> 572 bytes .../Soda Dark/panel-close-pressed@2x.png | Bin 0 -> 1252 bytes .../Theme - Soda/Soda Dark/panel-close.png | Bin 0 -> 568 bytes .../Theme - Soda/Soda Dark/panel-close@2x.png | Bin 0 -> 1245 bytes .../Soda Dark/quick-panel-background-fix.png | Bin 161 -> 157 bytes .../quick-panel-background-fix@2x.png | Bin 207 -> 200 bytes .../Soda Dark/quick-panel-background.png | Bin 559 -> 553 bytes .../Soda Dark/quick-panel-background@2x.png | Bin 1193 -> 1204 bytes .../Soda Dark/sidebar-row-selected.png | Bin 110 -> 115 bytes .../Soda Dark/sidebar-row-selected@2x.png | Bin 140 -> 142 bytes .../Soda Dark/standard-puck-horizontal.png | Bin 414 -> 419 bytes .../Soda Dark/standard-puck-horizontal@2x.png | Bin 884 -> 868 bytes .../Soda Dark/standard-scrollbar-corner.png | Bin 118 -> 114 bytes .../standard-scrollbar-corner@2x.png | Bin 237 -> 228 bytes .../standard-scrollbar-horizontal.png | Bin 113 -> 111 bytes .../standard-scrollbar-horizontal@2x.png | Bin 143 -> 144 bytes .../Soda Dark/standard-scrollbar-vertical.png | Bin 106 -> 105 bytes .../standard-scrollbar-vertical@2x.png | Bin 160 -> 157 bytes .../Soda Dark/tab-close-inactive.png | Bin 412 -> 405 bytes .../Soda Dark/tab-close-inactive@2x.png | Bin 787 -> 801 bytes .../Theme - Soda/Soda Dark/tab-close.png | Bin 412 -> 405 bytes .../Theme - Soda/Soda Dark/tab-close@2x.png | Bin 787 -> 801 bytes .../Theme - Soda/Soda Dark/tab-inactive.png | Bin 228 -> 207 bytes .../Soda Dark/tab-inactive@2x.png | Bin 302 -> 326 bytes .../Soda Dark/tabset-background.png | Bin 140 -> 138 bytes .../Soda Dark/tabset-background@2x.png | Bin 166 -> 167 bytes .../Soda Dark/tabset-left-hover.png | Bin 0 -> 332 bytes .../Soda Dark/tabset-left-hover@2x.png | Bin 0 -> 681 bytes .../Theme - Soda/Soda Dark/tabset-left.png | Bin 0 -> 334 bytes .../Theme - Soda/Soda Dark/tabset-left@2x.png | Bin 0 -> 688 bytes .../Soda Dark/tabset-list-hover.png | Bin 0 -> 187 bytes .../Soda Dark/tabset-list-hover@2x.png | Bin 0 -> 307 bytes .../Theme - Soda/Soda Dark/tabset-list.png | Bin 0 -> 186 bytes .../Theme - Soda/Soda Dark/tabset-list@2x.png | Bin 0 -> 289 bytes .../Soda Dark/tabset-right-hover.png | Bin 0 -> 342 bytes .../Soda Dark/tabset-right-hover@2x.png | Bin 0 -> 723 bytes .../Theme - Soda/Soda Dark/tabset-right.png | Bin 0 -> 342 bytes .../Soda Dark/tabset-right@2x.png | Bin 0 -> 707 bytes .../Soda Dark/text-field-list-hover.png | Bin 0 -> 301 bytes .../Soda Dark/text-field-list-hover@2x.png | Bin 0 -> 558 bytes .../Soda Dark/text-field-list.png | Bin 0 -> 306 bytes .../Soda Dark/text-field-list@2x.png | Bin 0 -> 537 bytes .../Theme - Soda/Soda Dark/text-field.png | Bin 235 -> 235 bytes .../Theme - Soda/Soda Dark/text-field@2x.png | Bin 380 -> 383 bytes .../Theme - Soda/Soda Light 3.sublime-theme | 1160 ++++ .../Theme - Soda/Soda Light.sublime-theme | 46 + .../Widget - Soda Light 3.sublime-settings | 4 + .../Soda Light/Widget - Soda Light.stTheme | 57 + .../Soda Light/btn-group-left-on.png | Bin 326 -> 325 bytes .../Soda Light/btn-group-left-on@2x.png | Bin 707 -> 708 bytes .../Soda Light/btn-group-left.png | Bin 339 -> 353 bytes .../Soda Light/btn-group-left@2x.png | Bin 787 -> 777 bytes .../Soda Light/btn-group-middle.png | Bin 191 -> 191 bytes .../Soda Light/btn-group-middle@2x.png | Bin 356 -> 352 bytes .../Soda Light/btn-group-right-on@2x.png | Bin 734 -> 733 bytes .../Soda Light/btn-group-right.png | Bin 339 -> 331 bytes .../Soda Light/btn-group-right@2x.png | Bin 767 -> 769 bytes .../Theme - Soda/Soda Light/btn-large-on.png | Bin 456 -> 435 bytes .../Soda Light/btn-large-on@2x.png | Bin 1090 -> 1064 bytes .../Theme - Soda/Soda Light/btn-large.png | Bin 481 -> 485 bytes .../Theme - Soda/Soda Light/btn-large@2x.png | Bin 1137 -> 1150 bytes .../Theme - Soda/Soda Light/btn-small-on.png | Bin 453 -> 454 bytes .../Soda Light/btn-small-on@2x.png | Bin 965 -> 965 bytes .../Theme - Soda/Soda Light/btn-small.png | Bin 481 -> 485 bytes .../Theme - Soda/Soda Light/btn-small@2x.png | Bin 1137 -> 1150 bytes .../Soda Light/classic/tab-active.png | Bin 912 -> 919 bytes .../Soda Light/classic/tab-active@2x.png | Bin 2159 -> 2145 bytes .../Soda Light/classic/tab-hover.png | Bin 802 -> 829 bytes .../Soda Light/classic/tab-hover@2x.png | Bin 1855 -> 1846 bytes .../Soda Light/classic/tab-inactive.png | Bin 808 -> 802 bytes .../Soda Light/classic/tab-inactive@2x.png | Bin 1907 -> 1884 bytes .../Soda Light/file-close-selected.png | Bin 369 -> 365 bytes .../Soda Light/file-close-selected@2x.png | Bin 865 -> 871 bytes .../Theme - Soda/Soda Light/file-close.png | Bin 420 -> 424 bytes .../Theme - Soda/Soda Light/file-close@2x.png | Bin 953 -> 955 bytes .../Soda Light/file-dirty-selected.png | Bin 340 -> 340 bytes .../Soda Light/file-dirty-selected@2x.png | Bin 726 -> 727 bytes .../Soda Light/fold-closed-hover.png | Bin 325 -> 325 bytes .../Soda Light/fold-closed-hover@2x.png | Bin 732 -> 736 bytes .../Theme - Soda/Soda Light/fold-closed.png | Bin 329 -> 330 bytes .../Soda Light/fold-closed@2x.png | Bin 747 -> 742 bytes .../Soda Light/fold-open-hover.png | Bin 324 -> 322 bytes .../Soda Light/fold-open-hover@2x.png | Bin 682 -> 684 bytes .../Theme - Soda/Soda Light/fold-open.png | Bin 326 -> 324 bytes .../Theme - Soda/Soda Light/fold-open@2x.png | Bin 681 -> 688 bytes .../Soda Light/folder-closed-hover.png | Bin 0 -> 282 bytes .../Soda Light/folder-closed-hover@2x.png | Bin 0 -> 517 bytes .../Soda Light/folder-closed-selected.png | Bin 0 -> 268 bytes .../Soda Light/folder-closed-selected@2x.png | Bin 0 -> 507 bytes .../Theme - Soda/Soda Light/folder-closed.png | Bin 0 -> 294 bytes .../Soda Light/folder-closed@2x.png | Bin 0 -> 508 bytes .../Soda Light/folder-open-hover.png | Bin 0 -> 385 bytes .../Soda Light/folder-open-hover@2x.png | Bin 0 -> 778 bytes .../Soda Light/folder-open-selected.png | Bin 0 -> 369 bytes .../Soda Light/folder-open-selected@2x.png | Bin 0 -> 812 bytes .../Theme - Soda/Soda Light/folder-open.png | Bin 0 -> 401 bytes .../Soda Light/folder-open@2x.png | Bin 0 -> 795 bytes .../Soda Light/group-closed-hover.png | Bin 355 -> 352 bytes .../Soda Light/group-closed-hover@2x.png | Bin 761 -> 790 bytes .../Soda Light/group-closed-selected.png | Bin 414 -> 412 bytes .../Soda Light/group-closed-selected@2x.png | Bin 810 -> 815 bytes .../Theme - Soda/Soda Light/group-closed.png | Bin 363 -> 360 bytes .../Soda Light/group-closed@2x.png | Bin 784 -> 794 bytes .../Soda Light/group-open-hover.png | Bin 322 -> 332 bytes .../Soda Light/group-open-hover@2x.png | Bin 700 -> 724 bytes .../Soda Light/group-open-selected.png | Bin 386 -> 380 bytes .../Soda Light/group-open-selected@2x.png | Bin 829 -> 830 bytes .../Theme - Soda/Soda Light/group-open.png | Bin 348 -> 347 bytes .../Theme - Soda/Soda Light/group-open@2x.png | Bin 788 -> 790 bytes .../Soda Light/icon-buffer-off.png | Bin 453 -> 452 bytes .../Soda Light/icon-buffer-off@2x.png | Bin 1023 -> 1018 bytes .../Soda Light/icon-buffer-on.png | Bin 460 -> 458 bytes .../Soda Light/icon-buffer-on@2x.png | Bin 1146 -> 1146 bytes .../Theme - Soda/Soda Light/icon-case-off.png | Bin 603 -> 602 bytes .../Soda Light/icon-case-off@2x.png | Bin 1477 -> 1475 bytes .../Theme - Soda/Soda Light/icon-case-on.png | Bin 642 -> 642 bytes .../Soda Light/icon-case-on@2x.png | Bin 1687 -> 1681 bytes .../Soda Light/icon-context-on.png | Bin 248 -> 249 bytes .../Soda Light/icon-context-on@2x.png | Bin 546 -> 502 bytes .../Soda Light/icon-highlight-off.png | Bin 376 -> 378 bytes .../Soda Light/icon-highlight-off@2x.png | Bin 728 -> 727 bytes .../Soda Light/icon-highlight-on.png | Bin 348 -> 349 bytes .../Soda Light/icon-highlight-on@2x.png | Bin 712 -> 697 bytes .../Soda Light/icon-preserve-off.png | Bin 430 -> 431 bytes .../Soda Light/icon-preserve-off@2x.png | Bin 864 -> 862 bytes .../Soda Light/icon-preserve-on.png | Bin 454 -> 454 bytes .../Soda Light/icon-preserve-on@2x.png | Bin 1053 -> 1031 bytes .../Soda Light/icon-regex-off.png | Bin 511 -> 512 bytes .../Soda Light/icon-regex-off@2x.png | Bin 1204 -> 1204 bytes .../Theme - Soda/Soda Light/icon-regex-on.png | Bin 545 -> 535 bytes .../Soda Light/icon-regex-on@2x.png | Bin 1291 -> 1290 bytes .../Soda Light/icon-reverse-off.png | Bin 377 -> 377 bytes .../Soda Light/icon-reverse-off@2x.png | Bin 829 -> 830 bytes .../Soda Light/icon-reverse-on.png | Bin 408 -> 406 bytes .../Soda Light/icon-reverse-on@2x.png | Bin 953 -> 932 bytes .../Soda Light/icon-selection-off.png | Bin 337 -> 339 bytes .../Soda Light/icon-selection-off@2x.png | Bin 679 -> 681 bytes .../Soda Light/icon-selection-on.png | Bin 332 -> 331 bytes .../Soda Light/icon-selection-on@2x.png | Bin 682 -> 664 bytes .../Theme - Soda/Soda Light/icon-word-off.png | Bin 322 -> 322 bytes .../Soda Light/icon-word-off@2x.png | Bin 724 -> 719 bytes .../Theme - Soda/Soda Light/icon-word-on.png | Bin 382 -> 383 bytes .../Soda Light/icon-word-on@2x.png | Bin 852 -> 851 bytes .../Theme - Soda/Soda Light/icon-wrap-off.png | Bin 522 -> 521 bytes .../Soda Light/icon-wrap-off@2x.png | Bin 1302 -> 1297 bytes .../Theme - Soda/Soda Light/icon-wrap-on.png | Bin 592 -> 594 bytes .../Soda Light/icon-wrap-on@2x.png | Bin 1437 -> 1431 bytes .../overlay-scrollbar-horizontal.png | Bin 243 -> 236 bytes .../overlay-scrollbar-horizontal@2x.png | Bin 479 -> 488 bytes .../Soda Light/overlay-scrollbar-vertical.png | Bin 238 -> 238 bytes .../overlay-scrollbar-vertical@2x.png | Bin 465 -> 472 bytes .../Soda Light/panel-close-pressed.png | Bin 0 -> 609 bytes .../Soda Light/panel-close-pressed@2x.png | Bin 0 -> 1387 bytes .../Theme - Soda/Soda Light/panel-close.png | Bin 0 -> 605 bytes .../Soda Light/panel-close@2x.png | Bin 0 -> 1373 bytes .../Soda Light/quick-panel-background-fix.png | Bin 148 -> 146 bytes .../quick-panel-background-fix@2x.png | Bin 216 -> 226 bytes .../Soda Light/quick-panel-background.png | Bin 528 -> 532 bytes .../Soda Light/quick-panel-background@2x.png | Bin 1152 -> 1196 bytes .../Soda Light/sidebar-row-selected.png | Bin 131 -> 129 bytes .../Soda Light/sidebar-row-selected@2x.png | Bin 169 -> 176 bytes .../Soda Light/standard-puck-horizontal.png | Bin 269 -> 263 bytes .../standard-puck-horizontal@2x.png | Bin 531 -> 526 bytes .../Soda Light/standard-scrollbar-corner.png | Bin 152 -> 152 bytes .../standard-scrollbar-corner@2x.png | Bin 265 -> 266 bytes .../standard-scrollbar-horizontal.png | Bin 129 -> 129 bytes .../standard-scrollbar-horizontal@2x.png | Bin 172 -> 173 bytes .../standard-scrollbar-vertical.png | Bin 124 -> 124 bytes .../standard-scrollbar-vertical@2x.png | Bin 172 -> 177 bytes .../Soda Light/tab-close-inactive.png | Bin 417 -> 412 bytes .../Soda Light/tab-close-inactive@2x.png | Bin 871 -> 882 bytes .../Theme - Soda/Soda Light/tab-close.png | Bin 421 -> 416 bytes .../Theme - Soda/Soda Light/tab-close@2x.png | Bin 855 -> 850 bytes .../Soda Light/tabset-background.png | Bin 147 -> 147 bytes .../Soda Light/tabset-background@2x.png | Bin 179 -> 178 bytes .../Soda Light/tabset-left-hover.png | Bin 0 -> 358 bytes .../Soda Light/tabset-left-hover@2x.png | Bin 0 -> 803 bytes .../Theme - Soda/Soda Light/tabset-left.png | Bin 0 -> 361 bytes .../Soda Light/tabset-left@2x.png | Bin 0 -> 803 bytes .../Soda Light/tabset-list-hover.png | Bin 0 -> 216 bytes .../Soda Light/tabset-list-hover@2x.png | Bin 0 -> 396 bytes .../Theme - Soda/Soda Light/tabset-list.png | Bin 0 -> 180 bytes .../Soda Light/tabset-list@2x.png | Bin 0 -> 325 bytes .../Soda Light/tabset-right-hover.png | Bin 0 -> 351 bytes .../Soda Light/tabset-right-hover@2x.png | Bin 0 -> 817 bytes .../Theme - Soda/Soda Light/tabset-right.png | Bin 0 -> 345 bytes .../Soda Light/tabset-right@2x.png | Bin 0 -> 819 bytes .../Soda Light/text-field-list-hover.png | Bin 0 -> 312 bytes .../Soda Light/text-field-list-hover@2x.png | Bin 0 -> 615 bytes .../Soda Light/text-field-list.png | Bin 0 -> 298 bytes .../Soda Light/text-field-list@2x.png | Bin 0 -> 578 bytes .../Theme - Soda/Soda Light/text-field.png | Bin 253 -> 265 bytes .../Theme - Soda/Soda Light/text-field@2x.png | Bin 441 -> 440 bytes .../Theme - Soda/package-metadata.json | 2 +- .../Default (OSX).sublime-keymap | 31 + .../package-metadata.json | 2 +- 451 files changed, 14151 insertions(+), 8317 deletions(-) create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Side Bar.sublime-menu create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/5.txt create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/6.txt create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/7.txt create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ConsoleExec/examples/flask_application_screenshot.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ConsoleExec/examples/hello_sublime_screenshot.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.ca-bundle create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.ca-list delete mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/1c5282418e2cb4989cd6beddcdbab0b5 delete mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/7f4f8622b4fd001c7f648e09aae7edaa delete mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/897abe0b41fd2f64e9e2e351cbc36d76 delete mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/ca-bundle.crt delete mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/d867a7b2aecc46f9c31afc4f2f50de05 create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-channel.json delete mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-packages.json delete mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-repositories.json create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-repository.json delete mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/all/semver.py delete mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/U32.py delete mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des.py delete mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des_c.py delete mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des_data.py delete mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/ntlm.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/messages/2.0.0.txt create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/__init__.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/automatic_upgrader.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/ca_certs.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/cache.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clear_directory.py rename EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/{lib/windows/ntlm => package_control/clients}/__init__.py (100%) create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/bitbucket_client.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/client_exception.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/github_client.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/json_api_client.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/readme_client.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/cmd.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/__init__.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/add_channel_command.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/add_repository_command.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/create_binary_package_command.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/create_package_command.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/disable_package_command.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/discover_packages_command.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/enable_package_command.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/existing_packages_command.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/grab_certs_command.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/install_package_command.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/list_packages_command.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/package_message_command.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/remove_package_command.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/upgrade_all_packages_command.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/upgrade_package_command.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/console_write.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/download_manager.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/__init__.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/background_downloader.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/binary_not_found_error.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/caching_downloader.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/cert_provider.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/cli_downloader.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/curl_downloader.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/decoding_downloader.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/downloader_exception.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/http_error.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/limiting_downloader.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/no_ca_cert_exception.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/non_clean_exit_error.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/non_http_error.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/rate_limit_exception.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/urllib_downloader.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/wget_downloader.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/wininet_downloader.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/file_not_found_error.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/__init__.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_connection.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_handler.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_response.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_https_response.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/invalid_certificate_exception.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/persistent_handler.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/validating_https_connection.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/validating_https_handler.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http_cache.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/open_compat.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_cleanup.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_creator.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_installer.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_io.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_manager.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_renamer.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/preferences_filename.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/__init__.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/bitbucket_repository_provider.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/channel_provider.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/github_repository_provider.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/github_user_provider.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/provider_exception.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/release_selector.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/repository_provider.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/reloader.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/semver.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/show_error.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/sys_path.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/thread_progress.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/unicode.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/__init__.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/git_upgrader.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/hg_upgrader.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/vcs_upgrader.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/versions.py create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark 3.sublime-theme create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/Widget - Soda Dark 3.sublime-settings create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-hover.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-hover@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-selected.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-selected@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-hover.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-hover@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-selected.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-selected@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/panel-close-pressed.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/panel-close-pressed@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/panel-close.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/panel-close@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left-hover.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left-hover@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list-hover.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list-hover@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right-hover.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right-hover@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list-hover.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list-hover@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light 3.sublime-theme create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/Widget - Soda Light 3.sublime-settings create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed-hover.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed-hover@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed-selected.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed-selected@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-hover.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-hover@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-selected.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-selected@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/panel-close-pressed.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/panel-close-pressed@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/panel-close.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/panel-close@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-left-hover.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-left-hover@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-left.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-left@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-list-hover.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-list-hover@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-list.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-list@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right-hover.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right-hover@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list-hover.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list-hover@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list@2x.png create mode 100644 EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Util/Default (OSX).sublime-keymap diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/AdvancedNewFile.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/AdvancedNewFile.py index 164d7de..6632e99 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/AdvancedNewFile.py +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/AdvancedNewFile.py @@ -20,12 +20,19 @@ SETTINGS = [ "alias_path", "alias_folder_index", "debug", - "auto_refresh_sidebar" + "auto_refresh_sidebar", + "completion_type", + "complete_single_entry", + "use_folder_name", + "relative_from_current", + "default_extension" ] VIEW_NAME = "AdvancedNewFileCreation" WIN_ROOT_REGEX = r"[a-zA-Z]:(/|\\)" NIX_ROOT_REGEX = r"^/" HOME_REGEX = r"^~" +PLATFORM = sublime.platform().lower() +IS_ST3 = int(sublime.version()) > 3000 # Set up logger logging.basicConfig(format='[AdvancedNewFile] %(levelname)s %(message)s') @@ -33,8 +40,8 @@ logger = logging.getLogger() class AdvancedNewFileCommand(sublime_plugin.WindowCommand): - def run(self, is_python=False): - self.PLATFORM = sublime.platform().lower() + def run(self, is_python=False, initial_path=None): + PLATFORM = sublime.platform().lower() self.root = None self.alias_root = None self.top_level_split_char = ":" @@ -42,37 +49,34 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): self.view = self.window.active_view() # Settings will be based on the view - settings = get_settings(self.view) - self.aliases = self.get_aliases(settings) - self.show_path = settings.get("show_path") - self.auto_refresh_sidebar = settings.get("auto_refresh_sidebar") - self.default_folder_index = settings.get("default_folder_index") - self.alias_folder_index = settings.get("alias_folder_index") - default_root = self.get_default_root(settings.get("default_root")) + self.settings = get_settings(self.view) + self.aliases = self.get_aliases() + self.show_path = self.settings.get("show_path") + self.default_folder_index = self.settings.get("default_folder_index") + self.alias_folder_index = self.settings.get("alias_folder_index") + default_root = self.get_default_root(self.settings.get("default_root")) if default_root == "path": - self.root = os.path.expanduser(settings.get("default_path")) + self.root = os.path.expanduser(self.settings.get("default_path")) default_root = "" self.root, path = self.split_path(default_root) - # Set some default values for the auto complete - PathAutocomplete.set_show_files(settings.get("show_files")) - PathAutocomplete.set_aliases(self.aliases) - PathAutocomplete.set_ignore_case(settings.get("ignore_case")) - # Search for initial string - path = settings.get("default_initial", "") - if settings.get("use_cursor_text", False): - tmp = self.get_cursor_path() - if tmp != "": - path = tmp + if initial_path is not None: + path = initial_path + else: + path = self.settings.get("default_initial", "") + if self.settings.get("use_cursor_text", False): + tmp = self.get_cursor_path() + if tmp != "": + path = tmp - alias_root = self.get_default_root(settings.get("alias_root"), True) + alias_root = self.get_default_root(self.settings.get("alias_root"), True) if alias_root == "path": - self.alias_root = os.path.expanduser(settings.get("alias_path")) + self.alias_root = os.path.expanduser(self.settings.get("alias_path")) alias_root = "" self.alias_root, tmp = self.split_path(alias_root, True) - debug = settings.get("debug") or False + debug = self.settings.get("debug") or False if debug: logger.setLevel(logging.DEBUG) else: @@ -80,12 +84,12 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): # Get user input self.show_filename_input(path) - def get_aliases(self, settings): - aliases = settings.get("alias") - all_os_aliases = settings.get("os_specific_alias") + def get_aliases(self): + aliases = self.settings.get("alias") + all_os_aliases = self.settings.get("os_specific_alias") for key in all_os_aliases: - if self.PLATFORM in all_os_aliases.get(key): - aliases[key] = all_os_aliases.get(key).get(self.PLATFORM) + if PLATFORM in all_os_aliases.get(key): + aliases[key] = all_os_aliases.get(key).get(PLATFORM) return aliases @@ -122,7 +126,7 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): root = None try: # Parse windows root - if self.PLATFORM == "windows": + if PLATFORM == "windows": if re.match(WIN_ROOT_REGEX, path): root = path[0:3] path = path[3:] @@ -137,10 +141,30 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): if parts[1] != "": path_list.append(parts[1]) path = self.top_level_split_char.join(path_list) + elif re.match(r"^/", path): + path_offset = 1 + if PLATFORM == "windows": + match = re.match(r"^/([a-zA-Z])/", path) + if match: + root = "%s:\\" % match.group(1) + path_offset = 3 + else: + root, _ = os.path.splitdrive(self.view.file_name()) + root += "\\" + else: + root = "/" + path = path[path_offset:] # Parse if tilde used elif re.match(HOME_REGEX, path) and root == None: root = os.path.expanduser("~") path = path[2:] + elif re.match(r"^\.{1,2}[/\\]", path) and self.settings.get("relative_from_current", False): + path_index = 2 + root = os.path.dirname(self.view.file_name()) + if re.match(r"^\.{2}[/\\]", path): + root = os.path.dirname(root) + path_index = 3 + path = path[path_index:] # Default if root == None: @@ -153,6 +177,8 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): root = root or self.window.folders()[folder_index] except IndexError: root = os.path.expanduser("~") + + return root, path def translate_alias(self, path): @@ -169,9 +195,8 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): root_found = False while join_index >= 0 and not root_found: # Folder aliases - for folder in self.window.folders(): - basename = os.path.basename(folder) - if basename == target: + for name, folder in get_project_folder_data(self.settings.get("use_folder_name")): + if name == target: root = folder root_found = True break @@ -180,7 +205,7 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): if alias == target: alias_path = self.aliases.get(alias) if re.search(HOME_REGEX, alias_path) is None: - if self.PLATFORM == "windows": + if PLATFORM == "windows": if re.search(WIN_ROOT_REGEX, alias_path) is None: root = os.path.join(self.alias_root, alias_path) break @@ -208,51 +233,218 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): caption = 'Enter a path for a new file' if self.is_python: caption = '%s (creates __init__.py in new dirs)' % caption - view = self.window.show_input_panel( + self.input_panel_view = self.window.show_input_panel( caption, initial, self.entered_filename, self.update_filename_input, self.clear ) - view.set_name(VIEW_NAME) - temp = view.settings().get("word_separators") - temp = temp.replace(".", "") - view.settings().set("word_separators", temp) - view.settings().set("auto_complete_commit_on_tab", True) - view.settings().set("tab_completion", True) - - PathAutocomplete.set_view_id(view.id()) - PathAutocomplete.set_root(self.root, True) + self.input_panel_view.set_name(VIEW_NAME) + self.input_panel_view.settings().set("auto_complete_commit_on_tab", False) + self.input_panel_view.settings().set("tab_completion", False) + self.input_panel_view.settings().set("translate_tabs_to_spaces", False) + self.input_panel_view.settings().set("anf_panel", True) def update_filename_input(self, path_in): - base, path = self.split_path(path_in) - if self.top_level_split_char in path_in or re.match(r"^~[/\\]", path_in): - PathAutocomplete.set_root(base, False) - else: - PathAutocomplete.set_root(base, True) + if self.settings.get("completion_type") == "windows": + if "prev_text" in dir(self) and self.prev_text != path_in: + if self.view is not None: + self.view.erase_status("AdvancedNewFile2") + if path_in.endswith("\t"): + path_in = path_in.replace("\t", "") + if self.settings.get("completion_type") == "windows": + path_in = self.windows_completion(path_in) + elif self.settings.get("completion_type") == "nix": + path_in = self.nix_completion(path_in) - creation_path = self.generate_creation_path(base, path) + base, path = self.split_path(path_in) + + creation_path = self.generate_creation_path(base, path, True) if self.show_path: if self.view != None: self.view.set_status("AdvancedNewFile", "Creating file at %s " % \ creation_path) else: - sublime.status_message("Creating file at %s " % creation_path) + sublime.status_message("Creating file at %s" % creation_path) logger.debug("Creation path is '%s'" % creation_path) - PathAutocomplete.set_path(path) - def generate_creation_path(self, base, path): - if self.PLATFORM == "windows": + def generate_completion_list(self, path_in, each_list=False): + alias_list = [] + dir_list = [] + file_list = [] + self.suggestion_entries = [] + if self.top_level_split_char in path_in or re.match(r"^~[/\\]", path_in): + pass + else: + directory, filename = os.path.split(path_in) + if len(directory) == 0: + alias_list += self.generate_alias_auto_complete(filename) + alias_list += self.generate_project_auto_complete(filename) + base, path = self.split_path(path_in) + full_path = self.generate_creation_path(base, path) + + directory, filename = os.path.split(full_path) + + if os.path.isdir(directory): + for d in os.listdir(directory): + full_path = os.path.join(directory, d) + if os.path.isdir(full_path): + is_file = False + elif self.settings.get("show_files"): + is_file = True + else: + continue + + if self.compare_entries(d, filename): + if is_file: + file_list.append(d) + else: + dir_list.append(d) + + completion_list = alias_list + dir_list + file_list + + return sorted(completion_list), alias_list, dir_list, file_list + + def windows_completion(self, path_in): + pattern = r"(.*[/\\:])(.*)" + match = re.match(pattern, path_in) + if "prev_text" in dir(self) and self.prev_text == path_in: + self.offset = (self.offset + 1) % len(self.completion_list) + else: + # Generate new completion list + self.completion_list, self.alias_list, self.dir_list, self.file_list = self.generate_completion_list(path_in) + self.offset = 0 + + if len(self.completion_list) == 0: + if match: + self.completion_list = [match.group(2)] + else: + self.completion_list = [path_in] + match = re.match(pattern, path_in) + if match : + completion = self.completion_list[self.offset] + if self.settings.get("complete_single_entry"): + if len(self.completion_list) == 1: + if completion in self.alias_list: + completion += ":" + elif completion in self.dir_list: + completion += "/" + new_content = re.sub(pattern, r"\1" , path_in) + new_content += completion + first_token = False + else: + completion = self.completion_list[self.offset] + if self.settings.get("complete_single_entry"): + if len(self.completion_list) == 1: + if completion in self.alias_list: + completion += ":" + elif completion in self.dir_list: + completion += "/" + new_content = completion + first_token = True + + if len(self.completion_list) > 1: + if first_token: + if self.view is not None: + if self.completion_list[self.offset] in self.alias_list: + self.view.set_status("AdvancedNewFile2", "Alias Completion") + elif self.completion_list[self.offset] in self.dir_list: + self.view.set_status("AdvancedNewFile2", "Directory Completion") + self.prev_text = new_content + else: + self.prev_text = None + self.input_panel_view.run_command("anf_replace", {"content": new_content}) + return new_content + + def nix_completion(self, path_in): + pattern = r"(.*[/\\:])(.*)" + + completion_list, alias_list, dir_list, file_list = self.generate_completion_list(path_in) + new_content = path_in + if len(completion_list) > 0: + common = os.path.commonprefix(completion_list) + match = re.match(pattern, path_in) + if match : + new_content = re.sub(pattern, r"\1", path_in) + new_content += common + else: + new_content = common + if len(completion_list) > 1: + dir_list = map(lambda s: s + "/", dir_list) + alias_list = map(lambda s: s + ":", alias_list) + status_message_list = sorted(list(dir_list) + list(alias_list) + file_list) + sublime.status_message(", ".join(status_message_list)) + else: + if completion_list[0] in alias_list: + new_content += ":" + elif completion_list[0] in dir_list: + new_content += "/" + self.input_panel_view.run_command("anf_replace", {"content": new_content}) + return new_content + + def generate_project_auto_complete(self, base): + folder_data = get_project_folder_data(self.settings.get("use_folder_name")) + if len(folder_data) > 1: + folders = [x[0] for x in folder_data] + return self.generate_auto_complete(base, folders) + return [] + + def generate_alias_auto_complete(self, base): + return self.generate_auto_complete(base, self.aliases) + + def generate_auto_complete(self, base, iterable_var): + sugg = [] + for entry in iterable_var: + if entry in self.suggestion_entries: + continue + self.suggestion_entries.append(entry) + compare_entry = entry + compare_base = base + if self.settings.get("ignore_case"): + compare_entry = compare_entry.lower() + compare_base = compare_base.lower() + + if self.compare_entries(compare_entry, compare_base): + sugg.append(entry) + + return sugg + + def compare_entries(self, compare_entry, compare_base): + if self.settings.get("ignore_case"): + compare_entry = compare_entry.lower() + compare_base = compare_base.lower() + + return compare_entry.startswith(compare_base) + + + def generate_creation_path(self, base, path, append_extension=False): + if PLATFORM == "windows": if not re.match(WIN_ROOT_REGEX, base): return base + self.top_level_split_char + path else: if not re.match(NIX_ROOT_REGEX, base): return base + self.top_level_split_char + path - return os.path.abspath(os.path.join(base, path)) + tokens = re.split(r"[/\\]", base) + re.split(r"[/\\]", path) + if tokens[0] == "": + tokens[0] = "/" + if PLATFORM == "windows": + tokens[0] = base[0:3] + + full_path = os.path.abspath(os.path.join(*tokens)) + if re.search(r"[/\\]$", path) or len(path) == 0: + full_path += os.path.sep + elif re.search(r"\.", tokens[-1]): + if re.search(r"\.$", tokens[-1]): + full_path += "." + elif append_extension: + filename = os.path.basename(full_path) + if not os.path.exists(full_path): + full_path += self.settings.get("default_extension", "") + return full_path def entered_filename(self, filename): # Check if valid root specified for windows. - if self.PLATFORM == "windows": + if PLATFORM == "windows": if re.match(WIN_ROOT_REGEX, filename): root = filename[0:3] if not os.path.isdir(root): @@ -261,11 +453,11 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): return base, path = self.split_path(filename) - file_path = os.path.join(base, path) + file_path = self.generate_creation_path(base, path, True) # Check for invalid alias specified. if self.top_level_split_char in filename and \ - not (self.PLATFORM == "windows" and re.match(WIN_ROOT_REGEX, base)) and \ - not (self.PLATFORM != "windows" and re.match(NIX_ROOT_REGEX, base)): + not (PLATFORM == "windows" and re.match(WIN_ROOT_REGEX, base)) and \ + not (PLATFORM != "windows" and re.match(NIX_ROOT_REGEX, base)): if base == "": error_message = "Current file cannot be resolved." else: @@ -291,17 +483,16 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): self.refresh_sidebar() def refresh_sidebar(self): - if self.auto_refresh_sidebar: + if self.settings.get("auto_refresh_sidebar"): try: self.window.run_command("refresh_folder_list") except: pass - def clear(self): if self.view != None: self.view.erase_status("AdvancedNewFile") - PathAutocomplete.clear() + self.view.erase_status("AdvancedNewFile2") def create(self, filename): base, filename = os.path.split(filename) @@ -310,13 +501,21 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): open(os.path.join(base, filename), "a").close() def create_folder(self, path): + init_list = [] + if self.is_python: + temp_path = path + while not os.path.exists(temp_path): + init_list.append(temp_path) + temp_path = os.path.dirname(temp_path) try: - os.makedirs(path) + if not os.path.exists(path): + os.makedirs(path) except OSError as ex: if ex.errno != errno.EEXIST: raise - if self.is_python: - open(os.path.join(base, '__init__.py'), 'a').close() + + for entry in init_list: + open(os.path.join(entry, '__init__.py'), 'a').close() def get_cursor_path(self): if self.view == None: @@ -325,7 +524,7 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): view = self.view path = "" for region in view.sel(): - syntax = view.syntax_name(region.begin()) + syntax = view.scope_name(region.begin()) if region.begin() != region.end(): path = view.substr(region) break @@ -337,226 +536,22 @@ class AdvancedNewFileCommand(sublime_plugin.WindowCommand): return path -class PathAutocomplete(sublime_plugin.EventListener): - aliases = {} - show_files = False - ignore_case = False +class AnfReplaceCommand(sublime_plugin.TextCommand): + def run(self, edit, content): + self.view.replace(edit, sublime.Region(0, self.view.size()), content) - path = "" - root = "" - default_root = True - view_id = None - prev_suggestions = [] - prev_base = "" - prev_directory = "" - path_empty = True - prev_root = "" - prev_prefix = "" - prev_locations = [] +class AdvancedNewFileAtCommand(sublime_plugin.WindowCommand): + def run(self, dirs): + if len(dirs) != 1: + return + path = dirs[0] + self.window.run_command("advanced_new_file", {"initial_path": path + os.sep}) - def on_query_context(self, view, key, operator, operand, match_all): - if key == "advanced_new_file_completion" and PathAutocomplete.view_id != None and view.id() == PathAutocomplete.view_id: - return True - return None - def continue_previous_autocomplete(self): - pac = PathAutocomplete - sep = os.sep - root_path = pac.root + sep - prev_base = pac.prev_base - prev_directory = pac.prev_directory - prev_root = pac.prev_root - - base = os.path.basename(pac.path) - directory = os.path.dirname(pac.path) - - # If base is empty, we may be cycling through directory options - if base == "": - return True - - # Ensures the correct directory is used if the default root is specified - # using an alias. - if base == prev_base and \ - directory == prev_directory and \ - prev_root == root_path and \ - pac.default_root: - return True - # Continue completions if file names are completed. - if os.path.isfile(os.path.join(root_path, pac.path)): - return True - return False - - def on_query_completions(self, view, prefix, locations): - self.suggestion_entries = [] - pac = PathAutocomplete - if pac.view_id == None or view.id() != pac.view_id: - return [] - - auto_complete_prefix = "" - if self.continue_previous_autocomplete() and prefix != "": - logger.debug("(Prev) Suggestions") - logger.debug(pac.prev_suggestions) - if len(pac.prev_suggestions) > 1: - return (pac.prev_suggestions, sublime.INHIBIT_WORD_COMPLETIONS | sublime.INHIBIT_EXPLICIT_COMPLETIONS) - elif len(pac.prev_suggestions) == 1: - auto_complete_prefix = pac.prev_suggestions[0][1] - - suggestions = [] - suggestions_w_spaces = [] - root_path = pac.root + os.sep - directory, base = os.path.split(pac.path) - - if directory == "" and pac.default_root: - # Project folders - sugg, sugg_w_spaces = self.generate_project_auto_complete(base) - suggestions += sugg - suggestions_w_spaces += sugg_w_spaces - # Aliases - sugg, sugg_w_spaces = self.generate_alias_auto_complete(base) - suggestions += sugg - suggestions_w_spaces += sugg_w_spaces - - # Directories - path = os.path.join(root_path, directory) - if os.path.exists(path): - sugg, sugg_w_spaces = self.generate_relative_auto_complete(path, base, auto_complete_prefix) - suggestions += sugg - suggestions_w_spaces += sugg_w_spaces - # If suggestions exist, use complete name - # else remove base prefix - if len(suggestions) > 0: - for name in suggestions_w_spaces: - suggestions.append((" " + name, name)) - else: - for name in suggestions_w_spaces: - temp = name - name = name[len(base) - 1:] - suggestions.append((" " + temp, name)) - - if len(suggestions) == 0 and locations == pac.prev_locations: - return (pac.prev_suggestions, sublime.INHIBIT_WORD_COMPLETIONS | sublime.INHIBIT_EXPLICIT_COMPLETIONS) - # Previous used to determine cycling through entries. - pac.prev_directory = directory - pac.prev_base = base - pac.prev_suggestions = suggestions - pac.prev_root = root_path - pac.prev_prefix = prefix - pac.prev_locations = locations - logger.debug("Suggestions:") - logger.debug(suggestions) - return (suggestions, sublime.INHIBIT_WORD_COMPLETIONS | sublime.INHIBIT_EXPLICIT_COMPLETIONS) - - def generate_project_auto_complete(self, base): - folders = sublime.active_window().folders() - if len(folders) > 1: - folders = map(lambda f: os.path.basename(f), folders) - return self.generate_auto_complete(base, folders) - return [], [] - - def generate_alias_auto_complete(self, base): - return self.generate_auto_complete(base, PathAutocomplete.aliases) - - def generate_auto_complete(self, base, iterable_var): - sugg = [] - sugg_w_spaces = [] - - for entry in iterable_var: - if entry in self.suggestion_entries: - continue - self.suggestion_entries.append(entry) - compare_entry = entry - compare_base = base - if PathAutocomplete.ignore_case: - compare_entry = compare_entry.lower() - compare_base = compare_base.lower() - - if compare_entry.find(compare_base) == 0: - if " " in base: - sugg_w_spaces.append(entry + ":") - else: - sugg.append((entry + ":", entry + ":")) - return sugg, sugg_w_spaces - - def generate_relative_auto_complete(self, path, base, auto_complete_prefix): - sep = os.sep - sugg = [] - sugg_w_spaces = [] - - # Attempt to prevent searching the same path when a path has been specified - # Problems occur when using tab to complete entry with single completion - # followed by ctrl + space - if ":" in auto_complete_prefix: - compare_prefix = auto_complete_prefix.split(":", 1)[1] - else: - compare_prefix = auto_complete_prefix - - if re.search(r"[/\\]$", auto_complete_prefix) and not path.endswith(compare_prefix[0:-1]): - path = os.path.join(path, compare_prefix) - - for filename in os.listdir(path): - if PathAutocomplete.show_files or os.path.isdir(os.path.join(path, filename)): - compare_base = base - compare_filename = filename - if PathAutocomplete.ignore_case: - compare_base = compare_base.lower() - compare_filename = filename.lower() - - if compare_filename.find(compare_base) == 0: - # Need to find a better way to do the auto complete. - if " " in compare_base: - if os.path.isdir(os.path.join(path, filename)): - sugg_w_spaces.append(auto_complete_prefix + filename + sep) - else: - sugg_w_spaces.append(auto_complete_prefix + filename) - else: - if os.path.isdir(os.path.join(path, filename)): - sugg.append((" " + auto_complete_prefix + filename + sep, auto_complete_prefix + filename + sep)) - else: - sugg.append((" " + auto_complete_prefix + filename, auto_complete_prefix + filename)) - - return sugg, sugg_w_spaces - - @staticmethod - def set_path(path_input): - PathAutocomplete.path = path_input - - @staticmethod - def set_root(root_input, default_root): - PathAutocomplete.root = root_input - PathAutocomplete.default_root = default_root - - @staticmethod - def clear(): - PathAutocomplete.path = "" - PathAutocomplete.root = "" - PathAutocomplete.prev_suggestions = [] - PathAutocomplete.prev_base = "" - PathAutocomplete.prev_directory = "" - PathAutocomplete.aliases = {} - PathAutocomplete.path_empty = True - PathAutocomplete.prev_root = "" - PathAutocomplete.default_root = True - PathAutocomplete.show_files = False - PathAutocomplete.prev_prefix = "" - PathAutocomplete.prev_locations = [] - PathAutocomplete.view_id = None - - @staticmethod - def set_aliases(aliases): - PathAutocomplete.aliases = aliases - - @staticmethod - def set_show_files(show_files): - PathAutocomplete.show_files = show_files - - @staticmethod - def set_ignore_case(ignore_case): - PathAutocomplete.ignore_case = ignore_case - - @staticmethod - def set_view_id(view_id): - PathAutocomplete.view_id = view_id + def is_visible(self, dirs): + settings = sublime.load_settings("AdvancedNewFile.sublime-settings") + return settings.get("show_sidebar_menu", False) and len(dirs) == 1 def get_settings(view): @@ -579,3 +574,31 @@ def get_settings(view): logger.error("AdvancedNewFile[Warning]: Invalid key '%s' in project settings.", key) return local_settings + +def get_project_folder_data(use_folder_name): + folders = [] + folder_entries = [] + window = sublime.active_window() + project_folders = window.folders() + + if IS_ST3: + project_data = window.project_data() + + if project_data is not None: + if use_folder_name: + for folder in project_data.get("folders", []): + folder_entries.append({}) + else: + folder_entries = project_data.get("folders", []) + else: + for folder in project_folders: + folder_entries.append({}) + for index in range(len(folder_entries)): + folder_path = project_folders[index] + folder_entry = folder_entries[index] + if "name" in folder_entry: + folders.append((folder_entry["name"], folder_path)) + else: + folders.append((os.path.basename(folder_path), folder_path)) + + return folders diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/AdvancedNewFile.sublime-settings b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/AdvancedNewFile.sublime-settings index 76cdf33..af15f63 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/AdvancedNewFile.sublime-settings +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/AdvancedNewFile.sublime-settings @@ -19,7 +19,7 @@ // A boolean defining if cursor text should be used. Text bound by single or // double quotes or within a region will be used. If multiple cursors - // are used, the earliest selection containing a region or existing + // are used, the earliest selection containing a region or existing // within quotes will be used. // NOTE: A value read from cursor will override the default // initial string setting. @@ -55,7 +55,7 @@ // This value specifies the root that will be used when resolving relative paths // defined in aliases. For more information about valid values, see "default_root". - // Note that if "default_path" or "default_folder_index" is used, + // Note that if "default_path" or "default_folder_index" is used, // "alias_path" and "alias_folder_index" must be used for the respective entries. "alias_root": "current", @@ -76,5 +76,29 @@ // In some builds, the sidebar does not refresh when contents of project folder are updated. // This setting is required to refresh the sidebar in these circumstances. // false by default - "auto_refresh_sidebar": false -} \ No newline at end of file + "auto_refresh_sidebar": false, + + // A boolean specifying if an AdvancedNewFile option should be shown in the + // sidebar context menu. + "show_sidebar_menu": false, + + // A string specifying the type of auto completion to use. Valid values are + // "windows" or "nix" + "completion_type": "windows", + + // A boolean setting specifying if a separator should be inserted when + // there is only one completion and completion type is "windows" + "complete_single_entry": true, + + // A boolean setting specifying if the folder name should be used + // or the name specified in the project. This setting only applies to ST3. + "use_folder_name": false, + + // Boolean setting specifying if relative paths should be based on the + // current working directory. + "relative_from_current": true, + + // String containing the default file extension. Note the extension is only applied + // if the specified path does not contain a dot (.) character. + "default_extension": "" +} diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/CHANGELOG.md b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/CHANGELOG.md index 55b5f3a..1eefb08 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/CHANGELOG.md +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/CHANGELOG.md @@ -1,6 +1,33 @@ # Changelog for AdvancedNewFile +- 9 September 2013 + - Bug fix for folder creation. + - Bug fix for permission issue. + - Add default extension setting. + +- 2 September 2013 + - Add setting to begin all relative paths from current working directory if available. + +- 14 August 2013 + - Prompt completion type for first token when using Windows completion. + - Fix bug with path autocompletion. + - Fix bug for tab completion with no view. + +- 27 July 2013 + - Rewrite autocomplete functionality. + - Bug Fixes + - Snippets no longer appear when entering completions. + +- 22 April 2013 + - Add option to refresh sidebar after creating a file. + - Add side bar context menu. + - Bug Fixes + - Multiple autocomplete issues. + - Creation of __init__.py files. + - Filling text with cursor values. + - 2 February 2013 - Update to be compatible with Sublime Text 3. + - 14 January 2013 - Add `alias_root` setting, used with aliases with relative paths. - Add setting to allow user to specify which folder from the project should be used. diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (Linux).sublime-keymap b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (Linux).sublime-keymap index 2ea7bc1..3d9daa9 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (Linux).sublime-keymap +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (Linux).sublime-keymap @@ -3,13 +3,10 @@ { "keys": ["shift+super+alt+n"], "command": "advanced_new_file", "args": {"is_python": true}}, { "keys": ["tab"], - "command": "insert_best_completion", - "args": {"default": "", "exact": false}, - "context": [ - { "key": "advanced_new_file_completion"}, - { "key": "setting.tab_completion", "operator": "equal", "operand": true }, - { "key": "last_command", "operator": "not_equal", "operand": "insert_best_completion" }, - { "key": "auto_complete_visible" , "operator": "equal", "operand": false} - ] + "command": "insert", + "args": {"characters": "\t"}, + "context": [{ + "key": "setting.anf_panel" + }] } ] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (OSX).sublime-keymap b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (OSX).sublime-keymap index 2ea7bc1..3d9daa9 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (OSX).sublime-keymap +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (OSX).sublime-keymap @@ -3,13 +3,10 @@ { "keys": ["shift+super+alt+n"], "command": "advanced_new_file", "args": {"is_python": true}}, { "keys": ["tab"], - "command": "insert_best_completion", - "args": {"default": "", "exact": false}, - "context": [ - { "key": "advanced_new_file_completion"}, - { "key": "setting.tab_completion", "operator": "equal", "operand": true }, - { "key": "last_command", "operator": "not_equal", "operand": "insert_best_completion" }, - { "key": "auto_complete_visible" , "operator": "equal", "operand": false} - ] + "command": "insert", + "args": {"characters": "\t"}, + "context": [{ + "key": "setting.anf_panel" + }] } ] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (Windows).sublime-keymap b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (Windows).sublime-keymap index 11a180a..fe13333 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (Windows).sublime-keymap +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Default (Windows).sublime-keymap @@ -1,16 +1,12 @@ [ { "keys": ["ctrl+alt+n"], "command": "advanced_new_file"}, { "keys": ["shift+ctrl+alt+n"], "command": "advanced_new_file", "args": {"is_python": true}}, - // Forces insert best autocomplete to run for advanced new file { "keys": ["tab"], - "command": "insert_best_completion", - "args": {"default": "", "exact": false}, - "context": [ - { "key": "advanced_new_file_completion"}, - { "key": "setting.tab_completion", "operator": "equal", "operand": true }, - { "key": "last_command", "operator": "not_equal", "operand": "insert_best_completion" }, - { "key": "auto_complete_visible" , "operator": "equal", "operand": false} - ] + "command": "insert", + "args": {"characters": "\t"}, + "context": [{ + "key": "setting.anf_panel" + }] } ] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/LICENSE.txt b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/LICENSE.txt index b086861..90b631c 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/LICENSE.txt +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/LICENSE.txt @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2012 AdvancedNewFile authors +Copyright (c) 2013 AdvancedNewFile authors 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, diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/README.md b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/README.md index d128cbf..19158a2 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/README.md +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/README.md @@ -22,6 +22,12 @@ Clone or copy this repository into the packages directory. By default, they are * Windows: %APPDATA%/Roaming/Sublime Text 2/Packages/ * Linux: ~/.config/sublime-text-2/Packages/ +or + +* OS X: ~/Library/Application Support/Sublime Text 3/Packages/ +* Windows: %APPDATA%/Roaming/Sublime Text 3/Packages/ +* Linux: ~/.config/sublime-text-3/Packages/ + ## Usage Simply bring up the AdvancedNewFile input through the appropriate [key binding](https://github.com/skuroda/Sublime-AdvancedNewFile). Then, enter the path, along with the file name into the input field. Upon pressing enter, the file will be created. In addition, if the directories specified do not yet exists, they will also be created. For more advanced usage of this plugin, be sure to look at [Advanced Path Usage](https://github.com/skuroda/Sublime-AdvancedNewFile#advanced-path-usage). By default, the path to the file being created will be filled shown in the status bar as you enter the path information. @@ -107,13 +113,35 @@ A boolean specifying if case should be ignored when building auto complete list. A boolean specifying if folders should automatically refresh and update the sidebar. In some builds, the sidebar does not refresh when contents of project folder are updated. This setting is required to refresh the sidebar in these circumstances. False by default. +`show_sidebar_menu`: + +A boolean specifying if an AdvancedNewFile option should be shown in the sidebar context menu. + +`completion_type`: + +A string specifying the type of auto completion to use. Valid values are "windows" or "nix". + +`complete_single_entry` + +A boolean setting specifying if a separator should be inserted when there is only one completion and completion type is "windows" + +`use_folder_name`: + +A boolean setting specifying if the folder name should be used or the name specified in the project. This setting only applies to ST3. + +`relative_from_current`: + +Boolean setting specifying if relative paths should be based on the current working directory. + +`default_extension`: + +String containing the default file extension. Note the extension is only applied if the specified path does not contain a dot (.) character. + ### Project Specific Settings All of the above settings can also be specified as part of the project specific settings. These values override any previous values set by higher level settings, with aliases being an exception. Alias settings will be merged with higher level configurations for alias. In addition, if the same alias exist for both default/user settings and project settings, the project setting will take precedence. - "settings": - { - "AdvancedNewFile": - { + "settings": { + "AdvancedNewFile": { "default_initial": "/project/specific/path" } } @@ -154,7 +182,7 @@ Sample OS Specific Aliases: { "os_specific_alias": { - "subl_packages" { + "subl_packages": { "windows": "~\\AppData\\Roaming\\Sublime Text 2\\Packages", "linux": "~/.config/sublime-text-2/Packages", "osx": "~/Library/Application Support/Sublime Text 2/Packages" diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Side Bar.sublime-menu b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Side Bar.sublime-menu new file mode 100644 index 0000000..dc104de --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/Side Bar.sublime-menu @@ -0,0 +1,3 @@ +[ + { "caption": "Advanced New File", "command": "advanced_new_file_at", "args": {"dirs": []} } +] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages.json index 5fb2ced..9801895 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages.json @@ -3,5 +3,8 @@ "2012.11.08.20.00.00": "messages/1.txt", "2012.11.12.11.00.00": "messages/2.txt", "2012.11.26.11.00.00": "messages/3.txt", - "2012.12.17.11.00.00": "messages/4.txt" -} \ No newline at end of file + "2012.12.17.11.00.00": "messages/4.txt", + "2013.07.29.11.00.00": "messages/5.txt", + "2013.08.05.11.00.00": "messages/6.txt", + "2013.09.03.11.00.00": "messages/7.txt" +} diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/5.txt b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/5.txt new file mode 100644 index 0000000..ac23414 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/5.txt @@ -0,0 +1,4 @@ +- Rewrite autocomplete functionality to support *nix or windows style completions. Please see the README for details about new settings. + +Bug Fixes: +- Snippets no longer appear when entering completions. diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/6.txt b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/6.txt new file mode 100644 index 0000000..66277ec --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/6.txt @@ -0,0 +1 @@ +- Merge ST2 and ST3 into a single version. diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/7.txt b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/7.txt new file mode 100644 index 0000000..c5eabd2 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/7.txt @@ -0,0 +1,6 @@ +Enhancements: +- Setting to allow relative paths to be based on the current working directory. Enabled by default. + +Bug Fixes: +- Various fixes for auto completion +- Prompt completion type when using Windows Completion diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/install.txt b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/install.txt index 871cba0..429cd7c 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/install.txt +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/messages/install.txt @@ -2,7 +2,7 @@ Thank you for installing the AdvancedNewFile plugin. For more information please visit https://github.com/skuroda/Sublime-AdvancedNewFile. -Note you may need to restart Sublime Text 2 after installing this plugin. +Note you may need to restart Sublime Text after installing this plugin. If you have any questions, comments, or run into issues, please let me know! Hope you enjoy the plugin. diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/package-metadata.json index c8ced63..831c552 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/AdvancedNewFile/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/skuroda/Sublime-AdvancedNewFile", "version": "2013.04.02.21.22.36", "description": "File creation plugin for Sublime Text 2 and Sublime Text 3."} \ No newline at end of file +{"url": "https://github.com/skuroda/Sublime-AdvancedNewFile", "version": "2013.09.09.06.50.47", "description": "File creation plugin for Sublime Text 2 and Sublime Text 3."} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ConsoleExec/examples/flask_application_screenshot.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ConsoleExec/examples/flask_application_screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..874e7a4cb99b3cbcc0211ae0c0c7de166d76251f GIT binary patch literal 39113 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 literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ConsoleExec/examples/hello_sublime_screenshot.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ConsoleExec/examples/hello_sublime_screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..9de25112c1a3066d35eefee4c17370a56f83ea07 GIT binary patch literal 32538 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 Package Control: Install Package -_To access the console press CTRL + `_ +Alternatively, simply clone this repository into the Packages directory. + + Preferences > Browse Packages... License ------- -Licensed under the [MIT License](http://www.opensource.org/licenses/mit-license.php) \ No newline at end of file +Licensed under the [MIT License](http://www.opensource.org/licenses/mit-license.php) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/move_tab.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/move_tab.py index 104c84b..703d183 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/move_tab.py +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/move_tab.py @@ -3,7 +3,7 @@ """ Move Tab -Plugin for Sublime Text 2 to move tabs around +Plugin for Sublime Text to move tabs around Copyright (c) 2012 Frédéric Massart - FMCorz.net diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/package-metadata.json index 00de1ed..584be22 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/MoveTab/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/SublimeText/MoveTab", "version": "2012.08.24.06.11.17", "description": "Plugin for Sublime Text 2 to move tabs around"} \ No newline at end of file +{"url": "https://github.com/SublimeText/MoveTab", "version": "2013.09.12.08.16.08", "description": "Plugin for Sublime Text to move tabs around"} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Navigation History/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Navigation History/package-metadata.json index 345fe4a..801a344 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Navigation History/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Navigation History/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/timjrobinson/SublimeNavigationHistory", "version": "2013.03.17.15.51.45", "description": "SublimeText 2/3 Navigation History - jump forward and back around your code. "} \ No newline at end of file +{"url": "https://github.com/timjrobinson/SublimeNavigationHistory", "version": "2013.03.17.19.51.45", "description": "SublimeText 2/3 Navigation History - jump forward and back around your code. "} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Open Recent Files/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Open Recent Files/package-metadata.json index fdec3ca..563a603 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Open Recent Files/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Open Recent Files/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/spadgos/sublime-OpenRecentFiles", "version": "2011.10.03.10.20.08", "description": "A package which adds Ctrl+Shift+T to open the most recent files."} \ No newline at end of file +{"url": "https://github.com/spadgos/sublime-OpenRecentFiles", "version": "2011.10.03.14.20.08", "description": "A package which adds Ctrl+Shift+T to open the most recent files."} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/Base File (OSX).sublime-settings b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/Base File (OSX).sublime-settings index 0ed3659..434d4f1 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/Base File (OSX).sublime-settings +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/Base File (OSX).sublime-settings @@ -1,5 +1,6 @@ { "highlight_search_icon": "bookmark", "highlight_search_scope": "entity.name.function", - "highlight_search_results": false -} \ No newline at end of file + "highlight_search_results": false, + "open_search_result_everywhere": false +} diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/Default.sublime-keymap b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/Default.sublime-keymap index 2018feb..8a69d3c 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/Default.sublime-keymap +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/Default.sublime-keymap @@ -3,5 +3,13 @@ "keys": ["g", "o"], "command": "open_search_result", "context": [{"key": "setting.command_mode"}] + }, + { + "keys": ["ctrl+enter"], + "command": "open_search_result", + "context": + [ + { "key": "selector", "operator": "equal", "operand": "text.find-in-files" } + ] } -] \ No newline at end of file +] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/README.md b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/README.md index ed2ce2a..40340cd 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/README.md +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/README.md @@ -24,3 +24,6 @@ openable paths or the icon. See your theme file for examples of colors. - 'highlight_search_icon': If you want an icon to show up in the gutter next to openable paths, include a valid icon name as a string (e.g., 'circle', 'dot' or 'bookmark') +- 'open_search_result_everywhere': Set to true to enable this plugin on all +files not just Find Results panes. You can use this for saving and reopening +your find results. diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/open_search_result.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/open_search_result.py index ccaa753..930ba2d 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/open_search_result.py +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/open_search_result.py @@ -1,31 +1,40 @@ import os import sublime, sublime_plugin -import util +# Load with Python3, fallback to load with Python2 +try: + from .util import parse_line_number, is_file_path +except ValueError: + from util import parse_line_number, is_file_path + + +class OpenSearchResultKeys: + HIGHLIGHT_ENABLED = 'highlight_search_results' + SCOPE_SETTINGS = 'highlight_search_scope' + ICON_SETTINGS = 'highlight_search_icon' + OPEN_EVERYWHERE = 'open_search_result_everywhere' class HighlightFilePaths(sublime_plugin.EventListener): HIGHLIGHT_REGION_NAME = 'HighlightFilePaths' - HIGHLIGHT_ENABLED_KEY = 'highlight_search_results' - SCOPE_SETTINGS_KEY = 'highlight_search_scope' - ICON_SETTINGS_KEY = 'highlight_search_icon' DEFAULT_SCOPE = 'search_result_highlight' DEFAULT_ICON = '' def show_highlight(self, view): valid_regions = [] - show_highlight = view.settings().get(self.HIGHLIGHT_ENABLED_KEY, False) - scope = view.settings().get(self.SCOPE_SETTINGS_KEY, self.DEFAULT_SCOPE) - icon = view.settings().get(self.ICON_SETTINGS_KEY, self.DEFAULT_ICON) + show_highlight = view.settings().get(OpenSearchResultKeys.HIGHLIGHT_ENABLED, False) + scope = view.settings().get(OpenSearchResultKeys.SCOPE_SETTINGS, self.DEFAULT_SCOPE) + icon = view.settings().get(OpenSearchResultKeys.ICON_SETTINGS, self.DEFAULT_ICON) + open_everywhere = view.settings().get(OpenSearchResultKeys.OPEN_EVERYWHERE, False) - if view.name() != 'Find Results': + if open_everywhere == False and view.name() != 'Find Results': return for s in view.sel(): line = view.line(s) line_str = view.substr(view.line(s)) - line_num = util.parse_line_number(line_str) + line_num = parse_line_number(line_str) - if util.is_file_path(line_str) or line_num: + if is_file_path(line_str) or line_num: valid_regions.append(line) if valid_regions: @@ -40,8 +49,8 @@ class HighlightFilePaths(sublime_plugin.EventListener): view.erase_regions(self.HIGHLIGHT_REGION_NAME) def on_selection_modified(self, view): - highlight_enabled = (view.settings().get(self.HIGHLIGHT_ENABLED_KEY) - or view.settings().get(self.ICON_SETTINGS_KEY)) + highlight_enabled = (view.settings().get(OpenSearchResultKeys.HIGHLIGHT_ENABLED) + or view.settings().get(OpenSearchResultKeys.ICON_SETTINGS)) if view.settings().get('is_widget') \ or not view.settings().get('command_mode') \ @@ -108,19 +117,20 @@ class OpenSearchResultCommand(sublime_plugin.TextCommand): break line = self.view.substr(prev).strip() - if util.is_file_path(line): + if is_file_path(line): return self.open_file_from_line(line, line_num) def run(self, edit): + open_everywhere = self.view.settings().get(OpenSearchResultKeys.OPEN_EVERYWHERE, False) for cursor in self.view.sel(): cur_line = self.view.line(cursor) line_str = self.view.substr(cur_line).strip() - line_num = util.parse_line_number(line_str) + line_num = parse_line_number(line_str) - if self.view.name() != 'Find Results': + if open_everywhere == False and self.view.name() != 'Find Results': return - if util.is_file_path(line_str): + if is_file_path(line_str): self.open_file_path(line_str) elif line_num: self.open_file_at_line_num(cur_line, line_num) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/package-metadata.json index 898c230..dcfeaf4 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/abrookins/OpenSearchResult", "version": "2012.09.11.15.18.59", "description": "a Sublime Text 2 plugin that opens files listed in the Find in File output"} \ No newline at end of file +{"url": "https://github.com/abrookins/OpenSearchResult", "version": "2013.07.25.03.39.55", "description": "a Sublime Text 2 plugin that opens files listed in the Find in File output"} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/util.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/util.py index 1196191..25a26e4 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/util.py +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/OpenSearchResult/util.py @@ -27,7 +27,7 @@ def is_file_path(line_str): >>> is_file_path('/Users/me/code/OpenSearchResult/open_search_result.py:') True - >>> is_file_path('C:\Users\me\\test.txt:') + >>> is_file_path('C:\\Users\\me\\test.txt:') True >>> is_file_path('5: def parse_line_number(line_str):') False diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Default.sublime-commands b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Default.sublime-commands index 7d823ee..dd5513c 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Default.sublime-commands +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Default.sublime-commands @@ -5,7 +5,7 @@ }, { "caption": "Package Control: Add Channel", - "command": "add_repository_channel" + "command": "add_channel" }, { "caption": "Package Control: Create Binary Package File", @@ -27,6 +27,10 @@ "caption": "Package Control: Enable Package", "command": "enable_package" }, + { + "caption": "Package Control: Grab CA Certs", + "command": "grab_certs" + }, { "caption": "Package Control: Install Package", "command": "install_package" diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.ca-bundle b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.ca-bundle new file mode 100644 index 0000000..b718caa --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.ca-bundle @@ -0,0 +1,43 @@ +----BEGIN CERTIFICATE----- +MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT +MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i +YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG +EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg +R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 +9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq +fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv +iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU +1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ +bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW +MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA +ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l +uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn +Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS +tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF +PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un +hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV +5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs +MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 +d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j +ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL +MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 +LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug +RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm ++9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW +PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM +xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB +Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3 +hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg +EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF +MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA +FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec +nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z +eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF +hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2 +Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe +vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep ++OkuE6N36B9K +-----END CERTIFICATE----- diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.ca-list b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.ca-list new file mode 100644 index 0000000..93aa232 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.ca-list @@ -0,0 +1,4 @@ +[ + "221e907bdfff70d71cea42361ae209d5", + "7d0986b90061d60c8c02aa3b1cf23850" +] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.py index 4b62978..9c47377 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.py +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.py @@ -1,4810 +1,101 @@ -# coding=utf-8 import sublime -import sublime_plugin -import os import sys -import subprocess -import zipfile -import urllib -import urllib2 -import json -from fnmatch import fnmatch -import re -import threading -import datetime -import time -import shutil -import tempfile -import httplib -import socket -import hashlib -import base64 +import os import locale -import urlparse -import gzip -import StringIO -import zlib -if os.name == 'nt': - from ctypes import windll, create_unicode_buffer +st_version = 2 + +# Warn about out-dated versions of ST3 +if sublime.version() == '': + st_version = 3 + print('Package Control: Please upgrade to Sublime Text 3 build 3012 or newer') + +elif int(sublime.version()) > 3000: + st_version = 3 -def add_to_path(path): - # Python 2.x on Windows can't properly import from non-ASCII paths, so - # this code added the DOC 8.3 version of the lib folder to the path in - # case the user's username includes non-ASCII characters - if os.name == 'nt': - buf = create_unicode_buffer(512) - if windll.kernel32.GetShortPathNameW(path, buf, len(buf)): - path = buf.value - - if path not in sys.path: - sys.path.append(path) +if st_version == 3: + installed_dir, _ = __name__.split('.') +elif st_version == 2: + installed_dir = os.path.basename(os.getcwd()) -lib_folder = os.path.join(sublime.packages_path(), 'Package Control', 'lib') -add_to_path(os.path.join(lib_folder, 'all')) +# Ensure the user has installed Package Control properly +if installed_dir != 'Package Control': + message = (u"Package Control\n\nThis package appears to be installed " + + u"incorrectly.\n\nIt should be installed as \"Package Control\", " + + u"but seems to be installed as \"%s\".\n\n" % installed_dir) + # If installed unpacked + if os.path.exists(os.path.join(sublime.packages_path(), installed_dir)): + message += (u"Please use the Preferences > Browse Packages... menu " + + u"entry to open the \"Packages/\" folder and rename" + + u"\"%s/\" to \"Package Control/\" " % installed_dir) + # If installed as a .sublime-package file + else: + message += (u"Please use the Preferences > Browse Packages... menu " + + u"entry to open the \"Packages/\" folder, then browse up a " + + u"folder and into the \"Installed Packages/\" folder.\n\n" + + u"Inside of \"Installed Packages/\", rename " + + u"\"%s.sublime-package\" to " % installed_dir + + u"\"Package Control.sublime-package\" ") + message += u"and restart Sublime Text." + sublime.error_message(message) + +# Normal execution will finish setting up the package +else: + reloader_name = 'package_control.reloader' + + # ST3 loads each package as a module, so it needs an extra prefix + if st_version == 3: + reloader_name = 'Package Control.' + reloader_name + from imp import reload + + # Make sure all dependencies are reloaded on upgrade + if reloader_name in sys.modules: + reload(sys.modules[reloader_name]) -import semver - - -if os.name == 'nt': - add_to_path(os.path.join(lib_folder, 'windows')) - from ntlm import ntlm - - -def unicode_from_os(e): - # This is needed as some exceptions coming from the OS are - # already encoded and so just calling unicode(e) will result - # in an UnicodeDecodeError as the string isn't in ascii form. try: - # Sublime Text on OS X does not seem to report the correct encoding - # so we hard-code that to UTF-8 - encoding = 'UTF-8' if os.name == 'darwin' else locale.getpreferredencoding() - return unicode(str(e), encoding) + # Python 3 + from .package_control import reloader - # If the "correct" encoding did not work, try some defaults, and then just - # obliterate characters that we can't seen to decode properly - except UnicodeDecodeError: - encodings = ['utf-8', 'cp1252'] - for encoding in encodings: - try: - return unicode(str(e), encoding, errors='strict') - except: - pass - return unicode(str(e), errors='replace') + from .package_control.commands import * + from .package_control.package_cleanup import PackageCleanup + + except (ValueError): + # Python 2 + from package_control import reloader + from package_control import sys_path + + from package_control.commands import * + from package_control.package_cleanup import PackageCleanup -def create_cmd(args, basename_binary=False): - if basename_binary: - args[0] = os.path.basename(args[0]) - - if os.name == 'nt': - return subprocess.list2cmdline(args) - else: - escaped_args = [] - for arg in args: - if re.search('^[a-zA-Z0-9/_^\\-\\.:=]+$', arg) == None: - arg = u"'" + arg.replace(u"'", u"'\\''") + u"'" - escaped_args.append(arg) - return u' '.join(escaped_args) - - -# Monkey patch AbstractBasicAuthHandler to prevent infinite recursion -def non_recursive_http_error_auth_reqed(self, authreq, host, req, headers): - authreq = headers.get(authreq, None) - - if not hasattr(self, 'retried'): - self.retried = 0 - - if self.retried > 5: - raise urllib2.HTTPError(req.get_full_url(), 401, "basic auth failed", - headers, None) - else: - self.retried += 1 - - if authreq: - mo = urllib2.AbstractBasicAuthHandler.rx.search(authreq) - if mo: - scheme, quote, realm = mo.groups() - if scheme.lower() == 'basic': - return self.retry_http_basic_auth(host, req, realm) - -urllib2.AbstractBasicAuthHandler.http_error_auth_reqed = non_recursive_http_error_auth_reqed - - -class DebuggableHTTPResponse(httplib.HTTPResponse): - """ - A custom HTTPResponse that formats debugging info for Sublime Text - """ - - _debug_protocol = 'HTTP' - - def __init__(self, sock, debuglevel=0, strict=0, method=None): - # We have to use a positive debuglevel to get it passed to here, - # however we don't want to use it because by default debugging prints - # to the stdout and we can't capture it, so we use a special -1 value - if debuglevel == 5: - debuglevel = -1 - httplib.HTTPResponse.__init__(self, sock, debuglevel, strict, method) - - def begin(self): - return_value = httplib.HTTPResponse.begin(self) - if self.debuglevel == -1: - print '%s: Urllib2 %s Debug Read' % (__name__, self._debug_protocol) - headers = self.msg.headers - versions = { - 9: 'HTTP/0.9', - 10: 'HTTP/1.0', - 11: 'HTTP/1.1' - } - status_line = versions[self.version] + ' ' + str(self.status) + ' ' + self.reason - headers.insert(0, status_line) - for line in headers: - print u" %s" % line.rstrip() - return return_value - - def read(self, *args): + def plugin_loaded(): + # Make sure the user's locale can handle non-ASCII. A whole bunch of + # work was done to try and make Package Control work even if the locale + # was poorly set, by manually encoding all file paths, but it ended up + # being a fool's errand since the package loading code built into + # Sublime Text is not written to work that way, and although packages + # could be installed, they could not be loaded properly. try: - return httplib.HTTPResponse.read(self, *args) - except (httplib.IncompleteRead) as (e): - return e.partial - - -class DebuggableHTTPSResponse(DebuggableHTTPResponse): - """ - A version of DebuggableHTTPResponse that sets the debug protocol to HTTPS - """ - - _debug_protocol = 'HTTPS' - - -class DebuggableHTTPConnection(httplib.HTTPConnection): - """ - A custom HTTPConnection that formats debugging info for Sublime Text - """ - - response_class = DebuggableHTTPResponse - _debug_protocol = 'HTTP' - - def __init__(self, host, port=None, strict=None, - timeout=socket._GLOBAL_DEFAULT_TIMEOUT, **kwargs): - self.passwd = kwargs.get('passwd') - - # Python 2.6.1 on OS X 10.6 does not include these - self._tunnel_host = None - self._tunnel_port = None - self._tunnel_headers = {} - - httplib.HTTPConnection.__init__(self, host, port, strict, timeout) - - def connect(self): - if self.debuglevel == -1: - print '%s: Urllib2 %s Debug General' % (__name__, self._debug_protocol) - print u" Connecting to %s on port %s" % (self.host, self.port) - httplib.HTTPConnection.connect(self) - - def send(self, string): - # We have to use a positive debuglevel to get it passed to the - # HTTPResponse object, however we don't want to use it because by - # default debugging prints to the stdout and we can't capture it, so - # we temporarily set it to -1 for the standard httplib code - reset_debug = False - if self.debuglevel == 5: - reset_debug = 5 - self.debuglevel = -1 - httplib.HTTPConnection.send(self, string) - if reset_debug or self.debuglevel == -1: - if len(string.strip()) > 0: - print '%s: Urllib2 %s Debug Write' % (__name__, self._debug_protocol) - for line in string.strip().splitlines(): - print ' ' + line - if reset_debug: - self.debuglevel = reset_debug - - def request(self, method, url, body=None, headers={}): - original_headers = headers.copy() - - # Handles the challenge request response cycle before the real request - proxy_auth = headers.get('Proxy-Authorization') - if os.name == 'nt' and proxy_auth and proxy_auth.lstrip()[0:4] == 'NTLM': - # The default urllib2.AbstractHTTPHandler automatically sets the - # Connection header to close because of urllib.addinfourl(), but in - # this case we are going to do some back and forth first for the NTLM - # proxy auth - headers['Connection'] = 'Keep-Alive' - self._send_request(method, url, body, headers) - - response = self.getresponse() - - content_length = int(response.getheader('content-length', 0)) - if content_length: - response._safe_read(content_length) - - proxy_authenticate = response.getheader('proxy-authenticate', None) - if not proxy_authenticate: - raise URLError('Invalid NTLM proxy authentication response') - ntlm_challenge = re.sub('^\s*NTLM\s+', '', proxy_authenticate) - - if self.host.find(':') != -1: - host_port = self.host - else: - host_port = "%s:%s" % (self.host, self.port) - username, password = self.passwd.find_user_password(None, host_port) - domain = '' - user = username - if username.find('\\') != -1: - domain, user = username.split('\\', 1) - - challenge, negotiate_flags = ntlm.parse_NTLM_CHALLENGE_MESSAGE(ntlm_challenge) - new_proxy_authorization = 'NTLM %s' % ntlm.create_NTLM_AUTHENTICATE_MESSAGE(challenge, user, - domain, password, negotiate_flags) - original_headers['Proxy-Authorization'] = new_proxy_authorization - response.close() - - httplib.HTTPConnection.request(self, method, url, body, original_headers) - - -class DebuggableHTTPHandler(urllib2.HTTPHandler): - """ - A custom HTTPHandler that formats debugging info for Sublime Text - """ - - def __init__(self, debuglevel=0, debug=False, **kwargs): - # This is a special value that will not trigger the standard debug - # functionality, but custom code where we can format the output - if debug: - self._debuglevel = 5 - else: - self._debuglevel = debuglevel - self.passwd = kwargs.get('passwd') - - def http_open(self, req): - def http_class_wrapper(host, **kwargs): - kwargs['passwd'] = self.passwd - return DebuggableHTTPConnection(host, **kwargs) - - return self.do_open(http_class_wrapper, req) - - -class RateLimitException(httplib.HTTPException, urllib2.URLError): - """ - An exception for when the rate limit of an API has been exceeded. - """ - - def __init__(self, host, limit): - httplib.HTTPException.__init__(self) - self.host = host - self.limit = limit - - def __str__(self): - return ('Rate limit of %s exceeded for %s' % (self.limit, self.host)) - - -if os.name == 'nt': - class ProxyNtlmAuthHandler(urllib2.BaseHandler): - - handler_order = 300 - auth_header = 'Proxy-Authorization' - - def __init__(self, password_manager=None): - if password_manager is None: - password_manager = HTTPPasswordMgr() - self.passwd = password_manager - self.retried = 0 - - def http_error_407(self, req, fp, code, msg, headers): - proxy_authenticate = headers.get('proxy-authenticate') - if os.name != 'nt' or proxy_authenticate[0:4] != 'NTLM': - return None - - type1_flags = ntlm.NTLM_TYPE1_FLAGS - - if req.host.find(':') != -1: - host_port = req.host - else: - host_port = "%s:%s" % (req.host, req.port) - username, password = self.passwd.find_user_password(None, host_port) - if not username: - return None - - if username.find('\\') == -1: - type1_flags &= ~ntlm.NTLM_NegotiateOemDomainSupplied - - negotiate_message = ntlm.create_NTLM_NEGOTIATE_MESSAGE(username, type1_flags) - auth = 'NTLM %s' % negotiate_message - if req.headers.get(self.auth_header, None) == auth: - return None - req.add_unredirected_header(self.auth_header, auth) - return self.parent.open(req, timeout=req.timeout) - - -# The following code is wrapped in a try because the Linux versions of Sublime -# Text do not include the ssl module due to the fact that different distros -# have different versions -try: - import ssl - - class InvalidCertificateException(httplib.HTTPException, urllib2.URLError): - """ - An exception for when an SSL certification is not valid for the URL - it was presented for. - """ - - def __init__(self, host, cert, reason): - httplib.HTTPException.__init__(self) - self.host = host - self.cert = cert - self.reason = reason - - def __str__(self): - return ('Host %s returned an invalid certificate (%s) %s\n' % - (self.host, self.reason, self.cert)) - - - class ValidatingHTTPSConnection(DebuggableHTTPConnection): - """ - A custom HTTPConnection class that validates SSL certificates, and - allows proxy authentication for HTTPS connections. - """ - - default_port = httplib.HTTPS_PORT - - response_class = DebuggableHTTPSResponse - _debug_protocol = 'HTTPS' - - def __init__(self, host, port=None, key_file=None, cert_file=None, - ca_certs=None, strict=None, **kwargs): - passed_args = {} - if 'timeout' in kwargs: - passed_args['timeout'] = kwargs['timeout'] - DebuggableHTTPConnection.__init__(self, host, port, strict, **passed_args) - - self.passwd = kwargs.get('passwd') - self.key_file = key_file - self.cert_file = cert_file - self.ca_certs = ca_certs - if 'user_agent' in kwargs: - self.user_agent = kwargs['user_agent'] - if self.ca_certs: - self.cert_reqs = ssl.CERT_REQUIRED - else: - self.cert_reqs = ssl.CERT_NONE - - def get_valid_hosts_for_cert(self, cert): - """ - Returns a list of valid hostnames for an SSL certificate - - :param cert: A dict from SSLSocket.getpeercert() - - :return: An array of hostnames - """ - - if 'subjectAltName' in cert: - return [x[1] for x in cert['subjectAltName'] - if x[0].lower() == 'dns'] - else: - return [x[0][1] for x in cert['subject'] - if x[0][0].lower() == 'commonname'] - - def validate_cert_host(self, cert, hostname): - """ - Checks if the cert is valid for the hostname - - :param cert: A dict from SSLSocket.getpeercert() - - :param hostname: A string hostname to check - - :return: A boolean if the cert is valid for the hostname - """ - - hosts = self.get_valid_hosts_for_cert(cert) - for host in hosts: - host_re = host.replace('.', '\.').replace('*', '[^.]*') - if re.search('^%s$' % (host_re,), hostname, re.I): - return True - return False - - def _tunnel(self, ntlm_follow_up=False): - """ - This custom _tunnel method allows us to read and print the debug - log for the whole response before throwing an error, and adds - support for proxy authentication - """ - - self._proxy_host = self.host - self._proxy_port = self.port - self._set_hostport(self._tunnel_host, self._tunnel_port) - - self._tunnel_headers['Host'] = u"%s:%s" % (self.host, self.port) - self._tunnel_headers['User-Agent'] = self.user_agent - self._tunnel_headers['Proxy-Connection'] = 'Keep-Alive' - - request = "CONNECT %s:%d HTTP/1.1\r\n" % (self.host, self.port) - for header, value in self._tunnel_headers.iteritems(): - request += "%s: %s\r\n" % (header, value) - self.send(request + "\r\n") - - response = self.response_class(self.sock, strict=self.strict, - method=self._method) - (version, code, message) = response._read_status() - - status_line = u"%s %s %s" % (version, code, message.rstrip()) - headers = [status_line] - - if self.debuglevel in [-1, 5]: - print '%s: Urllib2 %s Debug Read' % (__name__, self._debug_protocol) - print u" %s" % status_line - - content_length = 0 - close_connection = False - while True: - line = response.fp.readline() - if line == '\r\n': break - - headers.append(line.rstrip()) - - parts = line.rstrip().split(': ', 1) - name = parts[0].lower() - value = parts[1].lower().strip() - if name == 'content-length': - content_length = int(value) - - if name in ['connection', 'proxy-connection'] and value == 'close': - close_connection = True - - if self.debuglevel in [-1, 5]: - print u" %s" % line.rstrip() - - # Handle proxy auth for SSL connections since regular urllib2 punts on this - if code == 407 and self.passwd and ('Proxy-Authorization' not in self._tunnel_headers or ntlm_follow_up): - if content_length: - response._safe_read(content_length) - - supported_auth_methods = {} - for line in headers: - parts = line.split(': ', 1) - if parts[0].lower() != 'proxy-authenticate': - continue - details = parts[1].split(' ', 1) - supported_auth_methods[details[0].lower()] = details[1] if len(details) > 1 else '' - - username, password = self.passwd.find_user_password(None, "%s:%s" % ( - self._proxy_host, self._proxy_port)) - - do_ntlm_follow_up = False - - if 'digest' in supported_auth_methods: - response_value = self.build_digest_response( - supported_auth_methods['digest'], username, password) - if response_value: - self._tunnel_headers['Proxy-Authorization'] = u"Digest %s" % response_value - - elif 'basic' in supported_auth_methods: - response_value = u"%s:%s" % (username, password) - response_value = base64.b64encode(response_value).strip() - self._tunnel_headers['Proxy-Authorization'] = u"Basic %s" % response_value - - elif 'ntlm' in supported_auth_methods and os.name == 'nt': - ntlm_challenge = supported_auth_methods['ntlm'] - if not len(ntlm_challenge): - type1_flags = ntlm.NTLM_TYPE1_FLAGS - if username.find('\\') == -1: - type1_flags &= ~ntlm.NTLM_NegotiateOemDomainSupplied - - negotiate_message = ntlm.create_NTLM_NEGOTIATE_MESSAGE(username, type1_flags) - self._tunnel_headers['Proxy-Authorization'] = 'NTLM %s' % negotiate_message - do_ntlm_follow_up = True - else: - domain = '' - user = username - if username.find('\\') != -1: - domain, user = username.split('\\', 1) - - challenge, negotiate_flags = ntlm.parse_NTLM_CHALLENGE_MESSAGE(ntlm_challenge) - self._tunnel_headers['Proxy-Authorization'] = 'NTLM %s' % ntlm.create_NTLM_AUTHENTICATE_MESSAGE(challenge, user, - domain, password, negotiate_flags) - - if 'Proxy-Authorization' in self._tunnel_headers: - self.host = self._proxy_host - self.port = self._proxy_port - - # If the proxy wanted the connection closed, we need to make a new connection - if close_connection: - self.sock.close() - self.sock = socket.create_connection((self.host, self.port), self.timeout) - - return self._tunnel(do_ntlm_follow_up) - - if code != 200: - self.close() - raise socket.error("Tunnel connection failed: %d %s" % (code, - message.strip())) - - def build_digest_response(self, fields, username, password): - """ - Takes a Proxy-Authenticate: Digest header and creates a response - header - - :param fields: - The string portion of the Proxy-Authenticate header after - "Digest " - - :param username: - The username to use for the response - - :param password: - The password to use for the response - - :return: - None if invalid Proxy-Authenticate header, otherwise the - string of fields for the Proxy-Authorization: Digest header - """ - - fields = urllib2.parse_keqv_list(urllib2.parse_http_list(fields)) - - realm = fields.get('realm') - nonce = fields.get('nonce') - qop = fields.get('qop') - algorithm = fields.get('algorithm') - if algorithm: - algorithm = algorithm.lower() - opaque = fields.get('opaque') - - if algorithm in ['md5', None]: - def hash(string): - return hashlib.md5(string).hexdigest() - elif algorithm == 'sha': - def hash(string): - return hashlib.sha1(string).hexdigest() - else: - return None - - host_port = u"%s:%s" % (self.host, self.port) - - a1 = "%s:%s:%s" % (username, realm, password) - a2 = "CONNECT:%s" % host_port - ha1 = hash(a1) - ha2 = hash(a2) - - if qop == None: - response = hash(u"%s:%s:%s" % (ha1, nonce, ha2)) - elif qop == 'auth': - nc = '00000001' - cnonce = hash(urllib2.randombytes(8))[:8] - response = hash(u"%s:%s:%s:%s:%s:%s" % (ha1, nonce, nc, cnonce, qop, ha2)) - else: - return None - - response_fields = { - 'username': username, - 'realm': realm, - 'nonce': nonce, - 'response': response, - 'uri': host_port - } - if algorithm: - response_fields['algorithm'] = algorithm - if qop == 'auth': - response_fields['nc'] = nc - response_fields['cnonce'] = cnonce - response_fields['qop'] = qop - if opaque: - response_fields['opaque'] = opaque - - return ', '.join([u"%s=\"%s\"" % (field, response_fields[field]) for field in response_fields]) - - def connect(self): - """ - Adds debugging and SSL certification validation - """ - - if self.debuglevel == -1: - print '%s: Urllib2 HTTPS Debug General' % __name__ - print u" Connecting to %s on port %s" % (self.host, self.port) - - self.sock = socket.create_connection((self.host, self.port), self.timeout) - if self._tunnel_host: - self._tunnel() - - if self.debuglevel == -1: - print u"%s: Urllib2 HTTPS Debug General" % __name__ - print u" Connecting to %s on port %s" % (self.host, self.port) - print u" CA certs file at %s" % (self.ca_certs) - - self.sock = ssl.wrap_socket(self.sock, keyfile=self.key_file, - certfile=self.cert_file, cert_reqs=self.cert_reqs, - ca_certs=self.ca_certs) - - if self.debuglevel == -1: - print u" Successfully upgraded connection to %s:%s with SSL" % ( - self.host, self.port) - - # This debugs and validates the SSL certificate - if self.cert_reqs & ssl.CERT_REQUIRED: - cert = self.sock.getpeercert() - - if self.debuglevel == -1: - subjectMap = { - 'organizationName': 'O', - 'commonName': 'CN', - 'organizationalUnitName': 'OU', - 'countryName': 'C', - 'serialNumber': 'serialNumber', - 'commonName': 'CN', - 'localityName': 'L', - 'stateOrProvinceName': 'S' - } - subject_list = list(cert['subject']) - subject_list.reverse() - subject_parts = [] - for pair in subject_list: - if pair[0][0] in subjectMap: - field_name = subjectMap[pair[0][0]] - else: - field_name = pair[0][0] - subject_parts.append(field_name + '=' + pair[0][1]) - - print u" Server SSL certificate:" - print u" subject: " + ','.join(subject_parts) - if 'subjectAltName' in cert: - print u" common name: " + cert['subjectAltName'][0][1] - if 'notAfter' in cert: - print u" expire date: " + cert['notAfter'] - - hostname = self.host.split(':', 0)[0] - - if not self.validate_cert_host(cert, hostname): - if self.debuglevel == -1: - print u" Certificate INVALID" - - raise InvalidCertificateException(hostname, cert, - 'hostname mismatch') - - if self.debuglevel == -1: - print u" Certificate validated for %s" % hostname - - if hasattr(urllib2, 'HTTPSHandler'): - class ValidatingHTTPSHandler(urllib2.HTTPSHandler): - """ - A urllib2 handler that validates SSL certificates for HTTPS requests - """ - - def __init__(self, **kwargs): - # This is a special value that will not trigger the standard debug - # functionality, but custom code where we can format the output - self._debuglevel = 0 - if 'debug' in kwargs and kwargs['debug']: - self._debuglevel = 5 - elif 'debuglevel' in kwargs: - self._debuglevel = kwargs['debuglevel'] - self._connection_args = kwargs - - def https_open(self, req): - def http_class_wrapper(host, **kwargs): - full_kwargs = dict(self._connection_args) - full_kwargs.update(kwargs) - return ValidatingHTTPSConnection(host, **full_kwargs) - - try: - return self.do_open(http_class_wrapper, req) - except urllib2.URLError, e: - if type(e.reason) == ssl.SSLError and e.reason.args[0] == 1: - raise InvalidCertificateException(req.host, '', - e.reason.args[1]) - raise - - https_request = urllib2.AbstractHTTPHandler.do_request_ - -except (ImportError): - pass - - -def preferences_filename(): - """:return: The appropriate settings filename based on the version of Sublime Text""" - - if int(sublime.version()) >= 2174: - return 'Preferences.sublime-settings' - return 'Global.sublime-settings' - - -class ThreadProgress(): - """ - Animates an indicator, [= ], in the status area while a thread runs - - :param thread: - The thread to track for activity - - :param message: - The message to display next to the activity indicator - - :param success_message: - The message to display once the thread is complete - """ - - def __init__(self, thread, message, success_message): - self.thread = thread - self.message = message - self.success_message = success_message - 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) + os.path.exists(os.path.join(sublime.packages_path(), u"fran\u00e7ais")) + except (UnicodeEncodeError) as e: + message = (u"Package Control\n\nYour system's locale is set to a " + + u"value that can not handle non-ASCII characters. Package " + + u"Control can not properly work unless this is fixed.\n\n" + + u"On Linux, please reference your distribution's docs for " + + u"information on properly setting the LANG environmental " + + u"variable. As a temporary work-around, you can launch " + + u"Sublime Text from the terminal with:\n\n" + + u"LANG=en_US.UTF-8 sublime_text") + sublime.error_message(message) return - before = i % self.size - after = (self.size - 1) - before + # Start shortly after Sublime starts so package renames don't cause errors + # with keybindings, settings, etc disappearing in the middle of parsing + sublime.set_timeout(lambda: PackageCleanup().start(), 2000) - sublime.status_message('%s [%s=%s]' % \ - (self.message, ' ' * before, ' ' * after)) - - if not after: - self.addend = -1 - if not before: - self.addend = 1 - i += self.addend - - sublime.set_timeout(lambda: self.run(i), 100) - - -class PlatformComparator(): - def get_best_platform(self, platforms): - ids = [sublime.platform() + '-' + sublime.arch(), sublime.platform(), - '*'] - - for id in ids: - if id in platforms: - return id - - return None - - -class ChannelProvider(PlatformComparator): - """ - Retrieves a channel and provides an API into the information - - The current channel/repository infrastructure caches repository info into - the channel to improve the Package Control client performance. This also - has the side effect of lessening the load on the GitHub and BitBucket APIs - and getting around not-infrequent HTTP 503 errors from those APIs. - - :param channel: - The URL of the channel - - :param package_manager: - An instance of :class:`PackageManager` used to download the file - """ - - def __init__(self, channel, package_manager): - self.channel_info = None - self.channel = channel - self.package_manager = package_manager - self.unavailable_packages = [] - - def match_url(self): - """Indicates if this provider can handle the provided channel""" - - return True - - def fetch_channel(self): - """Retrieves and loads the JSON for other methods to use""" - - if self.channel_info != None: - return - - channel_json = self.package_manager.download_url(self.channel, - 'Error downloading channel.') - if channel_json == False: - self.channel_info = False - return - - try: - channel_info = json.loads(channel_json) - except (ValueError): - print '%s: Error parsing JSON from channel %s.' % (__name__, - self.channel) - channel_info = False - - self.channel_info = channel_info - - def get_name_map(self): - """:return: A dict of the mapping for URL slug -> package name""" - - self.fetch_channel() - if self.channel_info == False: - return False - return self.channel_info.get('package_name_map', {}) - - def get_renamed_packages(self): - """:return: A dict of the packages that have been renamed""" - - self.fetch_channel() - if self.channel_info == False: - return False - return self.channel_info.get('renamed_packages', {}) - - def get_repositories(self): - """:return: A list of the repository URLs""" - - self.fetch_channel() - if self.channel_info == False: - return False - return self.channel_info['repositories'] - - def get_certs(self): - """ - Provides a secure way for distribution of SSL CA certificates - - Unfortunately Python does not include a bundle of CA certs with urllib2 - to perform SSL certificate validation. To circumvent this issue, - Package Control acts as a distributor of the CA certs for all HTTPS - URLs of package downloads. - - The default channel scrapes and caches info about all packages - periodically, and in the process it checks the CA certs for all of - the HTTPS URLs listed in the repositories. The contents of the CA cert - files are then hashed, and the CA cert is stored in a filename with - that hash. This is a fingerprint to ensure that Package Control has - the appropriate CA cert for a domain name. - - Next, the default channel file serves up a JSON object of the domain - names and the hashes of their current CA cert files. If Package Control - does not have the appropriate hash for a domain, it may retrieve it - from the channel server. To ensure that Package Control is talking to - a trusted authority to get the CA certs from, the CA cert for - sublime.wbond.net is bundled with Package Control. Then when downloading - the channel file, Package Control can ensure that the channel file's - SSL certificate is valid, thus ensuring the resulting CA certs are - legitimate. - - As a matter of optimization, the distribution of Package Control also - includes the current CA certs for all known HTTPS domains that are - included in the channel, as of the time when Package Control was - last released. - - :return: A dict of {'Domain Name': ['cert_file_hash', 'cert_file_download_url']} - """ - - self.fetch_channel() - if self.channel_info == False: - return False - return self.channel_info.get('certs', {}) - - def get_packages(self, repo): - """ - Provides access to the repository info that is cached in a channel - - :param repo: - The URL of the repository to get the cached info of - - :return: - A dict in the format: - { - 'Package Name': { - # Package details - see example-packages.json for format - }, - ... - } - or False if there is an error - """ - - self.fetch_channel() - if self.channel_info == False: - return False - if self.channel_info.get('packages', False) == False: - return False - if self.channel_info['packages'].get(repo, False) == False: - return False - - output = {} - for package in self.channel_info['packages'][repo]: - copy = package.copy() - - platforms = copy['platforms'].keys() - best_platform = self.get_best_platform(platforms) - - if not best_platform: - self.unavailable_packages.append(copy['name']) - continue - - copy['downloads'] = copy['platforms'][best_platform] - - del copy['platforms'] - - copy['url'] = copy['homepage'] - del copy['homepage'] - - output[copy['name']] = copy - - return output - - def get_unavailable_packages(self): - """ - Provides a list of packages that are unavailable for the current - platform/architecture that Sublime Text is running on. - - This list will be empty unless get_packages() is called first. - - :return: A list of package names - """ - - return self.unavailable_packages - - -# The providers (in order) to check when trying to download a channel -_channel_providers = [ChannelProvider] - - -class PackageProvider(PlatformComparator): - """ - Generic repository downloader that fetches package info - - With the current channel/repository architecture where the channel file - caches info from all includes repositories, these package providers just - serve the purpose of downloading packages not in the default channel. - - The structure of the JSON a repository should contain is located in - example-packages.json. - - :param repo: - The URL of the package repository - - :param package_manager: - An instance of :class:`PackageManager` used to download the file - """ - def __init__(self, repo, package_manager): - self.repo_info = None - self.repo = repo - self.package_manager = package_manager - self.unavailable_packages = [] - - def match_url(self): - """Indicates if this provider can handle the provided repo""" - - return True - - def fetch_repo(self): - """Retrieves and loads the JSON for other methods to use""" - - if self.repo_info != None: - return - - repository_json = self.package_manager.download_url(self.repo, - 'Error downloading repository.') - if repository_json == False: - self.repo_info = False - return - - try: - self.repo_info = json.loads(repository_json) - except (ValueError): - print '%s: Error parsing JSON from repository %s.' % (__name__, - self.repo) - self.repo_info = False - - def get_packages(self): - """ - Provides access to the repository info that is cached in a channel - - :return: - A dict in the format: - { - 'Package Name': { - # Package details - see example-packages.json for format - }, - ... - } - or False if there is an error - """ - - self.fetch_repo() - if self.repo_info == False: - return False - - output = {} - - for package in self.repo_info['packages']: - - platforms = package['platforms'].keys() - best_platform = self.get_best_platform(platforms) - - if not best_platform: - self.unavailable_packages.append(package['name']) - continue - - # Rewrites the legacy "zipball" URLs to the new "zip" format - downloads = package['platforms'][best_platform] - rewritten_downloads = [] - for download in downloads: - download['url'] = re.sub( - '^(https://nodeload.github.com/[^/]+/[^/]+/)zipball(/.*)$', - '\\1zip\\2', download['url']) - rewritten_downloads.append(download) - - info = { - 'name': package['name'], - 'description': package.get('description'), - 'url': package.get('homepage', self.repo), - 'author': package.get('author'), - 'last_modified': package.get('last_modified'), - 'downloads': rewritten_downloads - } - - output[package['name']] = info - - return output - - def get_renamed_packages(self): - """:return: A dict of the packages that have been renamed""" - - return self.repo_info.get('renamed_packages', {}) - - def get_unavailable_packages(self): - """ - Provides a list of packages that are unavailable for the current - platform/architecture that Sublime Text is running on. - - This list will be empty unless get_packages() is called first. - - :return: A list of package names - """ - - return self.unavailable_packages - - -class NonCachingProvider(): - """ - Base for package providers that do not need to cache the JSON - """ - - def fetch_json(self, url): - """ - Retrieves and parses the JSON from a URL - - :return: A dict or list from the JSON, or False on error - """ - - repository_json = self.package_manager.download_url(url, - 'Error downloading repository.') - if repository_json == False: - return False - try: - return json.loads(repository_json) - except (ValueError): - print '%s: Error parsing JSON from repository %s.' % (__name__, - url) - return False - - def get_unavailable_packages(self): - """ - Method for compatibility with PackageProvider class. These providers - are based on API calls, and thus do not support different platform - downloads, making it impossible for there to be unavailable packages. - - :return: An empty list - """ - - return [] - - -class GitHubPackageProvider(NonCachingProvider): - """ - Allows using a public GitHub repository as the source for a single package - - :param repo: - The public web URL to the GitHub repository. Should be in the format - `https://github.com/user/package` for the master branch, or - `https://github.com/user/package/tree/{branch_name}` for any other - branch. - - :param package_manager: - An instance of :class:`PackageManager` used to access the API - """ - - def __init__(self, repo, package_manager): - # Clean off the trailing .git to be more forgiving - self.repo = re.sub('\.git$', '', repo) - self.package_manager = package_manager - - def match_url(self): - """Indicates if this provider can handle the provided repo""" - - master = re.search('^https?://github.com/[^/]+/[^/]+/?$', self.repo) - branch = re.search('^https?://github.com/[^/]+/[^/]+/tree/[^/]+/?$', - self.repo) - return master != None or branch != None - - def get_packages(self): - """Uses the GitHub API to construct necessary info for a package""" - - branch = 'master' - branch_match = re.search( - '^https?://github.com/[^/]+/[^/]+/tree/([^/]+)/?$', self.repo) - if branch_match != None: - branch = branch_match.group(1) - - api_url = re.sub('^https?://github.com/([^/]+)/([^/]+)($|/.*$)', - 'https://api.github.com/repos/\\1/\\2', self.repo) - - repo_info = self.fetch_json(api_url) - if repo_info == False: - return False - - # In addition to hitting the main API endpoint for this repo, we - # also have to list the commits to get the timestamp of the last - # commit since we use that to generate a version number - commit_api_url = api_url + '/commits?' + \ - urllib.urlencode({'sha': branch, 'per_page': 1}) - - commit_info = self.fetch_json(commit_api_url) - if commit_info == False: - return False - - # We specifically use nodeload.github.com here because the download - # URLs all redirect there, and some of the downloaders don't follow - # HTTP redirect headers - download_url = 'https://nodeload.github.com/' + \ - repo_info['owner']['login'] + '/' + \ - repo_info['name'] + '/zip/' + urllib.quote(branch) - - commit_date = commit_info[0]['commit']['committer']['date'] - timestamp = datetime.datetime.strptime(commit_date[0:19], - '%Y-%m-%dT%H:%M:%S') - utc_timestamp = timestamp.strftime( - '%Y.%m.%d.%H.%M.%S') - - homepage = repo_info['homepage'] - if not homepage: - homepage = repo_info['html_url'] - - package = { - 'name': repo_info['name'], - 'description': repo_info['description'] if \ - repo_info['description'] else 'No description provided', - 'url': homepage, - 'author': repo_info['owner']['login'], - 'last_modified': timestamp.strftime('%Y-%m-%d %H:%M:%S'), - 'downloads': [ - { - 'version': utc_timestamp, - 'url': download_url - } - ] - } - return {package['name']: package} - - def get_renamed_packages(self): - """For API-compatibility with :class:`PackageProvider`""" - - return {} - - -class GitHubUserProvider(NonCachingProvider): - """ - Allows using a GitHub user/organization as the source for multiple packages - - :param repo: - The public web URL to the GitHub user/org. Should be in the format - `https://github.com/user`. - - :param package_manager: - An instance of :class:`PackageManager` used to access the API - """ - - def __init__(self, repo, package_manager): - self.repo = repo - self.package_manager = package_manager - - def match_url(self): - """Indicates if this provider can handle the provided repo""" - - return re.search('^https?://github.com/[^/]+/?$', self.repo) != None - - def get_packages(self): - """Uses the GitHub API to construct necessary info for all packages""" - - user_match = re.search('^https?://github.com/([^/]+)/?$', self.repo) - user = user_match.group(1) - - api_url = 'https://api.github.com/users/%s/repos?per_page=100' % user - - repo_info = self.fetch_json(api_url) - if repo_info == False: - return False - - packages = {} - for package_info in repo_info: - # All packages for the user are made available, and always from - # the master branch. Anything else requires a custom packages.json - commit_api_url = ('https://api.github.com/repos/%s/%s/commits' + \ - '?sha=master&per_page=1') % (user, package_info['name']) - - commit_info = self.fetch_json(commit_api_url) - if commit_info == False: - return False - - commit_date = commit_info[0]['commit']['committer']['date'] - timestamp = datetime.datetime.strptime(commit_date[0:19], - '%Y-%m-%dT%H:%M:%S') - utc_timestamp = timestamp.strftime( - '%Y.%m.%d.%H.%M.%S') - - homepage = package_info['homepage'] - if not homepage: - homepage = package_info['html_url'] - - package = { - 'name': package_info['name'], - 'description': package_info['description'] if \ - package_info['description'] else 'No description provided', - 'url': homepage, - 'author': package_info['owner']['login'], - 'last_modified': timestamp.strftime('%Y-%m-%d %H:%M:%S'), - 'downloads': [ - { - 'version': utc_timestamp, - # We specifically use nodeload.github.com here because - # the download URLs all redirect there, and some of the - # downloaders don't follow HTTP redirect headers - 'url': 'https://nodeload.github.com/' + \ - package_info['owner']['login'] + '/' + \ - package_info['name'] + '/zip/master' - } - ] - } - packages[package['name']] = package - return packages - - def get_renamed_packages(self): - """For API-compatibility with :class:`PackageProvider`""" - - return {} - - -class BitBucketPackageProvider(NonCachingProvider): - """ - Allows using a public BitBucket repository as the source for a single package - - :param repo: - The public web URL to the BitBucket repository. Should be in the format - `https://bitbucket.org/user/package`. - - :param package_manager: - An instance of :class:`PackageManager` used to access the API - """ - - def __init__(self, repo, package_manager): - self.repo = repo - self.package_manager = package_manager - - def match_url(self): - """Indicates if this provider can handle the provided repo""" - - return re.search('^https?://bitbucket.org', self.repo) != None - - def get_packages(self): - """Uses the BitBucket API to construct necessary info for a package""" - - api_url = re.sub('^https?://bitbucket.org/', - 'https://api.bitbucket.org/1.0/repositories/', self.repo) - api_url = api_url.rstrip('/') - - repo_info = self.fetch_json(api_url) - if repo_info == False: - return False - - # Since HG allows for arbitrary main branch names, we have to hit - # this URL just to get that info - main_branch_url = api_url + '/main-branch/' - main_branch_info = self.fetch_json(main_branch_url) - if main_branch_info == False: - return False - - # Grabbing the changesets is necessary because we construct the - # version number from the last commit timestamp - changeset_url = api_url + '/changesets/' + main_branch_info['name'] - last_commit = self.fetch_json(changeset_url) - if last_commit == False: - return False - - commit_date = last_commit['timestamp'] - timestamp = datetime.datetime.strptime(commit_date[0:19], - '%Y-%m-%d %H:%M:%S') - utc_timestamp = timestamp.strftime( - '%Y.%m.%d.%H.%M.%S') - - homepage = repo_info['website'] - if not homepage: - homepage = self.repo - package = { - 'name': repo_info['name'], - 'description': repo_info['description'] if \ - repo_info['description'] else 'No description provided', - 'url': homepage, - 'author': repo_info['owner'], - 'last_modified': timestamp.strftime('%Y-%m-%d %H:%M:%S'), - 'downloads': [ - { - 'version': utc_timestamp, - 'url': self.repo + '/get/' + \ - last_commit['node'] + '.zip' - } - ] - } - return {package['name']: package} - - def get_renamed_packages(self): - """For API-compatibility with :class:`PackageProvider`""" - - return {} - - -# The providers (in order) to check when trying to download repository info -_package_providers = [BitBucketPackageProvider, GitHubPackageProvider, - GitHubUserProvider, PackageProvider] - - -class BinaryNotFoundError(Exception): - """If a necessary executable is not found in the PATH on the system""" - - pass - - -class NonCleanExitError(Exception): - """ - When an subprocess does not exit cleanly - - :param returncode: - The command line integer return code of the subprocess - """ - - def __init__(self, returncode): - self.returncode = returncode - - def __str__(self): - return repr(self.returncode) - - -class NonHttpError(Exception): - """If a downloader had a non-clean exit, but it was not due to an HTTP error""" - - pass - - -class Downloader(): - """ - A base downloader that actually performs downloading URLs - - The SSL module is not included with the bundled Python for Linux - users of Sublime Text, so Linux machines will fall back to using curl - or wget for HTTPS URLs. - """ - - def check_certs(self, domain, timeout): - """ - Ensures that the SSL CA cert for a domain is present on the machine - - :param domain: - The domain to ensure there is a CA cert for - - :param timeout: - The int timeout for downloading the CA cert from the channel - - :return: - The CA cert bundle path on success, or False on error - """ - - cert_match = False - - certs_list = self.settings.get('certs', {}) - certs_dir = os.path.join(sublime.packages_path(), 'Package Control', - 'certs') - ca_bundle_path = os.path.join(certs_dir, 'ca-bundle.crt') - - cert_info = certs_list.get(domain) - if cert_info: - cert_match = self.locate_cert(certs_dir, cert_info[0], cert_info[1]) - - wildcard_info = certs_list.get('*') - if wildcard_info: - cert_match = self.locate_cert(certs_dir, wildcard_info[0], wildcard_info[1]) or cert_match - - if not cert_match: - print '%s: No CA certs available for %s.' % (__name__, domain) - return False - - return ca_bundle_path - - def locate_cert(self, certs_dir, cert_id, location): - """ - Makes sure the SSL cert specified has been added to the CA cert - bundle that is present on the machine - - :param certs_dir: - The path of the folder that contains the cert files - - :param cert_id: - The identifier for CA cert(s). For those provided by the channel - system, this will be an md5 of the contents of the cert(s). For - user-provided certs, this is something they provide. - - :param location: - An http(s) URL, or absolute filesystem path to the CA cert(s) - - :return: - If the cert specified (by cert_id) is present on the machine and - part of the ca-bundle.crt file in the certs_dir - """ - - cert_path = os.path.join(certs_dir, cert_id) - if not os.path.exists(cert_path): - if str(location) != '': - if re.match('^https?://', location): - contents = self.download_cert(cert_id, location) - else: - contents = self.load_cert(cert_id, location) - if contents: - self.save_cert(certs_dir, cert_id, contents) - return True - return False - return True - - def download_cert(self, cert_id, url): - """ - Downloads CA cert(s) from a URL - - :param cert_id: - The identifier for CA cert(s). For those provided by the channel - system, this will be an md5 of the contents of the cert(s). For - user-provided certs, this is something they provide. - - :param url: - An http(s) URL to the CA cert(s) - - :return: - The contents of the CA cert(s) - """ - - cert_downloader = self.__class__(self.settings) - return cert_downloader.download(url, - 'Error downloading CA certs for %s.' % (domain), timeout, 1) - - def load_cert(self, cert_id, path): - """ - Copies CA cert(s) from a file path - - :param cert_id: - The identifier for CA cert(s). For those provided by the channel - system, this will be an md5 of the contents of the cert(s). For - user-provided certs, this is something they provide. - - :param path: - The absolute filesystem path to a file containing the CA cert(s) - - :return: - The contents of the CA cert(s) - """ - - if os.path.exists(path): - with open(path, 'rb') as f: - return f.read() - - def save_cert(self, certs_dir, cert_id, contents): - """ - Saves CA cert(s) to the certs_dir (and ca-bundle.crt file) - - :param certs_dir: - The path of the folder that contains the cert files - - :param cert_id: - The identifier for CA cert(s). For those provided by the channel - system, this will be an md5 of the contents of the cert(s). For - user-provided certs, this is something they provide. - - :param contents: - The contents of the CA cert(s) - """ - - ca_bundle_path = os.path.join(certs_dir, 'ca-bundle.crt') - cert_path = os.path.join(certs_dir, cert_id) - with open(cert_path, 'wb') as f: - f.write(contents) - with open(ca_bundle_path, 'ab') as f: - f.write("\n" + contents) - - def decode_response(self, encoding, response): - if encoding == 'gzip': - return gzip.GzipFile(fileobj=StringIO.StringIO(response)).read() - elif encoding == 'deflate': - decompresser = zlib.decompressobj(-zlib.MAX_WBITS) - return decompresser.decompress(response) + decompresser.flush() - return response - - -class CliDownloader(Downloader): - """ - Base for downloaders that use a command line program - - :param settings: - A dict of the various Package Control settings. The Sublime Text - Settings API is not used because this code is run in a thread. - """ - - def __init__(self, settings): - self.settings = settings - - def clean_tmp_file(self): - if os.path.exists(self.tmp_file): - os.remove(self.tmp_file) - - def find_binary(self, name): - """ - Finds the given executable name in the system PATH - - :param name: - The exact name of the executable to find - - :return: - The absolute path to the executable - - :raises: - BinaryNotFoundError when the executable can not be found - """ - - for dir in os.environ['PATH'].split(os.pathsep): - path = os.path.join(dir, name) - if os.path.exists(path): - return path - - raise BinaryNotFoundError('The binary %s could not be located' % name) - - def execute(self, args): - """ - Runs the executable and args and returns the result - - :param args: - A list of the executable path and all arguments to be passed to it - - :return: - The text output of the executable - - :raises: - NonCleanExitError when the executable exits with an error - """ - - if self.settings.get('debug'): - print u"%s: Trying to execute command %s" % ( - __name__, create_cmd(args)) - - proc = subprocess.Popen(args, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.PIPE) - - output = proc.stdout.read() - self.stderr = proc.stderr.read() - returncode = proc.wait() - if returncode != 0: - error = NonCleanExitError(returncode) - error.stderr = self.stderr - error.stdout = output - raise error - return output - - -class UrlLib2Downloader(Downloader): - """ - A downloader that uses the Python urllib2 module - - :param settings: - A dict of the various Package Control settings. The Sublime Text - Settings API is not used because this code is run in a thread. - """ - - def __init__(self, settings): - self.settings = settings - - def download(self, url, error_message, timeout, tries): - """ - Downloads a URL and returns the contents - - Uses the proxy settings from the Package Control.sublime-settings file, - however there seem to be a decent number of proxies that this code - does not work with. Patches welcome! - - :param url: - The URL to download - - :param error_message: - A string to include in the console error that is printed - when an error occurs - - :param timeout: - The int number of seconds to set the timeout to - - :param tries: - The int number of times to try and download the URL in the case of - a timeout or HTTP 503 error - - :return: - The string contents of the URL, or False on error - """ - - http_proxy = self.settings.get('http_proxy') - https_proxy = self.settings.get('https_proxy') - if http_proxy or https_proxy: - proxies = {} - if http_proxy: - proxies['http'] = http_proxy - if https_proxy: - proxies['https'] = https_proxy - proxy_handler = urllib2.ProxyHandler(proxies) - else: - proxy_handler = urllib2.ProxyHandler() - - password_manager = urllib2.HTTPPasswordMgrWithDefaultRealm() - proxy_username = self.settings.get('proxy_username') - proxy_password = self.settings.get('proxy_password') - if proxy_username and proxy_password: - if http_proxy: - password_manager.add_password(None, http_proxy, proxy_username, - proxy_password) - if https_proxy: - password_manager.add_password(None, https_proxy, proxy_username, - proxy_password) - - handlers = [proxy_handler] - if os.name == 'nt': - ntlm_auth_handler = ProxyNtlmAuthHandler(password_manager) - handlers.append(ntlm_auth_handler) - - basic_auth_handler = urllib2.ProxyBasicAuthHandler(password_manager) - digest_auth_handler = urllib2.ProxyDigestAuthHandler(password_manager) - handlers.extend([digest_auth_handler, basic_auth_handler]) - - debug = self.settings.get('debug') - - if debug: - print u"%s: Urllib2 Debug Proxy" % __name__ - print u" http_proxy: %s" % http_proxy - print u" https_proxy: %s" % https_proxy - print u" proxy_username: %s" % proxy_username - print u" proxy_password: %s" % proxy_password - - secure_url_match = re.match('^https://([^/]+)', url) - if secure_url_match != None: - secure_domain = secure_url_match.group(1) - bundle_path = self.check_certs(secure_domain, timeout) - if not bundle_path: - return False - bundle_path = bundle_path.encode(sys.getfilesystemencoding()) - handlers.append(ValidatingHTTPSHandler(ca_certs=bundle_path, - debug=debug, passwd=password_manager, - user_agent=self.settings.get('user_agent'))) - else: - handlers.append(DebuggableHTTPHandler(debug=debug, - passwd=password_manager)) - urllib2.install_opener(urllib2.build_opener(*handlers)) - - while tries > 0: - tries -= 1 - try: - request = urllib2.Request(url, headers={ - "User-Agent": self.settings.get('user_agent'), - # Don't be alarmed if the response from the server does not - # select one of these since the server runs a relatively new - # version of OpenSSL which supports compression on the SSL - # layer, and Apache will use that instead of HTTP-level - # encoding. - "Accept-Encoding": "gzip,deflate"}) - http_file = urllib2.urlopen(request, timeout=timeout) - self.handle_rate_limit(http_file, url) - result = http_file.read() - encoding = http_file.headers.get('Content-Encoding') - return self.decode_response(encoding, result) - - except (httplib.HTTPException) as (e): - print '%s: %s HTTP exception %s (%s) downloading %s.' % ( - __name__, error_message, e.__class__.__name__, - unicode_from_os(e), url) - - except (urllib2.HTTPError) as (e): - # Make sure we obey Github's rate limiting headers - self.handle_rate_limit(e, url) - - # Bitbucket and Github return 503 a decent amount - if unicode_from_os(e.code) == '503': - print ('%s: Downloading %s was rate limited, ' + - 'trying again') % (__name__, url) - continue - print '%s: %s HTTP error %s downloading %s.' % (__name__, - error_message, unicode_from_os(e.code), url) - - except (urllib2.URLError) as (e): - - # Bitbucket and Github timeout a decent amount - if unicode_from_os(e.reason) == 'The read operation timed out' \ - or unicode_from_os(e.reason) == 'timed out': - print (u'%s: Downloading %s timed out, trying ' + - u'again') % (__name__, url) - continue - print u'%s: %s URL error %s downloading %s.' % (__name__, - error_message, unicode_from_os(e.reason), url) - break - return False - - def handle_rate_limit(self, response, url): - """ - Checks the headers of a respone object to make sure we are obeying the - rate limit - - :param response: - The response object that has a headers dict - - :param url: - The URL that was requested - - :raises: - RateLimitException when the rate limit has been hit - """ - - limit_remaining = response.headers.get('X-RateLimit-Remaining', 1) - if str(limit_remaining) == '0': - hostname = urlparse.urlparse(url).hostname - limit = response.headers.get('X-RateLimit-Limit', 1) - raise RateLimitException(hostname, limit) - - -class WgetDownloader(CliDownloader): - """ - A downloader that uses the command line program wget - - :param settings: - A dict of the various Package Control settings. The Sublime Text - Settings API is not used because this code is run in a thread. - """ - - def __init__(self, settings): - self.settings = settings - self.debug = settings.get('debug') - self.wget = self.find_binary('wget') - - def download(self, url, error_message, timeout, tries): - """ - Downloads a URL and returns the contents - - :param url: - The URL to download - - :param error_message: - A string to include in the console error that is printed - when an error occurs - - :param timeout: - The int number of seconds to set the timeout to - - :param tries: - The int number of times to try and download the URL in the case of - a timeout or HTTP 503 error - - :return: - The string contents of the URL, or False on error - """ - - if not self.wget: - return False - - self.tmp_file = tempfile.NamedTemporaryFile().name - command = [self.wget, '--connect-timeout=' + str(int(timeout)), '-o', - self.tmp_file, '-O', '-', '-U', - self.settings.get('user_agent'), '--header', - # Don't be alarmed if the response from the server does not select - # one of these since the server runs a relatively new version of - # OpenSSL which supports compression on the SSL layer, and Apache - # will use that instead of HTTP-level encoding. - 'Accept-Encoding: gzip,deflate'] - - secure_url_match = re.match('^https://([^/]+)', url) - if secure_url_match != None: - secure_domain = secure_url_match.group(1) - bundle_path = self.check_certs(secure_domain, timeout) - if not bundle_path: - return False - command.append(u'--ca-certificate=' + bundle_path) - - if self.debug: - command.append('-d') - else: - command.append('-S') - - http_proxy = self.settings.get('http_proxy') - https_proxy = self.settings.get('https_proxy') - proxy_username = self.settings.get('proxy_username') - proxy_password = self.settings.get('proxy_password') - - if proxy_username: - command.append(u"--proxy-user=%s" % proxy_username) - if proxy_password: - command.append(u"--proxy-password=%s" % proxy_password) - - if self.debug: - print u"%s: Wget Debug Proxy" % __name__ - print u" http_proxy: %s" % http_proxy - print u" https_proxy: %s" % https_proxy - print u" proxy_username: %s" % proxy_username - print u" proxy_password: %s" % proxy_password - - command.append(url) - - if http_proxy: - os.putenv('http_proxy', http_proxy) - if https_proxy: - os.putenv('https_proxy', https_proxy) - - while tries > 0: - tries -= 1 - try: - result = self.execute(command) - - general, headers = self.parse_output() - encoding = headers.get('content-encoding') - if encoding: - result = self.decode_response(encoding, result) - - return result - - except (NonCleanExitError) as (e): - - try: - general, headers = self.parse_output() - self.handle_rate_limit(headers, url) - - if general['status'] == '503': - # GitHub and BitBucket seem to rate limit via 503 - print ('%s: Downloading %s was rate limited' + - ', trying again') % (__name__, url) - continue - - error_string = 'HTTP error %s %s' % (general['status'], - general['message']) - - except (NonHttpError) as (e): - - error_string = unicode_from_os(e) - - # GitHub and BitBucket seem to time out a lot - if error_string.find('timed out') != -1: - print ('%s: Downloading %s timed out, ' + - 'trying again') % (__name__, url) - continue - - print (u'%s: %s %s downloading %s.' % (__name__, error_message, - error_string, url)).encode('UTF-8') - - break - return False - - def parse_output(self): - with open(self.tmp_file, 'r') as f: - output = f.read().splitlines() - self.clean_tmp_file() - - error = None - header_lines = [] - if self.debug: - section = 'General' - last_section = None - for line in output: - if section == 'General': - if self.skippable_line(line): - continue - - # Skip blank lines - if line.strip() == '': - continue - - # Error lines - if line[0:5] == 'wget:': - error = line[5:].strip() - if line[0:7] == 'failed:': - error = line[7:].strip() - - if line == '---request begin---': - section = 'Write' - continue - elif line == '---request end---': - section = 'General' - continue - elif line == '---response begin---': - section = 'Read' - continue - elif line == '---response end---': - section = 'General' - continue - - if section != last_section: - print "%s: Wget HTTP Debug %s" % (__name__, section) - - if section == 'Read': - header_lines.append(line) - - print ' ' + line - last_section = section - - else: - for line in output: - if self.skippable_line(line): - continue - - # Check the resolving and connecting to lines for errors - if re.match('(Resolving |Connecting to )', line): - failed_match = re.search(' failed: (.*)$', line) - if failed_match: - error = failed_match.group(1).strip() - - # Error lines - if line[0:5] == 'wget:': - error = line[5:].strip() - if line[0:7] == 'failed:': - error = line[7:].strip() - - if line[0:2] == ' ': - header_lines.append(line.lstrip()) - - if error: - raise NonHttpError(error) - - return self.parse_headers(header_lines) - - def skippable_line(self, line): - # Skip date lines - if re.match('--\d{4}-\d{2}-\d{2}', line): - return True - if re.match('\d{4}-\d{2}-\d{2}', line): - return True - # Skip HTTP status code lines since we already have that info - if re.match('\d{3} ', line): - return True - # Skip Saving to and progress lines - if re.match('(Saving to:|\s*\d+K)', line): - return True - # Skip notice about ignoring body on HTTP error - if re.match('Skipping \d+ byte', line): - return True - - def parse_headers(self, output=None): - if not output: - with open(self.tmp_file, 'r') as f: - output = f.read().splitlines() - self.clean_tmp_file() - - general = { - 'version': '0.9', - 'status': '200', - 'message': 'OK' - } - headers = {} - for line in output: - # When using the -S option, headers have two spaces before them, - # additionally, valid headers won't have spaces, so this is always - # a safe operation to perform - line = line.lstrip() - if line.find('HTTP/') == 0: - match = re.match('HTTP/(\d\.\d)\s+(\d+)\s+(.*)$', line) - general['version'] = match.group(1) - general['status'] = match.group(2) - general['message'] = match.group(3) - else: - name, value = line.split(':', 1) - headers[name.lower()] = value.strip() - - return (general, headers) - - def handle_rate_limit(self, headers, url): - limit_remaining = headers.get('x-ratelimit-remaining', '1') - limit = headers.get('x-ratelimit-limit', '1') - - if str(limit_remaining) == '0': - hostname = urlparse.urlparse(url).hostname - raise RateLimitException(hostname, limit) - - -class CurlDownloader(CliDownloader): - """ - A downloader that uses the command line program curl - - :param settings: - A dict of the various Package Control settings. The Sublime Text - Settings API is not used because this code is run in a thread. - """ - - def __init__(self, settings): - self.settings = settings - self.curl = self.find_binary('curl') - - def download(self, url, error_message, timeout, tries): - """ - Downloads a URL and returns the contents - - :param url: - The URL to download - - :param error_message: - A string to include in the console error that is printed - when an error occurs - - :param timeout: - The int number of seconds to set the timeout to - - :param tries: - The int number of times to try and download the URL in the case of - a timeout or HTTP 503 error - - :return: - The string contents of the URL, or False on error - """ - - if not self.curl: - return False - - self.tmp_file = tempfile.NamedTemporaryFile().name - command = [self.curl, '--user-agent', self.settings.get('user_agent'), - '--connect-timeout', str(int(timeout)), '-sSL', - # Don't be alarmed if the response from the server does not select - # one of these since the server runs a relatively new version of - # OpenSSL which supports compression on the SSL layer, and Apache - # will use that instead of HTTP-level encoding. - '--compressed', - # We have to capture the headers to check for rate limit info - '--dump-header', self.tmp_file] - - secure_url_match = re.match('^https://([^/]+)', url) - if secure_url_match != None: - secure_domain = secure_url_match.group(1) - bundle_path = self.check_certs(secure_domain, timeout) - if not bundle_path: - return False - command.extend(['--cacert', bundle_path]) - - debug = self.settings.get('debug') - if debug: - command.append('-v') - - http_proxy = self.settings.get('http_proxy') - https_proxy = self.settings.get('https_proxy') - proxy_username = self.settings.get('proxy_username') - proxy_password = self.settings.get('proxy_password') - - if debug: - print u"%s: Curl Debug Proxy" % __name__ - print u" http_proxy: %s" % http_proxy - print u" https_proxy: %s" % https_proxy - print u" proxy_username: %s" % proxy_username - print u" proxy_password: %s" % proxy_password - - if http_proxy or https_proxy: - command.append('--proxy-anyauth') - - if proxy_username or proxy_password: - command.extend(['-U', u"%s:%s" % (proxy_username, proxy_password)]) - - if http_proxy: - os.putenv('http_proxy', http_proxy) - if https_proxy: - os.putenv('HTTPS_PROXY', https_proxy) - - command.append(url) - - while tries > 0: - tries -= 1 - try: - output = self.execute(command) - - with open(self.tmp_file, 'r') as f: - headers = f.read() - self.clean_tmp_file() - - limit = 1 - limit_remaining = 1 - status = '200 OK' - for header in headers.splitlines(): - if header[0:5] == 'HTTP/': - status = re.sub('^HTTP/\d\.\d\s+', '', header) - if header.lower()[0:22] == 'x-ratelimit-remaining:': - limit_remaining = header.lower()[22:].strip() - if header.lower()[0:18] == 'x-ratelimit-limit:': - limit = header.lower()[18:].strip() - - if debug: - self.print_debug(self.stderr) - - if str(limit_remaining) == '0': - hostname = urlparse.urlparse(url).hostname - raise RateLimitException(hostname, limit) - - if status != '200 OK': - e = NonCleanExitError(22) - e.stderr = status - raise e - - return output - - except (NonCleanExitError) as (e): - # Stderr is used for both the error message and the debug info - # so we need to process it to extra the debug info - if self.settings.get('debug'): - e.stderr = self.print_debug(e.stderr) - - self.clean_tmp_file() - - if e.returncode == 22: - code = re.sub('^.*?(\d+)([\w\s]+)?$', '\\1', e.stderr) - if code == '503': - # GitHub and BitBucket seem to rate limit via 503 - print ('%s: Downloading %s was rate limited' + - ', trying again') % (__name__, url) - continue - error_string = 'HTTP error ' + code - elif e.returncode == 6: - error_string = 'URL error host not found' - elif e.returncode == 28: - # GitHub and BitBucket seem to time out a lot - print ('%s: Downloading %s timed out, trying ' + - 'again') % (__name__, url) - continue - else: - error_string = e.stderr.rstrip() - - print '%s: %s %s downloading %s.' % (__name__, error_message, - error_string, url) - break - - return False - - def print_debug(self, string): - section = 'General' - last_section = None - - output = '' - - for line in string.splitlines(): - # Placeholder for body of request - if line and line[0:2] == '{ ': - continue - - if len(line) > 1: - subtract = 0 - if line[0:2] == '* ': - section = 'General' - subtract = 2 - elif line[0:2] == '> ': - section = 'Write' - subtract = 2 - elif line[0:2] == '< ': - section = 'Read' - subtract = 2 - line = line[subtract:] - - # If the line does not start with "* ", "< ", "> " or " " - # then it is a real stderr message - if subtract == 0 and line[0:2] != ' ': - output += line - continue - - if line.strip() == '': - continue - - if section != last_section: - print "%s: Curl HTTP Debug %s" % (__name__, section) - - print ' ' + line - last_section = section - - return output - - -# A cache of channel and repository info to allow users to install multiple -# packages without having to wait for the metadata to be downloaded more -# than once. The keys are managed locally by the utilizing code. -_channel_repository_cache = {} - - -class RepositoryDownloader(threading.Thread): - """ - Downloads information about a repository in the background - - :param package_manager: - An instance of :class:`PackageManager` used to download files - - :param name_map: - The dict of name mapping for URL slug -> package name - - :param repo: - The URL of the repository to download info about - """ - - def __init__(self, package_manager, name_map, repo): - self.package_manager = package_manager - self.repo = repo - self.packages = {} - self.name_map = name_map - threading.Thread.__init__(self) - - def run(self): - for provider_class in _package_providers: - provider = provider_class(self.repo, self.package_manager) - if provider.match_url(): - break - packages = provider.get_packages() - if packages == False: - self.packages = False - return - - mapped_packages = {} - for package in packages.keys(): - mapped_package = self.name_map.get(package, package) - mapped_packages[mapped_package] = packages[package] - mapped_packages[mapped_package]['name'] = mapped_package - packages = mapped_packages - - self.packages = packages - self.renamed_packages = provider.get_renamed_packages() - self.unavailable_packages = provider.get_unavailable_packages() - - -class VcsUpgrader(): - """ - Base class for updating packages that are a version control repository on local disk - - :param vcs_binary: - The full filesystem path to the executable for the version control - system. May be set to None to allow the code to try and find it. - - :param update_command: - The command to pass to the version control executable to update the - repository. - - :param working_copy: - The local path to the working copy/package directory - - :param cache_length: - The lenth of time to cache if incoming changesets are available - """ - - def __init__(self, vcs_binary, update_command, working_copy, cache_length, debug): - self.binary = vcs_binary - self.update_command = update_command - self.working_copy = working_copy - self.cache_length = cache_length - self.debug = debug - - def execute(self, args, dir): - """ - Creates a subprocess with the executable/args - - :param args: - A list of the executable path and all arguments to it - - :param dir: - The directory in which to run the executable - - :return: A string of the executable output - """ - - startupinfo = None - if os.name == 'nt': - startupinfo = subprocess.STARTUPINFO() - startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW - - if self.debug: - print u"%s: Trying to execute command %s" % ( - __name__, create_cmd(args)) - proc = subprocess.Popen(args, stdin=subprocess.PIPE, - stdout=subprocess.PIPE, stderr=subprocess.STDOUT, - startupinfo=startupinfo, cwd=dir) - - return proc.stdout.read().replace('\r\n', '\n').rstrip(' \n\r') - - def find_binary(self, name): - """ - Locates the executable by looking in the PATH and well-known directories - - :param name: - The string filename of the executable - - :return: The filesystem path to the executable, or None if not found - """ - - if self.binary: - if self.debug: - print u"%s: Using \"%s_binary\" from settings \"%s\"" % ( - __name__, self.vcs_type, self.binary) - return self.binary - - # Try the path first - for dir in os.environ['PATH'].split(os.pathsep): - path = os.path.join(dir, name) - if os.path.exists(path): - if self.debug: - print u"%s: Found %s at \"%s\"" % (__name__, self.vcs_type, - path) - return path - - # This is left in for backwards compatibility and for windows - # users who may have the binary, albeit in a common dir that may - # not be part of the PATH - if os.name == 'nt': - dirs = ['C:\\Program Files\\Git\\bin', - 'C:\\Program Files (x86)\\Git\\bin', - 'C:\\Program Files\\TortoiseGit\\bin', - 'C:\\Program Files\\Mercurial', - 'C:\\Program Files (x86)\\Mercurial', - 'C:\\Program Files (x86)\\TortoiseHg', - 'C:\\Program Files\\TortoiseHg', - 'C:\\cygwin\\bin'] - else: - dirs = ['/usr/local/git/bin'] - - for dir in dirs: - path = os.path.join(dir, name) - if os.path.exists(path): - if self.debug: - print u"%s: Found %s at \"%s\"" % (__name__, self.vcs_type, - path) - return path - - if self.debug: - print u"%s: Could not find %s on your machine" % (__name__, - self.vcs_type) - return None - - -class GitUpgrader(VcsUpgrader): - """ - Allows upgrading a local git-repository-based package - """ - - vcs_type = 'git' - - def retrieve_binary(self): - """ - Returns the path to the git executable - - :return: The string path to the executable or False on error - """ - - name = 'git' - if os.name == 'nt': - name += '.exe' - binary = self.find_binary(name) - if binary and os.path.isdir(binary): - full_path = os.path.join(binary, name) - if os.path.exists(full_path): - binary = full_path - if not binary: - sublime.error_message(('%s: Unable to find %s. ' + - 'Please set the git_binary setting by accessing the ' + - 'Preferences > Package Settings > %s > ' + - u'Settings – User menu entry. The Settings – Default entry ' + - 'can be used for reference, but changes to that will be ' + - 'overwritten upon next upgrade.') % (__name__, name, __name__)) - return False - - if os.name == 'nt': - tortoise_plink = self.find_binary('TortoisePlink.exe') - if tortoise_plink: - os.environ.setdefault('GIT_SSH', tortoise_plink) - return binary - - def run(self): - """ - Updates the repository with remote changes - - :return: False or error, or True on success - """ - - binary = self.retrieve_binary() - if not binary: - return False - args = [binary] - args.extend(self.update_command) - self.execute(args, self.working_copy) - return True - - def incoming(self): - """:return: bool if remote revisions are available""" - - cache_key = self.working_copy + '.incoming' - working_copy_cache = _channel_repository_cache.get(cache_key) - if working_copy_cache and working_copy_cache.get('time') > \ - time.time(): - return working_copy_cache.get('data') - - binary = self.retrieve_binary() - if not binary: - return False - self.execute([binary, 'fetch'], self.working_copy) - args = [binary, 'log'] - args.append('..' + '/'.join(self.update_command[-2:])) - output = self.execute(args, self.working_copy) - incoming = len(output) > 0 - - _channel_repository_cache[cache_key] = { - 'time': time.time() + self.cache_length, - 'data': incoming - } - return incoming - - -class HgUpgrader(VcsUpgrader): - """ - Allows upgrading a local mercurial-repository-based package - """ - - vcs_type = 'hg' - - def retrieve_binary(self): - """ - Returns the path to the hg executable - - :return: The string path to the executable or False on error - """ - - name = 'hg' - if os.name == 'nt': - name += '.exe' - binary = self.find_binary(name) - if binary and os.path.isdir(binary): - full_path = os.path.join(binary, name) - if os.path.exists(full_path): - binary = full_path - if not binary: - sublime.error_message(('%s: Unable to find %s. ' + - 'Please set the hg_binary setting by accessing the ' + - 'Preferences > Package Settings > %s > ' + - u'Settings – User menu entry. The Settings – Default entry ' + - 'can be used for reference, but changes to that will be ' + - 'overwritten upon next upgrade.') % (__name__, name, __name__)) - return False - return binary - - def run(self): - """ - Updates the repository with remote changes - - :return: False or error, or True on success - """ - - binary = self.retrieve_binary() - if not binary: - return False - args = [binary] - args.extend(self.update_command) - self.execute(args, self.working_copy) - return True - - def incoming(self): - """:return: bool if remote revisions are available""" - - cache_key = self.working_copy + '.incoming' - working_copy_cache = _channel_repository_cache.get(cache_key) - if working_copy_cache and working_copy_cache.get('time') > \ - time.time(): - return working_copy_cache.get('data') - - binary = self.retrieve_binary() - if not binary: - return False - args = [binary, 'in', '-q'] - args.append(self.update_command[-1]) - output = self.execute(args, self.working_copy) - incoming = len(output) > 0 - - _channel_repository_cache[cache_key] = { - 'time': time.time() + self.cache_length, - 'data': incoming - } - return incoming - - -def clear_directory(directory, ignore_paths=None): - was_exception = False - for root, dirs, files in os.walk(directory, topdown=False): - paths = [os.path.join(root, f) for f in files] - paths.extend([os.path.join(root, d) for d in dirs]) - - for path in paths: - try: - # Don't delete the metadata file, that way we have it - # when the reinstall happens, and the appropriate - # usage info can be sent back to the server - if ignore_paths and path in ignore_paths: - continue - if os.path.isdir(path): - os.rmdir(path) - else: - os.remove(path) - except (OSError, IOError) as (e): - was_exception = True - - return not was_exception - - - -class PackageManager(): - """ - Allows downloading, creating, installing, upgrading, and deleting packages - - Delegates metadata retrieval to the _channel_providers and - _package_providers classes. Uses VcsUpgrader-based classes for handling - git and hg repositories in the Packages folder. Downloader classes are - utilized to fetch contents of URLs. - - Also handles displaying package messaging, and sending usage information to - the usage server. - """ - - def __init__(self): - # Here we manually copy the settings since sublime doesn't like - # code accessing settings from threads - self.settings = {} - settings = sublime.load_settings(__name__ + '.sublime-settings') - for setting in ['timeout', 'repositories', 'repository_channels', - 'package_name_map', 'dirs_to_ignore', 'files_to_ignore', - 'package_destination', 'cache_length', 'auto_upgrade', - 'files_to_ignore_binary', 'files_to_keep', 'dirs_to_keep', - 'git_binary', 'git_update_command', 'hg_binary', - 'hg_update_command', 'http_proxy', 'https_proxy', - 'auto_upgrade_ignore', 'auto_upgrade_frequency', - 'submit_usage', 'submit_url', 'renamed_packages', - 'files_to_include', 'files_to_include_binary', 'certs', - 'ignore_vcs_packages', 'proxy_username', 'proxy_password', - 'debug', 'user_agent']: - if settings.get(setting) == None: - continue - self.settings[setting] = settings.get(setting) - - # https_proxy will inherit from http_proxy unless it is set to a - # string value or false - no_https_proxy = self.settings.get('https_proxy') in ["", None] - if no_https_proxy and self.settings.get('http_proxy'): - self.settings['https_proxy'] = self.settings.get('http_proxy') - if self.settings['https_proxy'] == False: - self.settings['https_proxy'] = '' - - self.settings['platform'] = sublime.platform() - self.settings['version'] = sublime.version() - - def compare_versions(self, version1, version2): - """ - Compares to version strings to see which is greater - - Date-based version numbers (used by GitHub and BitBucket providers) - are automatically pre-pended with a 0 so they are always less than - version 1.0. - - :return: - -1 if version1 is less than version2 - 0 if they are equal - 1 if version1 is greater than version2 - """ - - def date_compat(v): - # We prepend 0 to all date-based version numbers so that developers - # may switch to explicit versioning from GitHub/BitBucket - # versioning based on commit dates - date_match = re.match('(\d{4})\.(\d{2})\.(\d{2})\.(\d{2})\.(\d{2})\.(\d{2})$', v) - if date_match: - v = '0.%s.%s.%s.%s.%s.%s' % date_match.groups() - return v - - def semver_compat(v): - # When translating dates into semver, the way to get each date - # segment into the version is to treat the year and month as - # minor and patch, and then the rest as a numeric build version - # with four different parts. The result looks like: - # 0.2012.11+10.31.23.59 - date_match = re.match('(\d{4}(?:\.\d{2}){2})\.(\d{2}(?:\.\d{2}){3})$', v) - if date_match: - v = '%s+%s' % (date_match.group(1), date_match.group(2)) - - # Semver must have major, minor, patch - elif re.match('^\d+$', v): - v += '.0.0' - elif re.match('^\d+\.\d+$', v): - v += '.0' - return v - - def cmp_compat(v): - return [int(x) for x in re.sub(r'(\.0+)*$', '', v).split(".")] - - version1 = date_compat(version1) - version2 = date_compat(version2) - try: - return semver.compare(semver_compat(version1), semver_compat(version2)) - except (ValueError): - return cmp(cmp_compat(version1), cmp_compat(version2)) - - def download_url(self, url, error_message): - """ - Downloads a URL and returns the contents - - :param url: - The string URL to download - - :param error_message: - The error message to include if the download fails - - :return: - The string contents of the URL, or False on error - """ - - has_ssl = 'ssl' in sys.modules and hasattr(urllib2, 'HTTPSHandler') - is_ssl = re.search('^https://', url) != None - downloader = None - - if (is_ssl and has_ssl) or not is_ssl: - downloader = UrlLib2Downloader(self.settings) - else: - for downloader_class in [CurlDownloader, WgetDownloader]: - try: - downloader = downloader_class(self.settings) - break - except (BinaryNotFoundError): - pass - - if not downloader: - sublime.error_message(('%s: Unable to download %s due to no ' + - 'ssl module available and no capable program found. Please ' + - 'install curl or wget.') % (__name__, url)) - return False - - url = url.replace(' ', '%20') - hostname = urlparse.urlparse(url).hostname.lower() - timeout = self.settings.get('timeout', 3) - - rate_limited_cache = _channel_repository_cache.get('rate_limited_domains', {}) - if rate_limited_cache.get('time') and rate_limited_cache.get('time') > time.time(): - rate_limited_domains = rate_limited_cache.get('data', []) - else: - rate_limited_domains = [] - - if self.settings.get('debug'): - try: - ip = socket.gethostbyname(hostname) - except (socket.gaierror) as (e): - ip = unicode_from_os(e) - - print u"%s: Download Debug" % __name__ - print u" URL: %s" % url - print u" Resolved IP: %s" % ip - print u" Timeout: %s" % str(timeout) - - if hostname in rate_limited_domains: - if self.settings.get('debug'): - print u" Skipping due to hitting rate limit for %s" % hostname - return False - - try: - return downloader.download(url, error_message, timeout, 3) - except (RateLimitException) as (e): - - rate_limited_domains.append(hostname) - _channel_repository_cache['rate_limited_domains'] = { - 'data': rate_limited_domains, - 'time': time.time() + self.settings.get('cache_length', - 300) - } - - print ('%s: Hit rate limit of %s for %s, skipping all futher ' + - 'download requests for this domain') % (__name__, - e.limit, e.host) - return False - - def get_metadata(self, package): - """ - Returns the package metadata for an installed package - - :return: - A dict with the keys: - version - url - description - or an empty dict on error - """ - - metadata_filename = os.path.join(self.get_package_dir(package), - 'package-metadata.json') - if os.path.exists(metadata_filename): - with open(metadata_filename) as f: - try: - return json.load(f) - except (ValueError): - return {} - return {} - - def list_repositories(self): - """ - Returns a master list of all repositories pulled from all sources - - These repositories come from the channels specified in the - "repository_channels" setting, plus any repositories listed in the - "repositories" setting. - - :return: - A list of all available repositories - """ - - repositories = self.settings.get('repositories') - repository_channels = self.settings.get('repository_channels') - for channel in repository_channels: - channel = channel.strip() - - channel_repositories = None - - # Caches various info from channels for performance - cache_key = channel + '.repositories' - repositories_cache = _channel_repository_cache.get(cache_key) - if repositories_cache and repositories_cache.get('time') > \ - time.time(): - channel_repositories = repositories_cache.get('data') - - name_map_cache_key = channel + '.package_name_map' - name_map_cache = _channel_repository_cache.get( - name_map_cache_key) - if name_map_cache and name_map_cache.get('time') > \ - time.time(): - name_map = name_map_cache.get('data') - name_map.update(self.settings.get('package_name_map', {})) - self.settings['package_name_map'] = name_map - - renamed_cache_key = channel + '.renamed_packages' - renamed_cache = _channel_repository_cache.get( - renamed_cache_key) - if renamed_cache and renamed_cache.get('time') > \ - time.time(): - renamed_packages = renamed_cache.get('data') - renamed_packages.update(self.settings.get('renamed_packages', - {})) - self.settings['renamed_packages'] = renamed_packages - - unavailable_cache_key = channel + '.unavailable_packages' - unavailable_cache = _channel_repository_cache.get( - unavailable_cache_key) - if unavailable_cache and unavailable_cache.get('time') > \ - time.time(): - unavailable_packages = unavailable_cache.get('data') - unavailable_packages.extend(self.settings.get('unavailable_packages', - [])) - self.settings['unavailable_packages'] = unavailable_packages - - certs_cache_key = channel + '.certs' - certs_cache = _channel_repository_cache.get(certs_cache_key) - if certs_cache and certs_cache.get('time') > time.time(): - certs = self.settings.get('certs', {}) - certs.update(certs_cache.get('data')) - self.settings['certs'] = certs - - # If any of the info was not retrieved from the cache, we need to - # grab the channel to get it - if channel_repositories == None or \ - self.settings.get('package_name_map') == None or \ - self.settings.get('renamed_packages') == None: - - for provider_class in _channel_providers: - provider = provider_class(channel, self) - if provider.match_url(): - break - - channel_repositories = provider.get_repositories() - - if channel_repositories == False: - continue - _channel_repository_cache[cache_key] = { - 'time': time.time() + self.settings.get('cache_length', - 300), - 'data': channel_repositories - } - - for repo in channel_repositories: - if provider.get_packages(repo) == False: - continue - packages_cache_key = repo + '.packages' - _channel_repository_cache[packages_cache_key] = { - 'time': time.time() + self.settings.get('cache_length', - 300), - 'data': provider.get_packages(repo) - } - - # Have the local name map override the one from the channel - name_map = provider.get_name_map() - name_map.update(self.settings.get('package_name_map', {})) - self.settings['package_name_map'] = name_map - _channel_repository_cache[name_map_cache_key] = { - 'time': time.time() + self.settings.get('cache_length', - 300), - 'data': name_map - } - - renamed_packages = provider.get_renamed_packages() - _channel_repository_cache[renamed_cache_key] = { - 'time': time.time() + self.settings.get('cache_length', - 300), - 'data': renamed_packages - } - if renamed_packages: - self.settings['renamed_packages'] = self.settings.get( - 'renamed_packages', {}) - self.settings['renamed_packages'].update(renamed_packages) - - unavailable_packages = provider.get_unavailable_packages() - _channel_repository_cache[unavailable_cache_key] = { - 'time': time.time() + self.settings.get('cache_length', - 300), - 'data': unavailable_packages - } - if unavailable_packages: - self.settings['unavailable_packages'] = self.settings.get( - 'unavailable_packages', []) - self.settings['unavailable_packages'].extend(unavailable_packages) - - certs = provider.get_certs() - _channel_repository_cache[certs_cache_key] = { - 'time': time.time() + self.settings.get('cache_length', - 300), - 'data': certs - } - if certs: - self.settings['certs'] = self.settings.get('certs', {}) - self.settings['certs'].update(certs) - - repositories.extend(channel_repositories) - return [repo.strip() for repo in repositories] - - def list_available_packages(self): - """ - Returns a master list of every available package from all sources - - :return: - A dict in the format: - { - 'Package Name': { - # Package details - see example-packages.json for format - }, - ... - } - """ - - repositories = self.list_repositories() - packages = {} - downloaders = [] - grouped_downloaders = {} - - # Repositories are run in reverse order so that the ones first - # on the list will overwrite those last on the list - for repo in repositories[::-1]: - repository_packages = None - - cache_key = repo + '.packages' - packages_cache = _channel_repository_cache.get(cache_key) - if packages_cache and packages_cache.get('time') > \ - time.time(): - repository_packages = packages_cache.get('data') - packages.update(repository_packages) - - if repository_packages == None: - downloader = RepositoryDownloader(self, - self.settings.get('package_name_map', {}), repo) - domain = re.sub('^https?://[^/]*?(\w+\.\w+)($|/.*$)', '\\1', - repo) - - # downloaders are grouped by domain so that multiple can - # be run in parallel without running into API access limits - if not grouped_downloaders.get(domain): - grouped_downloaders[domain] = [] - grouped_downloaders[domain].append(downloader) - - # Allows creating a separate named function for each downloader - # delay. Not having this contained in a function causes all of the - # schedules to reference the same downloader.start() - def schedule(downloader, delay): - downloader.has_started = False - - def inner(): - downloader.start() - downloader.has_started = True - sublime.set_timeout(inner, delay) - - # Grabs every repo grouped by domain and delays the start - # of each download from that domain by a fixed amount - for domain_downloaders in grouped_downloaders.values(): - for i in range(len(domain_downloaders)): - downloader = domain_downloaders[i] - downloaders.append(downloader) - schedule(downloader, i * 150) - - complete = [] - - # Wait for all of the downloaders to finish - while downloaders: - downloader = downloaders.pop() - if downloader.has_started: - downloader.join() - complete.append(downloader) - else: - downloaders.insert(0, downloader) - - # Grabs the results and stuff if all in the cache - for downloader in complete: - repository_packages = downloader.packages - if repository_packages == False: - continue - cache_key = downloader.repo + '.packages' - _channel_repository_cache[cache_key] = { - 'time': time.time() + self.settings.get('cache_length', 300), - 'data': repository_packages - } - packages.update(repository_packages) - - renamed_packages = downloader.renamed_packages - if renamed_packages == False: - continue - renamed_cache_key = downloader.repo + '.renamed_packages' - _channel_repository_cache[renamed_cache_key] = { - 'time': time.time() + self.settings.get('cache_length', 300), - 'data': renamed_packages - } - if renamed_packages: - self.settings['renamed_packages'] = self.settings.get( - 'renamed_packages', {}) - self.settings['renamed_packages'].update(renamed_packages) - - unavailable_packages = downloader.unavailable_packages - unavailable_cache_key = downloader.repo + '.unavailable_packages' - _channel_repository_cache[unavailable_cache_key] = { - 'time': time.time() + self.settings.get('cache_length', 300), - 'data': unavailable_packages - } - if unavailable_packages: - self.settings['unavailable_packages'] = self.settings.get( - 'unavailable_packages', []) - self.settings['unavailable_packages'].extend(unavailable_packages) - - return packages - - def list_packages(self): - """ :return: A list of all installed, non-default, package names""" - - package_names = os.listdir(sublime.packages_path()) - package_names = [path for path in package_names if - os.path.isdir(os.path.join(sublime.packages_path(), path))] - - # Ignore things to be deleted - ignored = [] - for package in package_names: - cleanup_file = os.path.join(sublime.packages_path(), package, - 'package-control.cleanup') - if os.path.exists(cleanup_file): - ignored.append(package) - - packages = list(set(package_names) - set(ignored) - - set(self.list_default_packages())) - packages = sorted(packages, key=lambda s: s.lower()) - - return packages - - def list_all_packages(self): - """ :return: A list of all installed package names, including default packages""" - - packages = os.listdir(sublime.packages_path()) - packages = sorted(packages, key=lambda s: s.lower()) - return packages - - def list_default_packages(self): - """ :return: A list of all default package names""" - - files = os.listdir(os.path.join(os.path.dirname( - sublime.packages_path()), 'Pristine Packages')) - files = list(set(files) - set(os.listdir( - sublime.installed_packages_path()))) - packages = [file.replace('.sublime-package', '') for file in files] - packages = sorted(packages, key=lambda s: s.lower()) - return packages - - def get_package_dir(self, package): - """:return: The full filesystem path to the package directory""" - - return os.path.join(sublime.packages_path(), package) - - def get_mapped_name(self, package): - """:return: The name of the package after passing through mapping rules""" - - return self.settings.get('package_name_map', {}).get(package, package) - - def create_package(self, package_name, package_destination, - binary_package=False): - """ - Creates a .sublime-package file from the running Packages directory - - :param package_name: - The package to create a .sublime-package file for - - :param package_destination: - The full filesystem path of the directory to save the new - .sublime-package file in. - - :param binary_package: - If the created package should follow the binary package include/ - exclude patterns from the settings. These normally include a setup - to exclude .py files and include .pyc files, but that can be - changed via settings. - - :return: bool if the package file was successfully created - """ - - package_dir = self.get_package_dir(package_name) + '/' - - if not os.path.exists(package_dir): - sublime.error_message(('%s: The folder for the package name ' + - 'specified, %s, does not exist in %s') % - (__name__, package_name, sublime.packages_path())) - return False - - package_filename = package_name + '.sublime-package' - package_path = os.path.join(package_destination, - package_filename) - - if not os.path.exists(sublime.installed_packages_path()): - os.mkdir(sublime.installed_packages_path()) - - if os.path.exists(package_path): - os.remove(package_path) - - try: - package_file = zipfile.ZipFile(package_path, "w", - compression=zipfile.ZIP_DEFLATED) - except (OSError, IOError) as (exception): - sublime.error_message(('%s: An error occurred creating the ' + - 'package file %s in %s. %s') % (__name__, package_filename, - package_destination, unicode_from_os(exception))) - return False - - dirs_to_ignore = self.settings.get('dirs_to_ignore', []) - if not binary_package: - files_to_ignore = self.settings.get('files_to_ignore', []) - files_to_include = self.settings.get('files_to_include', []) - else: - files_to_ignore = self.settings.get('files_to_ignore_binary', []) - files_to_include = self.settings.get('files_to_include_binary', []) - - package_dir_regex = re.compile('^' + re.escape(package_dir)) - for root, dirs, files in os.walk(package_dir): - [dirs.remove(dir) for dir in dirs if dir in dirs_to_ignore] - paths = dirs - paths.extend(files) - for path in paths: - full_path = os.path.join(root, path) - relative_path = re.sub(package_dir_regex, '', full_path) - - ignore_matches = [fnmatch(relative_path, p) for p in files_to_ignore] - include_matches = [fnmatch(relative_path, p) for p in files_to_include] - if any(ignore_matches) and not any(include_matches): - continue - - if os.path.isdir(full_path): - continue - package_file.write(full_path, relative_path) - - package_file.close() - - return True - - def install_package(self, package_name): - """ - Downloads and installs (or upgrades) a package - - Uses the self.list_available_packages() method to determine where to - retrieve the package file from. - - The install process consists of: - - 1. Finding the package - 2. Downloading the .sublime-package/.zip file - 3. Extracting the package file - 4. Showing install/upgrade messaging - 5. Submitting usage info - 6. Recording that the package is installed - - :param package_name: - The package to download and install - - :return: bool if the package was successfully installed - """ - - packages = self.list_available_packages() - - if package_name in self.settings.get('unavailable_packages', []): - print ('%s: The package "%s" is not available ' + - 'on this platform.') % (__name__, package_name) - return False - - if package_name not in packages.keys(): - sublime.error_message(('%s: The package specified, %s, is ' + - 'not available.') % (__name__, package_name)) - return False - - download = packages[package_name]['downloads'][0] - url = download['url'] - - package_filename = package_name + \ - '.sublime-package' - package_path = os.path.join(sublime.installed_packages_path(), - package_filename) - pristine_package_path = os.path.join(os.path.dirname( - sublime.packages_path()), 'Pristine Packages', package_filename) - - package_dir = self.get_package_dir(package_name) - - package_metadata_file = os.path.join(package_dir, - 'package-metadata.json') - - if os.path.exists(os.path.join(package_dir, '.git')): - if self.settings.get('ignore_vcs_packages'): - sublime.error_message(('%s: Skipping git package %s since ' + - 'the setting ignore_vcs_packages is set to true') % - (__name__, package_name)) - return False - return GitUpgrader(self.settings['git_binary'], - self.settings['git_update_command'], package_dir, - self.settings['cache_length'], self.settings['debug']).run() - elif os.path.exists(os.path.join(package_dir, '.hg')): - if self.settings.get('ignore_vcs_packages'): - sublime.error_message(('%s: Skipping hg package %s since ' + - 'the setting ignore_vcs_packages is set to true') % - (__name__, package_name)) - return False - return HgUpgrader(self.settings['hg_binary'], - self.settings['hg_update_command'], package_dir, - self.settings['cache_length'], self.settings['debug']).run() - - is_upgrade = os.path.exists(package_metadata_file) - old_version = None - if is_upgrade: - old_version = self.get_metadata(package_name).get('version') - - package_bytes = self.download_url(url, 'Error downloading package.') - if package_bytes == False: - return False - with open(package_path, "wb") as package_file: - package_file.write(package_bytes) - - if not os.path.exists(package_dir): - os.mkdir(package_dir) - - # We create a backup copy incase something was edited - else: - try: - backup_dir = os.path.join(os.path.dirname( - sublime.packages_path()), 'Backup', - datetime.datetime.now().strftime('%Y%m%d%H%M%S')) - if not os.path.exists(backup_dir): - os.makedirs(backup_dir) - package_backup_dir = os.path.join(backup_dir, package_name) - shutil.copytree(package_dir, package_backup_dir) - except (OSError, IOError) as (exception): - sublime.error_message(('%s: An error occurred while trying ' + - 'to backup the package directory for %s. %s') % - (__name__, package_name, unicode_from_os(exception))) - shutil.rmtree(package_backup_dir) - return False - - try: - package_zip = zipfile.ZipFile(package_path, 'r') - except (zipfile.BadZipfile): - sublime.error_message(('%s: An error occurred while ' + - 'trying to unzip the package file for %s. Please try ' + - 'installing the package again.') % (__name__, package_name)) - return False - - root_level_paths = [] - last_path = None - for path in package_zip.namelist(): - last_path = path - if path.find('/') in [len(path) - 1, -1]: - root_level_paths.append(path) - if path[0] == '/' or path.find('../') != -1 or path.find('..\\') != -1: - sublime.error_message(('%s: The package specified, %s, ' + - 'contains files outside of the package dir and cannot ' + - 'be safely installed.') % (__name__, package_name)) - return False - - if last_path and len(root_level_paths) == 0: - root_level_paths.append(last_path[0:last_path.find('/') + 1]) - - os.chdir(package_dir) - - overwrite_failed = False - - # Here we don’t use .extractall() since it was having issues on OS X - skip_root_dir = len(root_level_paths) == 1 and \ - root_level_paths[0].endswith('/') - extracted_paths = [] - for path in package_zip.namelist(): - dest = path - try: - if not isinstance(dest, unicode): - dest = unicode(dest, 'utf-8', 'strict') - except (UnicodeDecodeError): - dest = unicode(dest, 'cp1252', 'replace') - - if os.name == 'nt': - regex = ':|\*|\?|"|<|>|\|' - if re.search(regex, dest) != None: - print ('%s: Skipping file from package named %s due to ' + - 'an invalid filename') % (__name__, path) - continue - - # If there was only a single directory in the package, we remove - # that folder name from the paths as we extract entries - if skip_root_dir: - dest = dest[len(root_level_paths[0]):] - - if os.name == 'nt': - dest = dest.replace('/', '\\') - else: - dest = dest.replace('\\', '/') - - dest = os.path.join(package_dir, dest) - - def add_extracted_dirs(dir): - while dir not in extracted_paths: - extracted_paths.append(dir) - dir = os.path.dirname(dir) - if dir == package_dir: - break - - if path.endswith('/'): - if not os.path.exists(dest): - os.makedirs(dest) - add_extracted_dirs(dest) - else: - dest_dir = os.path.dirname(dest) - if not os.path.exists(dest_dir): - os.makedirs(dest_dir) - add_extracted_dirs(dest_dir) - extracted_paths.append(dest) - try: - open(dest, 'wb').write(package_zip.read(path)) - except (IOError) as (e): - message = unicode_from_os(e) - if re.search('[Ee]rrno 13', message): - overwrite_failed = True - break - print ('%s: Skipping file from package named %s due to ' + - 'an invalid filename') % (__name__, path) - - except (UnicodeDecodeError): - print ('%s: Skipping file from package named %s due to ' + - 'an invalid filename') % (__name__, path) - package_zip.close() - - - # If upgrading failed, queue the package to upgrade upon next start - if overwrite_failed: - reinstall_file = os.path.join(package_dir, 'package-control.reinstall') - open(reinstall_file, 'w').close() - - # Don't delete the metadata file, that way we have it - # when the reinstall happens, and the appropriate - # usage info can be sent back to the server - clear_directory(package_dir, [reinstall_file, package_metadata_file]) - - sublime.error_message(('%s: An error occurred while trying to ' + - 'upgrade %s. Please restart Sublime Text to finish the ' + - 'upgrade.') % (__name__, package_name)) - return False - - - # Here we clean out any files that were not just overwritten. It is ok - # if there is an error removing a file. The next time there is an - # upgrade, it should be cleaned out successfully then. - clear_directory(package_dir, extracted_paths) - - - self.print_messages(package_name, package_dir, is_upgrade, old_version) - - with open(package_metadata_file, 'w') as f: - metadata = { - "version": packages[package_name]['downloads'][0]['version'], - "url": packages[package_name]['url'], - "description": packages[package_name]['description'] - } - json.dump(metadata, f) - - # Submit install and upgrade info - if is_upgrade: - params = { - 'package': package_name, - 'operation': 'upgrade', - 'version': packages[package_name]['downloads'][0]['version'], - 'old_version': old_version - } - else: - params = { - 'package': package_name, - 'operation': 'install', - 'version': packages[package_name]['downloads'][0]['version'] - } - self.record_usage(params) - - # Record the install in the settings file so that you can move - # settings across computers and have the same packages installed - def save_package(): - settings = sublime.load_settings(__name__ + '.sublime-settings') - installed_packages = settings.get('installed_packages', []) - if not installed_packages: - installed_packages = [] - installed_packages.append(package_name) - installed_packages = list(set(installed_packages)) - installed_packages = sorted(installed_packages, - key=lambda s: s.lower()) - settings.set('installed_packages', installed_packages) - sublime.save_settings(__name__ + '.sublime-settings') - sublime.set_timeout(save_package, 1) - - # Here we delete the package file from the installed packages directory - # since we don't want to accidentally overwrite user changes - os.remove(package_path) - # We have to remove the pristine package too or else Sublime Text 2 - # will silently delete the package - if os.path.exists(pristine_package_path): - os.remove(pristine_package_path) - - os.chdir(sublime.packages_path()) - return True - - def print_messages(self, package, package_dir, is_upgrade, old_version): - """ - Prints out package install and upgrade messages - - The functionality provided by this allows package maintainers to - show messages to the user when a package is installed, or when - certain version upgrade occur. - - :param package: - The name of the package the message is for - - :param package_dir: - The full filesystem path to the package directory - - :param is_upgrade: - If the install was actually an upgrade - - :param old_version: - The string version of the package before the upgrade occurred - """ - - messages_file = os.path.join(package_dir, 'messages.json') - if not os.path.exists(messages_file): - return - - messages_fp = open(messages_file, 'r') - try: - message_info = json.load(messages_fp) - except (ValueError): - print '%s: Error parsing messages.json for %s' % (__name__, package) - return - messages_fp.close() - - output = '' - if not is_upgrade and message_info.get('install'): - install_messages = os.path.join(package_dir, - message_info.get('install')) - message = '\n\n%s:\n%s\n\n ' % (package, - ('-' * len(package))) - with open(install_messages, 'r') as f: - message += unicode(f.read(), 'utf-8', errors='replace').replace('\n', '\n ') - output += message + '\n' - - elif is_upgrade and old_version: - upgrade_messages = list(set(message_info.keys()) - - set(['install'])) - upgrade_messages = sorted(upgrade_messages, - cmp=self.compare_versions, reverse=True) - for version in upgrade_messages: - if self.compare_versions(old_version, version) >= 0: - break - if not output: - message = '\n\n%s:\n%s\n' % (package, - ('-' * len(package))) - output += message - upgrade_messages = os.path.join(package_dir, - message_info.get(version)) - message = '\n ' - with open(upgrade_messages, 'r') as f: - message += unicode(f.read(), 'utf-8', errors='replace').replace('\n', '\n ') - output += message + '\n' - - if not output: - return - - def print_to_panel(): - window = sublime.active_window() - - views = window.views() - view = None - for _view in views: - if _view.name() == 'Package Control Messages': - view = _view - break - - if not view: - view = window.new_file() - view.set_name('Package Control Messages') - view.set_scratch(True) - - def write(string): - edit = view.begin_edit() - view.insert(edit, view.size(), string) - view.end_edit(edit) - - if not view.size(): - view.settings().set("word_wrap", True) - write('Package Control Messages\n' + - '========================') - - write(output) - sublime.set_timeout(print_to_panel, 1) - - def remove_package(self, package_name): - """ - Deletes a package - - The deletion process consists of: - - 1. Deleting the directory (or marking it for deletion if deletion fails) - 2. Submitting usage info - 3. Removing the package from the list of installed packages - - :param package_name: - The package to delete - - :return: bool if the package was successfully deleted - """ - - installed_packages = self.list_packages() - - if package_name not in installed_packages: - sublime.error_message(('%s: The package specified, %s, is not ' + - 'installed.') % (__name__, package_name)) - return False - - os.chdir(sublime.packages_path()) - - # Give Sublime Text some time to ignore the package - time.sleep(1) - - package_filename = package_name + '.sublime-package' - package_path = os.path.join(sublime.installed_packages_path(), - package_filename) - installed_package_path = os.path.join(os.path.dirname( - sublime.packages_path()), 'Installed Packages', package_filename) - pristine_package_path = os.path.join(os.path.dirname( - sublime.packages_path()), 'Pristine Packages', package_filename) - package_dir = self.get_package_dir(package_name) - - version = self.get_metadata(package_name).get('version') - - try: - if os.path.exists(package_path): - os.remove(package_path) - except (OSError, IOError) as (exception): - sublime.error_message(('%s: An error occurred while trying to ' + - 'remove the package file for %s. %s') % (__name__, - package_name, unicode_from_os(exception))) - return False - - try: - if os.path.exists(installed_package_path): - os.remove(installed_package_path) - except (OSError, IOError) as (exception): - sublime.error_message(('%s: An error occurred while trying to ' + - 'remove the installed package file for %s. %s') % (__name__, - package_name, unicode_from_os(exception))) - return False - - try: - if os.path.exists(pristine_package_path): - os.remove(pristine_package_path) - except (OSError, IOError) as (exception): - sublime.error_message(('%s: An error occurred while trying to ' + - 'remove the pristine package file for %s. %s') % (__name__, - package_name, unicode_from_os(exception))) - return False - - # We don't delete the actual package dir immediately due to a bug - # in sublime_plugin.py - can_delete_dir = True - if not clear_directory(package_dir): - # If there is an error deleting now, we will mark it for - # cleanup the next time Sublime Text starts - open(os.path.join(package_dir, 'package-control.cleanup'), - 'w').close() - can_delete_dir = False - - params = { - 'package': package_name, - 'operation': 'remove', - 'version': version - } - self.record_usage(params) - - # Remove the package from the installed packages list - def clear_package(): - settings = sublime.load_settings('%s.sublime-settings' % __name__) - installed_packages = settings.get('installed_packages', []) - if not installed_packages: - installed_packages = [] - installed_packages.remove(package_name) - settings.set('installed_packages', installed_packages) - sublime.save_settings('%s.sublime-settings' % __name__) - sublime.set_timeout(clear_package, 1) - - if can_delete_dir: - os.rmdir(package_dir) - - return True - - def record_usage(self, params): - """ - Submits install, upgrade and delete actions to a usage server - - The usage information is currently displayed on the Package Control - community package list at http://wbond.net/sublime_packages/community - - :param params: - A dict of the information to submit - """ - - if not self.settings.get('submit_usage'): - return - params['package_control_version'] = \ - self.get_metadata('Package Control').get('version') - params['sublime_platform'] = self.settings.get('platform') - params['sublime_version'] = self.settings.get('version') - url = self.settings.get('submit_url') + '?' + urllib.urlencode(params) - - result = self.download_url(url, 'Error submitting usage information.') - if result == False: - return - - try: - result = json.loads(result) - if result['result'] != 'success': - raise ValueError() - except (ValueError): - print '%s: Error submitting usage information for %s' % (__name__, - params['package']) - - -class PackageCreator(): - """ - Abstract class for commands that create .sublime-package files - """ - - def show_panel(self): - """ - Shows a list of packages that can be turned into a .sublime-package file - """ - - self.manager = PackageManager() - self.packages = self.manager.list_packages() - if not self.packages: - sublime.error_message(('%s: There are no packages available to ' + - 'be packaged.') % (__name__)) - return - self.window.show_quick_panel(self.packages, self.on_done) - - def get_package_destination(self): - """ - Retrieves the destination for .sublime-package files - - :return: - A string - the path to the folder to save .sublime-package files in - """ - - destination = self.manager.settings.get('package_destination') - - # We check destination via an if statement instead of using - # the dict.get() method since the key may be set, but to a blank value - if not destination: - destination = os.path.join(os.path.expanduser('~'), 'Desktop') - - return destination - - -class CreatePackageCommand(sublime_plugin.WindowCommand, PackageCreator): - """ - Command to create a regular .sublime-package file - """ - - def run(self): - self.show_panel() - - def on_done(self, picked): - """ - Quick panel user selection handler - processes the user package - selection and create the package file - - :param picked: - An integer of the 0-based package name index from the presented - list. -1 means the user cancelled. - """ - - if picked == -1: - return - package_name = self.packages[picked] - package_destination = self.get_package_destination() - - if self.manager.create_package(package_name, package_destination): - self.window.run_command('open_dir', {"dir": - package_destination, "file": package_name + - '.sublime-package'}) - - -class CreateBinaryPackageCommand(sublime_plugin.WindowCommand, PackageCreator): - """ - Command to create a binary .sublime-package file. Binary packages in - general exclude the .py source files and instead include the .pyc files. - Actual included and excluded files are controlled by settings. - """ - - def run(self): - self.show_panel() - - def on_done(self, picked): - """ - Quick panel user selection handler - processes the user package - selection and create the package file - - :param picked: - An integer of the 0-based package name index from the presented - list. -1 means the user cancelled. - """ - - if picked == -1: - return - package_name = self.packages[picked] - package_destination = self.get_package_destination() - - if self.manager.create_package(package_name, package_destination, - binary_package=True): - self.window.run_command('open_dir', {"dir": - package_destination, "file": package_name + - '.sublime-package'}) - - -class PackageRenamer(): - """ - Class to handle renaming packages via the renamed_packages setting - gathered from channels and repositories. - """ - - def load_settings(self): - """ - Loads the list of installed packages from the - Package Control.sublime-settings file. - """ - - self.settings_file = '%s.sublime-settings' % __name__ - self.settings = sublime.load_settings(self.settings_file) - self.installed_packages = self.settings.get('installed_packages', []) - if not isinstance(self.installed_packages, list): - self.installed_packages = [] - - def rename_packages(self, installer): - """ - Renames any installed packages that the user has installed. - - :param installer: - An instance of :class:`PackageInstaller` - """ - - # Fetch the packages since that will pull in the renamed packages list - installer.manager.list_available_packages() - renamed_packages = installer.manager.settings.get('renamed_packages', {}) - if not renamed_packages: - renamed_packages = {} - - # These are packages that have been tracked as installed - installed_pkgs = self.installed_packages - # There are the packages actually present on the filesystem - present_packages = installer.manager.list_packages() - - # Rename directories for packages that have changed names - for package_name in renamed_packages: - package_dir = os.path.join(sublime.packages_path(), package_name) - metadata_path = os.path.join(package_dir, 'package-metadata.json') - if not os.path.exists(metadata_path): - continue - - new_package_name = renamed_packages[package_name] - new_package_dir = os.path.join(sublime.packages_path(), - new_package_name) - - changing_case = package_name.lower() == new_package_name.lower() - case_insensitive_fs = sublime.platform() in ['windows', 'osx'] - - # Since Windows and OSX use case-insensitive filesystems, we have to - # scan through the list of installed packages if the rename of the - # package is just changing the case of it. If we don't find the old - # name for it, we continue the loop since os.path.exists() will return - # true due to the case-insensitive nature of the filesystems. - if case_insensitive_fs and changing_case: - has_old = False - for present_package_name in present_packages: - if present_package_name == package_name: - has_old = True - break - if not has_old: - continue - - if not os.path.exists(new_package_dir) or (case_insensitive_fs and changing_case): - - # Windows will not allow you to rename to the same name with - # a different case, so we work around that with a temporary name - if os.name == 'nt' and changing_case: - temp_package_name = '__' + new_package_name - temp_package_dir = os.path.join(sublime.packages_path(), - temp_package_name) - os.rename(package_dir, temp_package_dir) - package_dir = temp_package_dir - - os.rename(package_dir, new_package_dir) - installed_pkgs.append(new_package_name) - - print '%s: Renamed %s to %s' % (__name__, package_name, - new_package_name) - - else: - installer.manager.remove_package(package_name) - print ('%s: Removed %s since package with new name (%s) ' + - 'already exists') % (__name__, package_name, - new_package_name) - - try: - installed_pkgs.remove(package_name) - except (ValueError): - pass - - sublime.set_timeout(lambda: self.save_packages(installed_pkgs), 10) - - def save_packages(self, installed_packages): - """ - Saves the list of installed packages (after having been appropriately - renamed) - - :param installed_packages: - The new list of installed packages - """ - - installed_packages = list(set(installed_packages)) - installed_packages = sorted(installed_packages, - key=lambda s: s.lower()) - - if installed_packages != self.installed_packages: - self.settings.set('installed_packages', installed_packages) - sublime.save_settings(self.settings_file) - - -class PackageInstaller(): - """ - Provides helper functionality related to installing packages - """ - - def __init__(self): - self.manager = PackageManager() - - def make_package_list(self, ignore_actions=[], override_action=None, - ignore_packages=[]): - """ - Creates a list of packages and what operation would be performed for - each. Allows filtering by the applicable action or package name. - Returns the information in a format suitable for displaying in the - quick panel. - - :param ignore_actions: - A list of actions to ignore packages by. Valid actions include: - `install`, `upgrade`, `downgrade`, `reinstall`, `overwrite`, - `pull` and `none`. `pull` andd `none` are for Git and Hg - repositories. `pull` is present when incoming changes are detected, - where as `none` is selected if no commits are available. `overwrite` - is for packages that do not include version information via the - `package-metadata.json` file. - - :param override_action: - A string action name to override the displayed action for all listed - packages. - - :param ignore_packages: - A list of packages names that should not be returned in the list - - :return: - A list of lists, each containing three strings: - 0 - package name - 1 - package description - 2 - action; [extra info;] package url - """ - - packages = self.manager.list_available_packages() - installed_packages = self.manager.list_packages() - - package_list = [] - for package in sorted(packages.iterkeys(), key=lambda s: s.lower()): - if ignore_packages and package in ignore_packages: - continue - package_entry = [package] - info = packages[package] - download = info['downloads'][0] - - if package in installed_packages: - installed = True - metadata = self.manager.get_metadata(package) - if metadata.get('version'): - installed_version = metadata['version'] - else: - installed_version = None - else: - installed = False - - installed_version_name = 'v' + installed_version if \ - installed and installed_version else 'unknown version' - new_version = 'v' + download['version'] - - vcs = None - package_dir = self.manager.get_package_dir(package) - settings = self.manager.settings - - if override_action: - action = override_action - extra = '' - - else: - if os.path.exists(os.path.join(sublime.packages_path(), - package, '.git')): - if settings.get('ignore_vcs_packages'): - continue - vcs = 'git' - incoming = GitUpgrader(settings.get('git_binary'), - settings.get('git_update_command'), package_dir, - settings.get('cache_length'), settings.get('debug') - ).incoming() - elif os.path.exists(os.path.join(sublime.packages_path(), - package, '.hg')): - if settings.get('ignore_vcs_packages'): - continue - vcs = 'hg' - incoming = HgUpgrader(settings.get('hg_binary'), - settings.get('hg_update_command'), package_dir, - settings.get('cache_length'), settings.get('debug') - ).incoming() - - if installed: - if not installed_version: - if vcs: - if incoming: - action = 'pull' - extra = ' with ' + vcs - else: - action = 'none' - extra = '' - else: - action = 'overwrite' - extra = ' %s with %s' % (installed_version_name, - new_version) - else: - res = self.manager.compare_versions( - installed_version, download['version']) - if res < 0: - action = 'upgrade' - extra = ' to %s from %s' % (new_version, - installed_version_name) - elif res > 0: - action = 'downgrade' - extra = ' to %s from %s' % (new_version, - installed_version_name) - else: - action = 'reinstall' - extra = ' %s' % new_version - else: - action = 'install' - extra = ' %s' % new_version - extra += ';' - - if action in ignore_actions: - continue - - description = info.get('description') - if not description: - description = 'No description provided' - package_entry.append(description) - package_entry.append(action + extra + ' ' + - re.sub('^https?://', '', info['url'])) - package_list.append(package_entry) - return package_list - - def disable_package(self, package): - """ - Disables a package before installing or upgrading to prevent errors - where Sublime Text tries to read files that no longer exist, or read a - half-written file. - - :param package: The string package name - """ - - # Don't disable Package Control so it does not get stuck disabled - if package == 'Package Control': - return False - - settings = sublime.load_settings(preferences_filename()) - ignored = settings.get('ignored_packages') - if not ignored: - ignored = [] - if not package in ignored: - ignored.append(package) - settings.set('ignored_packages', ignored) - sublime.save_settings(preferences_filename()) - return True - return False - - def reenable_package(self, package): - """ - Re-enables a package after it has been installed or upgraded - - :param package: The string package name - """ - - settings = sublime.load_settings(preferences_filename()) - ignored = settings.get('ignored_packages') - if not ignored: - return - if package in ignored: - settings.set('ignored_packages', - list(set(ignored) - set([package]))) - sublime.save_settings(preferences_filename()) - - def on_done(self, picked): - """ - Quick panel user selection handler - disables a package, installs or - upgrades it, then re-enables the package - - :param picked: - An integer of the 0-based package name index from the presented - list. -1 means the user cancelled. - """ - - if picked == -1: - return - name = self.package_list[picked][0] - - if self.disable_package(name): - on_complete = lambda: self.reenable_package(name) - else: - on_complete = None - - thread = PackageInstallerThread(self.manager, name, on_complete) - thread.start() - ThreadProgress(thread, 'Installing package %s' % name, - 'Package %s successfully %s' % (name, self.completion_type)) - - -class PackageInstallerThread(threading.Thread): - """ - A thread to run package install/upgrade operations in so that the main - Sublime Text thread does not get blocked and freeze the UI - """ - - def __init__(self, manager, package, on_complete): - """ - :param manager: - An instance of :class:`PackageManager` - - :param package: - The string package name to install/upgrade - - :param on_complete: - A callback to run after installing/upgrading the package - """ - - self.package = package - self.manager = manager - self.on_complete = on_complete - threading.Thread.__init__(self) - - def run(self): - try: - self.result = self.manager.install_package(self.package) - finally: - if self.on_complete: - sublime.set_timeout(self.on_complete, 1) - - -class InstallPackageCommand(sublime_plugin.WindowCommand): - """ - A command that presents the list of available packages and allows the - user to pick one to install. - """ - - def run(self): - thread = InstallPackageThread(self.window) - thread.start() - ThreadProgress(thread, 'Loading repositories', '') - - -class InstallPackageThread(threading.Thread, PackageInstaller): - """ - A thread to run the action of retrieving available packages in. Uses the - default PackageInstaller.on_done quick panel handler. - """ - - def __init__(self, window): - """ - :param window: - An instance of :class:`sublime.Window` that represents the Sublime - Text window to show the available package list in. - """ - - self.window = window - self.completion_type = 'installed' - threading.Thread.__init__(self) - PackageInstaller.__init__(self) - - def run(self): - self.package_list = self.make_package_list(['upgrade', 'downgrade', - 'reinstall', 'pull', 'none']) - - def show_quick_panel(): - if not self.package_list: - sublime.error_message(('%s: There are no packages ' + - 'available for installation.') % __name__) - return - self.window.show_quick_panel(self.package_list, self.on_done) - sublime.set_timeout(show_quick_panel, 10) - - -class DiscoverPackagesCommand(sublime_plugin.WindowCommand): - """ - A command that opens the community package list webpage - """ - - def run(self): - self.window.run_command('open_url', - {'url': 'http://wbond.net/sublime_packages/community'}) - - -class UpgradePackageCommand(sublime_plugin.WindowCommand): - """ - A command that presents the list of installed packages that can be upgraded. - """ - - def run(self): - package_renamer = PackageRenamer() - package_renamer.load_settings() - - thread = UpgradePackageThread(self.window, package_renamer) - thread.start() - ThreadProgress(thread, 'Loading repositories', '') - - -class UpgradePackageThread(threading.Thread, PackageInstaller): - """ - A thread to run the action of retrieving upgradable packages in. - """ - - def __init__(self, window, package_renamer): - """ - :param window: - An instance of :class:`sublime.Window` that represents the Sublime - Text window to show the list of upgradable packages in. - - :param package_renamer: - An instance of :class:`PackageRenamer` - """ - self.window = window - self.package_renamer = package_renamer - self.completion_type = 'upgraded' - threading.Thread.__init__(self) - PackageInstaller.__init__(self) - - def run(self): - self.package_renamer.rename_packages(self) - - self.package_list = self.make_package_list(['install', 'reinstall', - 'none']) - - def show_quick_panel(): - if not self.package_list: - sublime.error_message(('%s: There are no packages ' + - 'ready for upgrade.') % __name__) - return - self.window.show_quick_panel(self.package_list, self.on_done) - sublime.set_timeout(show_quick_panel, 10) - - def on_done(self, picked): - """ - Quick panel user selection handler - disables a package, upgrades it, - then re-enables the package - - :param picked: - An integer of the 0-based package name index from the presented - list. -1 means the user cancelled. - """ - - if picked == -1: - return - name = self.package_list[picked][0] - - if self.disable_package(name): - on_complete = lambda: self.reenable_package(name) - else: - on_complete = None - - thread = PackageInstallerThread(self.manager, name, on_complete) - thread.start() - ThreadProgress(thread, 'Upgrading package %s' % name, - 'Package %s successfully %s' % (name, self.completion_type)) - - -class UpgradeAllPackagesCommand(sublime_plugin.WindowCommand): - """ - A command to automatically upgrade all installed packages that are - upgradable. - """ - - def run(self): - package_renamer = PackageRenamer() - package_renamer.load_settings() - - thread = UpgradeAllPackagesThread(self.window, package_renamer) - thread.start() - ThreadProgress(thread, 'Loading repositories', '') - - -class UpgradeAllPackagesThread(threading.Thread, PackageInstaller): - """ - A thread to run the action of retrieving upgradable packages in. - """ - - def __init__(self, window, package_renamer): - self.window = window - self.package_renamer = package_renamer - self.completion_type = 'upgraded' - threading.Thread.__init__(self) - PackageInstaller.__init__(self) - - def run(self): - self.package_renamer.rename_packages(self) - package_list = self.make_package_list(['install', 'reinstall', 'none']) - - disabled_packages = {} - - def do_upgrades(): - # Pause so packages can be disabled - time.sleep(0.5) - - # We use a function to generate the on-complete lambda because if - # we don't, the lambda will bind to info at the current scope, and - # thus use the last value of info from the loop - def make_on_complete(name): - return lambda: self.reenable_package(name) - - for info in package_list: - if disabled_packages.get(info[0]): - on_complete = make_on_complete(info[0]) - else: - on_complete = None - thread = PackageInstallerThread(self.manager, info[0], on_complete) - thread.start() - ThreadProgress(thread, 'Upgrading package %s' % info[0], - 'Package %s successfully %s' % (info[0], self.completion_type)) - - # Disabling a package means changing settings, which can only be done - # in the main thread. We then create a new background thread so that - # the upgrade process does not block the UI. - def disable_packages(): - for info in package_list: - disabled_packages[info[0]] = self.disable_package(info[0]) - threading.Thread(target=do_upgrades).start() - - sublime.set_timeout(disable_packages, 1) - - -class ExistingPackagesCommand(): - """ - Allows listing installed packages and their current version - """ - - def __init__(self): - self.manager = PackageManager() - - def make_package_list(self, action=''): - """ - Returns a list of installed packages suitable for displaying in the - quick panel. - - :param action: - An action to display at the beginning of the third element of the - list returned for each package - - :return: - A list of lists, each containing three strings: - 0 - package name - 1 - package description - 2 - [action] installed version; package url - """ - - packages = self.manager.list_packages() - - if action: - action += ' ' - - package_list = [] - for package in sorted(packages, key=lambda s: s.lower()): - package_entry = [package] - metadata = self.manager.get_metadata(package) - package_dir = os.path.join(sublime.packages_path(), package) - - description = metadata.get('description') - if not description: - description = 'No description provided' - package_entry.append(description) - - version = metadata.get('version') - if not version and os.path.exists(os.path.join(package_dir, - '.git')): - installed_version = 'git repository' - elif not version and os.path.exists(os.path.join(package_dir, - '.hg')): - installed_version = 'hg repository' - else: - installed_version = 'v' + version if version else \ - 'unknown version' - - url = metadata.get('url') - if url: - url = '; ' + re.sub('^https?://', '', url) - else: - url = '' - - package_entry.append(action + installed_version + url) - package_list.append(package_entry) - - return package_list - - -class ListPackagesCommand(sublime_plugin.WindowCommand): - """ - A command that shows a list of all installed packages in the quick panel - """ - - def run(self): - ListPackagesThread(self.window).start() - - -class ListPackagesThread(threading.Thread, ExistingPackagesCommand): - """ - A thread to prevent the listing of existing packages from freezing the UI - """ - - def __init__(self, window): - """ - :param window: - An instance of :class:`sublime.Window` that represents the Sublime - Text window to show the list of installed packages in. - """ - - self.window = window - threading.Thread.__init__(self) - ExistingPackagesCommand.__init__(self) - - def run(self): - self.package_list = self.make_package_list() - - def show_quick_panel(): - if not self.package_list: - sublime.error_message(('%s: There are no packages ' + - 'to list.') % __name__) - return - self.window.show_quick_panel(self.package_list, self.on_done) - sublime.set_timeout(show_quick_panel, 10) - - def on_done(self, picked): - """ - Quick panel user selection handler - opens the homepage for any - selected package in the user's browser - - :param picked: - An integer of the 0-based package name index from the presented - list. -1 means the user cancelled. - """ - - if picked == -1: - return - package_name = self.package_list[picked][0] - - def open_dir(): - self.window.run_command('open_dir', - {"dir": os.path.join(sublime.packages_path(), package_name)}) - sublime.set_timeout(open_dir, 10) - - -class RemovePackageCommand(sublime_plugin.WindowCommand, - ExistingPackagesCommand): - """ - A command that presents a list of installed packages, allowing the user to - select one to remove - """ - - def __init__(self, window): - """ - :param window: - An instance of :class:`sublime.Window` that represents the Sublime - Text window to show the list of installed packages in. - """ - - self.window = window - ExistingPackagesCommand.__init__(self) - - def run(self): - self.package_list = self.make_package_list('remove') - if not self.package_list: - sublime.error_message(('%s: There are no packages ' + - 'that can be removed.') % __name__) - return - self.window.show_quick_panel(self.package_list, self.on_done) - - def on_done(self, picked): - """ - Quick panel user selection handler - deletes the selected package - - :param picked: - An integer of the 0-based package name index from the presented - list. -1 means the user cancelled. - """ - - if picked == -1: - return - package = self.package_list[picked][0] - - # Don't disable Package Control so it does not get stuck disabled - if package != 'Package Control': - settings = sublime.load_settings(preferences_filename()) - ignored = settings.get('ignored_packages') - if not ignored: - ignored = [] - if not package in ignored: - ignored.append(package) - settings.set('ignored_packages', ignored) - sublime.save_settings(preferences_filename()) - - ignored.remove(package) - thread = RemovePackageThread(self.manager, package, - ignored) - thread.start() - ThreadProgress(thread, 'Removing package %s' % package, - 'Package %s successfully removed' % package) - - -class RemovePackageThread(threading.Thread): - """ - A thread to run the remove package operation in so that the Sublime Text - UI does not become frozen - """ - - def __init__(self, manager, package, ignored): - self.manager = manager - self.package = package - self.ignored = ignored - threading.Thread.__init__(self) - - def run(self): - self.result = self.manager.remove_package(self.package) - - def unignore_package(): - settings = sublime.load_settings(preferences_filename()) - settings.set('ignored_packages', self.ignored) - sublime.save_settings(preferences_filename()) - sublime.set_timeout(unignore_package, 10) - - -class AddRepositoryChannelCommand(sublime_plugin.WindowCommand): - """ - A command to add a new channel (list of repositories) to the user's machine - """ - - def run(self): - self.window.show_input_panel('Channel JSON URL', '', - self.on_done, self.on_change, self.on_cancel) - - def on_done(self, input): - """ - Input panel handler - adds the provided URL as a channel - - :param input: - A string of the URL to the new channel - """ - - settings = sublime.load_settings('%s.sublime-settings' % __name__) - repository_channels = settings.get('repository_channels', []) - if not repository_channels: - repository_channels = [] - repository_channels.append(input) - settings.set('repository_channels', repository_channels) - sublime.save_settings('%s.sublime-settings' % __name__) - sublime.status_message(('Channel %s successfully ' + - 'added') % input) - - def on_change(self, input): - pass - - def on_cancel(self): - pass - - -class AddRepositoryCommand(sublime_plugin.WindowCommand): - """ - A command to add a new repository to the user's machine - """ - - def run(self): - self.window.show_input_panel('GitHub or BitBucket Web URL, or Custom' + - ' JSON Repository URL', '', self.on_done, - self.on_change, self.on_cancel) - - def on_done(self, input): - """ - Input panel handler - adds the provided URL as a repository - - :param input: - A string of the URL to the new repository - """ - - settings = sublime.load_settings('%s.sublime-settings' % __name__) - repositories = settings.get('repositories', []) - if not repositories: - repositories = [] - repositories.append(input) - settings.set('repositories', repositories) - sublime.save_settings('%s.sublime-settings' % __name__) - sublime.status_message('Repository %s successfully added' % input) - - def on_change(self, input): - pass - - def on_cancel(self): - pass - - -class DisablePackageCommand(sublime_plugin.WindowCommand): - """ - A command that adds a package to Sublime Text's ignored packages list - """ - - def run(self): - manager = PackageManager() - packages = manager.list_all_packages() - self.settings = sublime.load_settings(preferences_filename()) - ignored = self.settings.get('ignored_packages') - if not ignored: - ignored = [] - self.package_list = list(set(packages) - set(ignored)) - self.package_list.sort() - if not self.package_list: - sublime.error_message(('%s: There are no enabled packages' + - 'to disable.') % __name__) - return - self.window.show_quick_panel(self.package_list, self.on_done) - - def on_done(self, picked): - """ - Quick panel user selection handler - disables the selected package - - :param picked: - An integer of the 0-based package name index from the presented - list. -1 means the user cancelled. - """ - - if picked == -1: - return - package = self.package_list[picked] - ignored = self.settings.get('ignored_packages') - if not ignored: - ignored = [] - ignored.append(package) - self.settings.set('ignored_packages', ignored) - sublime.save_settings(preferences_filename()) - sublime.status_message(('Package %s successfully added to list of ' + - 'disabled packages - restarting Sublime Text may be required') % - package) - - -class EnablePackageCommand(sublime_plugin.WindowCommand): - """ - A command that removes a package from Sublime Text's ignored packages list - """ - - def run(self): - self.settings = sublime.load_settings(preferences_filename()) - self.disabled_packages = self.settings.get('ignored_packages') - self.disabled_packages.sort() - if not self.disabled_packages: - sublime.error_message(('%s: There are no disabled packages ' + - 'to enable.') % __name__) - return - self.window.show_quick_panel(self.disabled_packages, self.on_done) - - def on_done(self, picked): - """ - Quick panel user selection handler - enables the selected package - - :param picked: - An integer of the 0-based package name index from the presented - list. -1 means the user cancelled. - """ - - if picked == -1: - return - package = self.disabled_packages[picked] - ignored = self.settings.get('ignored_packages') - self.settings.set('ignored_packages', - list(set(ignored) - set([package]))) - sublime.save_settings(preferences_filename()) - sublime.status_message(('Package %s successfully removed from list ' + - 'of disabled packages - restarting Sublime Text may be required') % - package) - - -class AutomaticUpgrader(threading.Thread): - """ - Automatically checks for updated packages and installs them. controlled - by the `auto_upgrade`, `auto_upgrade_ignore`, `auto_upgrade_frequency` and - `auto_upgrade_last_run` settings. - """ - - def __init__(self, found_packages): - """ - :param found_packages: - A list of package names for the packages that were found to be - installed on the machine. - """ - - self.installer = PackageInstaller() - self.manager = self.installer.manager - - self.load_settings() - - self.package_renamer = PackageRenamer() - self.package_renamer.load_settings() - - self.auto_upgrade = self.settings.get('auto_upgrade') - self.auto_upgrade_ignore = self.settings.get('auto_upgrade_ignore') - - self.next_run = int(time.time()) - self.last_run = None - last_run_file = os.path.join(sublime.packages_path(), 'User', - 'Package Control.last-run') - - if os.path.isfile(last_run_file): - with open(last_run_file) as fobj: - try: - self.last_run = int(fobj.read()) - except ValueError: - pass - - frequency = self.settings.get('auto_upgrade_frequency') - if frequency: - if self.last_run: - self.next_run = int(self.last_run) + (frequency * 60 * 60) - else: - self.next_run = time.time() - - # Detect if a package is missing that should be installed - self.missing_packages = list(set(self.installed_packages) - - set(found_packages)) - - if self.auto_upgrade and self.next_run <= time.time(): - with open(last_run_file, 'w') as fobj: - fobj.write(str(int(time.time()))) - - threading.Thread.__init__(self) - - def load_settings(self): - """ - Loads the list of installed packages from the - Package Control.sublime-settings file - """ - - self.settings_file = '%s.sublime-settings' % __name__ - self.settings = sublime.load_settings(self.settings_file) - self.installed_packages = self.settings.get('installed_packages', []) - self.should_install_missing = self.settings.get('install_missing') - if not isinstance(self.installed_packages, list): - self.installed_packages = [] - - def run(self): - self.install_missing() - - if self.next_run > time.time(): - self.print_skip() - return - - self.upgrade_packages() - - def install_missing(self): - """ - Installs all packages that were listed in the list of - `installed_packages` from Package Control.sublime-settings but were not - found on the filesystem and passed as `found_packages`. - """ - - if not self.missing_packages or not self.should_install_missing: - return - - print '%s: Installing %s missing packages' % \ - (__name__, len(self.missing_packages)) - for package in self.missing_packages: - if self.installer.manager.install_package(package): - print '%s: Installed missing package %s' % \ - (__name__, package) - - def print_skip(self): - """ - Prints a notice in the console if the automatic upgrade is skipped - due to already having been run in the last `auto_upgrade_frequency` - hours. - """ - - last_run = datetime.datetime.fromtimestamp(self.last_run) - next_run = datetime.datetime.fromtimestamp(self.next_run) - date_format = '%Y-%m-%d %H:%M:%S' - print ('%s: Skipping automatic upgrade, last run at ' + - '%s, next run at %s or after') % (__name__, - last_run.strftime(date_format), next_run.strftime(date_format)) - - def upgrade_packages(self): - """ - Upgrades all packages that are not currently upgraded to the lastest - version. Also renames any installed packages to their new names. - """ - - if not self.auto_upgrade: - return - - self.package_renamer.rename_packages(self.installer) - - packages = self.installer.make_package_list(['install', - 'reinstall', 'downgrade', 'overwrite', 'none'], - ignore_packages=self.auto_upgrade_ignore) - - # If Package Control is being upgraded, just do that and restart - for package in packages: - if package[0] != __name__: - continue - - def reset_last_run(): - settings = sublime.load_settings(self.settings_file) - settings.set('auto_upgrade_last_run', None) - sublime.save_settings(self.settings_file) - sublime.set_timeout(reset_last_run, 1) - packages = [package] - break - - if not packages: - print '%s: No updated packages' % __name__ - return - - print '%s: Installing %s upgrades' % (__name__, len(packages)) - for package in packages: - self.installer.manager.install_package(package[0]) - version = re.sub('^.*?(v[\d\.]+).*?$', '\\1', package[2]) - if version == package[2] and version.find('pull with') != -1: - vcs = re.sub('^pull with (\w+).*?$', '\\1', version) - version = 'latest %s commit' % vcs - print '%s: Upgraded %s to %s' % (__name__, package[0], version) - - -class PackageCleanup(threading.Thread, PackageRenamer): - """ - Cleans up folders for packages that were removed, but that still have files - in use. - """ - - def __init__(self): - self.manager = PackageManager() - self.load_settings() - threading.Thread.__init__(self) - - def run(self): - found_pkgs = [] - installed_pkgs = self.installed_packages - for package_name in os.listdir(sublime.packages_path()): - package_dir = os.path.join(sublime.packages_path(), package_name) - metadata_path = os.path.join(package_dir, 'package-metadata.json') - - # Cleanup packages that could not be removed due to in-use files - cleanup_file = os.path.join(package_dir, 'package-control.cleanup') - if os.path.exists(cleanup_file): - try: - shutil.rmtree(package_dir) - print '%s: Removed old directory for package %s' % \ - (__name__, package_name) - except (OSError) as (e): - if not os.path.exists(cleanup_file): - open(cleanup_file, 'w').close() - print ('%s: Unable to remove old directory for package ' + - '%s - deferring until next start: %s') % (__name__, - package_name, unicode_from_os(e)) - - # Finish reinstalling packages that could not be upgraded due to - # in-use files - reinstall = os.path.join(package_dir, 'package-control.reinstall') - if os.path.exists(reinstall): - if not clear_directory(package_dir, [metadata_path]): - if not os.path.exists(reinstall): - open(reinstall, 'w').close() - # Assigning this here prevents the callback from referencing the value - # of the "package_name" variable when it is executed - restart_message = ('%s: An error occurred while trying to ' + - 'finish the upgrade of %s. You will most likely need to ' + - 'restart your computer to complete the upgrade.') % ( - __name__, package_name) - def show_still_locked(): - sublime.error_message(restart_message) - sublime.set_timeout(show_still_locked, 10) - else: - self.manager.install_package(package_name) - - # This adds previously installed packages from old versions of PC - if os.path.exists(metadata_path) and \ - package_name not in self.installed_packages: - installed_pkgs.append(package_name) - params = { - 'package': package_name, - 'operation': 'install', - 'version': \ - self.manager.get_metadata(package_name).get('version') - } - self.manager.record_usage(params) - - found_pkgs.append(package_name) - - sublime.set_timeout(lambda: self.finish(installed_pkgs, found_pkgs), 10) - - def finish(self, installed_pkgs, found_pkgs): - """ - A callback that can be run the main UI thread to perform saving of the - Package Control.sublime-settings file. Also fires off the - :class:`AutomaticUpgrader`. - - :param installed_pkgs: - A list of the string package names of all "installed" packages, - even ones that do not appear to be in the filesystem. - - :param found_pkgs: - A list of the string package names of all packages that are - currently installed on the filesystem. - """ - - self.save_packages(installed_pkgs) - AutomaticUpgrader(found_pkgs).start() - - -# Start shortly after Sublime starts so package renames don't cause errors -# with keybindings, settings, etc disappearing in the middle of parsing -sublime.set_timeout(lambda: PackageCleanup().start(), 2000) + if st_version == 2: + plugin_loaded() diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.sublime-settings b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.sublime-settings index 2726c3f..03e1594 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.sublime-settings +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/Package Control.sublime-settings @@ -2,18 +2,19 @@ // A list of URLs that each contain a JSON file with a list of repositories. // The repositories from these channels are placed in order after the // repositories from the "repositories" setting - "repository_channels": [ - "https://sublime.wbond.net/repositories.json" + "channels": [ + "https://sublime.wbond.net/channel.json" ], - + // A list of URLs that contain a packages JSON file. These repositories - // are placed in order before repositories from the "repository_channels" + // are placed in order before repositories from the "channels" // setting "repositories": [], // A list of CA certs needed for domains. The default channel provides a // list of domains and an identifier (the md5 hash) for the CA cert(s) - // necessary for each. + // necessary for each. Not used on Windows since the system CA cert list + // is automatically used via WinINet. // // If a custom cert is required for a proxy or for an alternate channel // or repository domain name, it should be added in one of the two forms: @@ -24,21 +25,29 @@ // In both cases the literal "*" means the cert will be checked to ensure // it is present for accessing any URL. This is necessary for proxy // connections, but also useful if you want to provide you own - // ca-bundle.crt file. + // Pckage Control.ca-bundle file. // // The "my_identifier" and "my_identifier_2" can be any unique string // that Package Control can use as a filename, and ensures that it has // merged the cert file with the ca-bundle.crt file in the certs/ directory // since that is what is passed to the downloaders. "certs": { - "api.bitbucket.org": ["d867a7b2aecc46f9c31afc4f2f50de05", ""], - "api.github.com": ["1c5282418e2cb4989cd6beddcdbab0b5", ""], - "bitbucket.org": ["897abe0b41fd2f64e9e2e351cbc36d76", ""], - "nodeload.github.com": ["1c5282418e2cb4989cd6beddcdbab0b5", ""], - "raw.github.com": ["1c5282418e2cb4989cd6beddcdbab0b5", ""], - "sublime.wbond.net": ["7f4f8622b4fd001c7f648e09aae7edaa", ""] + "api.bitbucket.org": ["7d0986b90061d60c8c02aa3b1cf23850", "https://sublime.wbond.net/certs/7d0986b90061d60c8c02aa3b1cf23850"], + "api.github.com": ["7d0986b90061d60c8c02aa3b1cf23850", "https://sublime.wbond.net/certs/7d0986b90061d60c8c02aa3b1cf23850"], + "bitbucket.org": ["7d0986b90061d60c8c02aa3b1cf23850", "https://sublime.wbond.net/certs/7d0986b90061d60c8c02aa3b1cf23850"], + "codeload.github.com": ["7d0986b90061d60c8c02aa3b1cf23850", "https://sublime.wbond.net/certs/7d0986b90061d60c8c02aa3b1cf23850"], + "downloads.sourceforge.net": ["221e907bdfff70d71cea42361ae209d5", "https://sublime.wbond.net/certs/221e907bdfff70d71cea42361ae209d5"], + "github.com": ["7d0986b90061d60c8c02aa3b1cf23850", "https://sublime.wbond.net/certs/7d0986b90061d60c8c02aa3b1cf23850"], + "nodeload.github.com": ["7d0986b90061d60c8c02aa3b1cf23850", "https://sublime.wbond.net/certs/7d0986b90061d60c8c02aa3b1cf23850"], + "raw.github.com": ["7d0986b90061d60c8c02aa3b1cf23850", "https://sublime.wbond.net/certs/7d0986b90061d60c8c02aa3b1cf23850"], + "sublime.wbond.net": ["221e907bdfff70d71cea42361ae209d5", "https://sublime.wbond.net/certs/221e907bdfff70d71cea42361ae209d5"] }, + // Install pre-release versions of packages. If this is false, versions + // under 1.0.0 will still be installed. Only packages using the SemVer + // -prerelease suffixes will be ignored. + "install_prereleases": false, + // If debugging information for HTTP/HTTPS connections should be printed // to the Sublime Text console "debug": false, @@ -53,7 +62,7 @@ // the channel for aggregated statistics "submit_usage": true, - // The URL to post install, upgrade and removal notices to + // The URL to post install, upgrade and removal notices to "submit_url": "https://sublime.wbond.net/submit", // If packages should be automatically upgraded when ST2 starts @@ -69,25 +78,36 @@ // Packages to not auto upgrade "auto_upgrade_ignore": [], - // Timeout for downloading channels, repositories and packages + // Timeout for downloading channels, repositories and packages. Doesn't + // have an effect on Windows due to a bug in WinINet. "timeout": 30, // The number of seconds to cache repository and package info for "cache_length": 300, - // An HTTP proxy server to use for requests + // An HTTP proxy server to use for requests. Not used on Windows since the + // system proxy configuration is utilized via WinINet. "http_proxy": "", // An HTTPS proxy server to use for requests - this will inherit from // http_proxy if it is set to "" or null and http_proxy has a value. You - // can set this to false to prevent inheriting from http_proxy. + // can set this to false to prevent inheriting from http_proxy. Not used on + // Windows since the system proxy configuration is utilized via WinINet. "https_proxy": "", - // Username and password for both http_proxy and https_proxy + // Username and password for both http_proxy and https_proxy. May be used + // with WinINet to set credentials for system-level proxy config. "proxy_username": "", "proxy_password": "", - // User agent for HTTP requests - "user_agent": "Sublime Package Control", + // If HTTP responses should be cached to disk + "http_cache": true, + + // Number of seconds to cache HTTP responses for, defaults to one week + "http_cache_length": 604800, + + // User agent for HTTP requests. If "%s" is present, will be replaced + // with the current version. + "user_agent": "Sublime Package Control v%s", // Setting this to true will cause Package Control to ignore all git // and hg repositories - this may help if trying to list packages to install @@ -97,12 +117,20 @@ // Custom paths to VCS binaries for when they can't be automatically // found on the system and a package includes a VCS metadata directory "git_binary": "", - "git_update_command": ["pull", "origin", "master", "--ff", "--commit"], - + + // This should NOT contain the name of the remote or branch - that will + // be automatically determined. + "git_update_command": ["pull", "--ff", "--commit"], + "hg_binary": "", - - // Be sure to keep the remote name as the last argument - "hg_update_command": ["pull", "--update", "default"], + + // For HG repositories, be sure to use "default" as the remote URL. + // This is the default behavior when cloning an HG repo. + "hg_update_command": ["pull", "--update"], + + // Full path to the openssl binary, if not found on your machine. This is + // only used when running the Grab CA Certs command. + "openssl_binary": "", // Directories to ignore when creating a package "dirs_to_ignore": [ @@ -133,6 +161,6 @@ "__init__.py" ], - // When a package is created, copy it to this folder - defaults to Desktop + // When a package is created, copy it to this folder - defaults to Desktop "package_destination": "" -} \ No newline at end of file +} diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/1c5282418e2cb4989cd6beddcdbab0b5 b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/1c5282418e2cb4989cd6beddcdbab0b5 deleted file mode 100644 index 432b087..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/1c5282418e2cb4989cd6beddcdbab0b5 +++ /dev/null @@ -1,113 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 0a:5f:11:4d:03:5b:17:91:17:d2:ef:d4:03:8c:3f:3b - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA - Validity - Not Before: Apr 2 12:00:00 2008 GMT - Not After : Apr 3 00:00:00 2022 GMT - Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance CA-3 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) - Modulus: - 00:bf:61:0a:29:10:1f:5e:fe:34:37:51:08:f8:1e: - fb:22:ed:61:be:0b:0d:70:4c:50:63:26:75:15:b9: - 41:88:97:b6:f0:a0:15:bb:08:60:e0:42:e8:05:29: - 10:87:36:8a:28:65:a8:ef:31:07:74:6d:36:97:2f: - 28:46:66:04:c7:2a:79:26:7a:99:d5:8e:c3:6d:4f: - a0:5e:ad:bc:3d:91:c2:59:7b:5e:36:6c:c0:53:cf: - 00:08:32:3e:10:64:58:10:13:69:c7:0c:ee:9c:42: - 51:00:f9:05:44:ee:24:ce:7a:1f:ed:8c:11:bd:12: - a8:f3:15:f4:1c:7a:31:69:01:1b:a7:e6:5d:c0:9a: - 6c:7e:09:9e:e7:52:44:4a:10:3a:23:e4:9b:b6:03: - af:a8:9c:b4:5b:9f:d4:4b:ad:92:8c:ce:b5:11:2a: - aa:37:18:8d:b4:c2:b8:d8:5c:06:8c:f8:ff:23:bd: - 35:5e:d4:7c:3e:7e:83:0e:91:96:05:98:c3:b2:1f: - e3:c8:65:eb:a9:7b:5d:a0:2c:cc:fc:3c:d9:6d:ed: - cc:fa:4b:43:8c:c9:d4:b8:a5:61:1c:b2:40:b6:28: - 12:df:b9:f8:5f:fe:d3:b2:c9:ef:3d:b4:1e:4b:7c: - 1c:4c:99:36:9e:3d:eb:ec:a7:68:5e:1d:df:67:6e: - 5e:fb - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Certificate Sign, CRL Sign - X509v3 Certificate Policies: - Policy: 2.16.840.1.114412.1.3.0.2 - CPS: http://www.digicert.com/ssl-cps-repository.htm - User Notice: - Explicit Text: - - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - Authority Information Access: - OCSP - URI:http://ocsp.digicert.com - - X509v3 CRL Distribution Points: - - Full Name: - URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl - - Full Name: - URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl - - X509v3 Authority Key Identifier: - keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 - - X509v3 Subject Key Identifier: - 50:EA:73:89:DB:29:FB:10:8F:9E:E5:01:20:D4:DE:79:99:48:83:F7 - Signature Algorithm: sha1WithRSAEncryption - 1e:e2:a5:48:9e:6c:db:53:38:0f:ef:a6:1a:2a:ac:e2:03:43: - ed:9a:bc:3e:8e:75:1b:f0:fd:2e:22:59:ac:13:c0:61:e2:e7: - fa:e9:99:cd:87:09:75:54:28:bf:46:60:dc:be:51:2c:92:f3: - 1b:91:7c:31:08:70:e2:37:b9:c1:5b:a8:bd:a3:0b:00:fb:1a: - 15:fd:03:ad:58:6a:c5:c7:24:99:48:47:46:31:1e:92:ef:b4: - 5f:4e:34:c7:90:bf:31:c1:f8:b1:84:86:d0:9c:01:aa:df:8a: - 56:06:ce:3a:e9:0e:ae:97:74:5d:d7:71:9a:42:74:5f:de:8d: - 43:7c:de:e9:55:ed:69:00:cb:05:e0:7a:61:61:33:d1:19:4d: - f9:08:ee:a0:39:c5:25:35:b7:2b:c4:0f:b2:dd:f1:a5:b7:0e: - 24:c4:26:28:8d:79:77:f5:2f:f0:57:ba:7c:07:d4:e1:fc:cd: - 5a:30:57:7e:86:10:47:dd:31:1f:d7:fc:a2:c2:bf:30:7c:5d: - 24:aa:e8:f9:ae:5f:6a:74:c2:ce:6b:b3:46:d8:21:be:29:d4: - 8e:5e:15:d6:42:4a:e7:32:6f:a4:b1:6b:51:83:58:be:3f:6d: - c7:fb:da:03:21:cb:6a:16:19:4e:0a:f0:ad:84:ca:5d:94:b3: - 5a:76:f7:61 ------BEGIN CERTIFICATE----- -MIIGWDCCBUCgAwIBAgIQCl8RTQNbF5EX0u/UA4w/OzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA4MDQwMjEyMDAwMFoXDTIyMDQwMzAwMDAwMFowZjEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTElMCMGA1UEAxMcRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -Q0EtMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9hCikQH17+NDdR -CPge+yLtYb4LDXBMUGMmdRW5QYiXtvCgFbsIYOBC6AUpEIc2iihlqO8xB3RtNpcv -KEZmBMcqeSZ6mdWOw21PoF6tvD2Rwll7XjZswFPPAAgyPhBkWBATaccM7pxCUQD5 -BUTuJM56H+2MEb0SqPMV9Bx6MWkBG6fmXcCabH4JnudSREoQOiPkm7YDr6ictFuf -1EutkozOtREqqjcYjbTCuNhcBoz4/yO9NV7UfD5+gw6RlgWYw7If48hl66l7XaAs -zPw82W3tzPpLQ4zJ1LilYRyyQLYoEt+5+F/+07LJ7z20Hkt8HEyZNp496+ynaF4d -32duXvsCAwEAAaOCAvowggL2MA4GA1UdDwEB/wQEAwIBhjCCAcYGA1UdIASCAb0w -ggG5MIIBtQYLYIZIAYb9bAEDAAIwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3 -LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH -AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy -AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj -AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg -AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ -AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt -AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj -AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl -AHIAZQBuAGMAZQAuMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYIKwYBBQUHAQEEKDAm -MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgY8GA1UdHwSB -hzCBhDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGln -aEFzc3VyYW5jZUVWUm9vdENBLmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNl -cnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDAfBgNVHSME -GDAWgBSxPsNpA/i/RwHUmCYaCALvY2QrwzAdBgNVHQ4EFgQUUOpzidsp+xCPnuUB -INTeeZlIg/cwDQYJKoZIhvcNAQEFBQADggEBAB7ipUiebNtTOA/vphoqrOIDQ+2a -vD6OdRvw/S4iWawTwGHi5/rpmc2HCXVUKL9GYNy+USyS8xuRfDEIcOI3ucFbqL2j -CwD7GhX9A61YasXHJJlIR0YxHpLvtF9ONMeQvzHB+LGEhtCcAarfilYGzjrpDq6X -dF3XcZpCdF/ejUN83ulV7WkAywXgemFhM9EZTfkI7qA5xSU1tyvED7Ld8aW3DiTE -JiiNeXf1L/BXunwH1OH8zVowV36GEEfdMR/X/KLCvzB8XSSq6PmuX2p0ws5rs0bY -Ib4p1I5eFdZCSucyb6Sxa1GDWL4/bcf72gMhy2oWGU4K8K2Eyl2Us1p292E= ------END CERTIFICATE----- \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/7f4f8622b4fd001c7f648e09aae7edaa b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/7f4f8622b4fd001c7f648e09aae7edaa deleted file mode 100644 index cb680c1..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/7f4f8622b4fd001c7f648e09aae7edaa +++ /dev/null @@ -1,165 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 145105 (0x236d1) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA - Validity - Not Before: Feb 19 22:45:05 2010 GMT - Not After : Feb 18 22:45:05 2020 GMT - Subject: C=US, O=GeoTrust, Inc., CN=RapidSSL CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:c7:71:f8:56:c7:1e:d9:cc:b5:ad:f6:b4:97:a3: - fb:a1:e6:0b:50:5f:50:aa:3a:da:0f:fc:3d:29:24: - 43:c6:10:29:c1:fc:55:40:72:ee:bd:ea:df:9f:b6: - 41:f4:48:4b:c8:6e:fe:4f:57:12:8b:5b:fa:92:dd: - 5e:e8:ad:f3:f0:1b:b1:7b:4d:fb:cf:fd:d1:e5:f8: - e3:dc:e7:f5:73:7f:df:01:49:cf:8c:56:c1:bd:37: - e3:5b:be:b5:4f:8b:8b:f0:da:4f:c7:e3:dd:55:47: - 69:df:f2:5b:7b:07:4f:3d:e5:ac:21:c1:c8:1d:7a: - e8:e7:f6:0f:a1:aa:f5:6f:de:a8:65:4f:10:89:9c: - 03:f3:89:7a:a5:5e:01:72:33:ed:a9:e9:5a:1e:79: - f3:87:c8:df:c8:c5:fc:37:c8:9a:9a:d7:b8:76:cc: - b0:3e:e7:fd:e6:54:ea:df:5f:52:41:78:59:57:ad: - f1:12:d6:7f:bc:d5:9f:70:d3:05:6c:fa:a3:7d:67: - 58:dd:26:62:1d:31:92:0c:79:79:1c:8e:cf:ca:7b: - c1:66:af:a8:74:48:fb:8e:82:c2:9e:2c:99:5c:7b: - 2d:5d:9b:bc:5b:57:9e:7c:3a:7a:13:ad:f2:a3:18: - 5b:2b:59:0f:cd:5c:3a:eb:68:33:c6:28:1d:82:d1: - 50:8b - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Certificate Sign, CRL Sign - X509v3 Subject Key Identifier: - 6B:69:3D:6A:18:42:4A:DD:8F:02:65:39:FD:35:24:86:78:91:16:30 - X509v3 Authority Key Identifier: - keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E - - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - X509v3 CRL Distribution Points: - URI:http://crl.geotrust.com/crls/gtglobal.crl - - Authority Information Access: - OCSP - URI:http://ocsp.geotrust.com - - Signature Algorithm: sha1WithRSAEncryption - ab:bc:bc:0a:5d:18:94:e3:c1:b1:c3:a8:4c:55:d6:be:b4:98: - f1:ee:3c:1c:cd:cf:f3:24:24:5c:96:03:27:58:fc:36:ae:a2: - 2f:8f:f1:fe:da:2b:02:c3:33:bd:c8:dd:48:22:2b:60:0f:a5: - 03:10:fd:77:f8:d0:ed:96:67:4f:fd:ea:47:20:70:54:dc:a9: - 0c:55:7e:e1:96:25:8a:d9:b5:da:57:4a:be:8d:8e:49:43:63: - a5:6c:4e:27:87:25:eb:5b:6d:fe:a2:7f:38:28:e0:36:ab:ad: - 39:a5:a5:62:c4:b7:5c:58:2c:aa:5d:01:60:a6:62:67:a3:c0: - c7:62:23:f4:e7:6c:46:ee:b5:d3:80:6a:22:13:d2:2d:3f:74: - 4f:ea:af:8c:5f:b4:38:9c:db:ae:ce:af:84:1e:a6:f6:34:51: - 59:79:d3:e3:75:dc:bc:d7:f3:73:df:92:ec:d2:20:59:6f:9c: - fb:95:f8:92:76:18:0a:7c:0f:2c:a6:ca:de:8a:62:7b:d8:f3: - ce:5f:68:bd:8f:3e:c1:74:bb:15:72:3a:16:83:a9:0b:e6:4d: - 99:9c:d8:57:ec:a8:01:51:c7:6f:57:34:5e:ab:4a:2c:42:f6: - 4f:1c:89:78:de:26:4e:f5:6f:93:4c:15:6b:27:56:4d:00:54: - 6c:7a:b7:b7 ------BEGIN CERTIFICATE----- -MIID1TCCAr2gAwIBAgIDAjbRMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT -MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i -YWwgQ0EwHhcNMTAwMjE5MjI0NTA1WhcNMjAwMjE4MjI0NTA1WjA8MQswCQYDVQQG -EwJVUzEXMBUGA1UEChMOR2VvVHJ1c3QsIEluYy4xFDASBgNVBAMTC1JhcGlkU1NM -IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx3H4Vsce2cy1rfa0 -l6P7oeYLUF9QqjraD/w9KSRDxhApwfxVQHLuverfn7ZB9EhLyG7+T1cSi1v6kt1e -6K3z8Buxe037z/3R5fjj3Of1c3/fAUnPjFbBvTfjW761T4uL8NpPx+PdVUdp3/Jb -ewdPPeWsIcHIHXro5/YPoar1b96oZU8QiZwD84l6pV4BcjPtqelaHnnzh8jfyMX8 -N8iamte4dsywPuf95lTq319SQXhZV63xEtZ/vNWfcNMFbPqjfWdY3SZiHTGSDHl5 -HI7PynvBZq+odEj7joLCniyZXHstXZu8W1eefDp6E63yoxhbK1kPzVw662gzxigd -gtFQiwIDAQABo4HZMIHWMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUa2k9ahhC -St2PAmU5/TUkhniRFjAwHwYDVR0jBBgwFoAUwHqYaI2J+6sFZAwRfap9ZbjKzE4w -EgYDVR0TAQH/BAgwBgEB/wIBADA6BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3Js -Lmdlb3RydXN0LmNvbS9jcmxzL2d0Z2xvYmFsLmNybDA0BggrBgEFBQcBAQQoMCYw -JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmdlb3RydXN0LmNvbTANBgkqhkiG9w0B -AQUFAAOCAQEAq7y8Cl0YlOPBscOoTFXWvrSY8e48HM3P8yQkXJYDJ1j8Nq6iL4/x -/torAsMzvcjdSCIrYA+lAxD9d/jQ7ZZnT/3qRyBwVNypDFV+4ZYlitm12ldKvo2O -SUNjpWxOJ4cl61tt/qJ/OCjgNqutOaWlYsS3XFgsql0BYKZiZ6PAx2Ij9OdsRu61 -04BqIhPSLT90T+qvjF+0OJzbrs6vhB6m9jRRWXnT43XcvNfzc9+S7NIgWW+c+5X4 -knYYCnwPLKbK3opie9jzzl9ovY8+wXS7FXI6FoOpC+ZNmZzYV+yoAVHHb1c0XqtK -LEL2TxyJeN4mTvVvk0wVaydWTQBUbHq3tw== ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 144470 (0x23456) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA - Validity - Not Before: May 21 04:00:00 2002 GMT - Not After : May 21 04:00:00 2022 GMT - Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:da:cc:18:63:30:fd:f4:17:23:1a:56:7e:5b:df: - 3c:6c:38:e4:71:b7:78:91:d4:bc:a1:d8:4c:f8:a8: - 43:b6:03:e9:4d:21:07:08:88:da:58:2f:66:39:29: - bd:05:78:8b:9d:38:e8:05:b7:6a:7e:71:a4:e6:c4: - 60:a6:b0:ef:80:e4:89:28:0f:9e:25:d6:ed:83:f3: - ad:a6:91:c7:98:c9:42:18:35:14:9d:ad:98:46:92: - 2e:4f:ca:f1:87:43:c1:16:95:57:2d:50:ef:89:2d: - 80:7a:57:ad:f2:ee:5f:6b:d2:00:8d:b9:14:f8:14: - 15:35:d9:c0:46:a3:7b:72:c8:91:bf:c9:55:2b:cd: - d0:97:3e:9c:26:64:cc:df:ce:83:19:71:ca:4e:e6: - d4:d5:7b:a9:19:cd:55:de:c8:ec:d2:5e:38:53:e5: - 5c:4f:8c:2d:fe:50:23:36:fc:66:e6:cb:8e:a4:39: - 19:00:b7:95:02:39:91:0b:0e:fe:38:2e:d1:1d:05: - 9a:f6:4d:3e:6f:0f:07:1d:af:2c:1e:8f:60:39:e2: - fa:36:53:13:39:d4:5e:26:2b:db:3d:a8:14:bd:32: - eb:18:03:28:52:04:71:e5:ab:33:3d:e1:38:bb:07: - 36:84:62:9c:79:ea:16:30:f4:5f:c0:2b:e8:71:6b: - e4:f9 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Subject Key Identifier: - C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E - X509v3 Authority Key Identifier: - keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E - - Signature Algorithm: sha1WithRSAEncryption - 35:e3:29:6a:e5:2f:5d:54:8e:29:50:94:9f:99:1a:14:e4:8f: - 78:2a:62:94:a2:27:67:9e:d0:cf:1a:5e:47:e9:c1:b2:a4:cf: - dd:41:1a:05:4e:9b:4b:ee:4a:6f:55:52:b3:24:a1:37:0a:eb: - 64:76:2a:2e:2c:f3:fd:3b:75:90:bf:fa:71:d8:c7:3d:37:d2: - b5:05:95:62:b9:a6:de:89:3d:36:7b:38:77:48:97:ac:a6:20: - 8f:2e:a6:c9:0c:c2:b2:99:45:00:c7:ce:11:51:22:22:e0:a5: - ea:b6:15:48:09:64:ea:5e:4f:74:f7:05:3e:c7:8a:52:0c:db: - 15:b4:bd:6d:9b:e5:c6:b1:54:68:a9:e3:69:90:b6:9a:a5:0f: - b8:b9:3f:20:7d:ae:4a:b5:b8:9c:e4:1d:b6:ab:e6:94:a5:c1: - c7:83:ad:db:f5:27:87:0e:04:6c:d5:ff:dd:a0:5d:ed:87:52: - b7:2b:15:02:ae:39:a6:6a:74:e9:da:c4:e7:bc:4d:34:1e:a9: - 5c:4d:33:5f:92:09:2f:88:66:5d:77:97:c7:1d:76:13:a9:d5: - e5:f1:16:09:11:35:d5:ac:db:24:71:70:2c:98:56:0b:d9:17: - b4:d1:e3:51:2b:5e:75:e8:d5:d0:dc:4f:34:ed:c2:05:66:80: - a1:cb:e6:33 ------BEGIN CERTIFICATE----- -MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT -MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i -YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG -EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg -R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 -9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq -fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv -iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU -1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ -bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW -MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA -ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l -uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn -Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS -tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF -PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un -hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV -5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== ------END CERTIFICATE----- \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/897abe0b41fd2f64e9e2e351cbc36d76 b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/897abe0b41fd2f64e9e2e351cbc36d76 deleted file mode 100644 index 591907f..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/897abe0b41fd2f64e9e2e351cbc36d76 +++ /dev/null @@ -1,285 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 03:37:b9:28:34:7c:60:a6:ae:c5:ad:b1:21:7f:38:60 - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA - Validity - Not Before: Nov 9 12:00:00 2007 GMT - Not After : Nov 10 00:00:00 2021 GMT - Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV CA-1 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:f3:96:62:d8:75:6e:19:ff:3f:34:7c:49:4f:31: - 7e:0d:04:4e:99:81:e2:b3:85:55:91:30:b1:c0:af: - 70:bb:2c:a8:e7:18:aa:3f:78:f7:90:68:52:86:01: - 88:97:e2:3b:06:65:90:aa:bd:65:76:c2:ec:be:10: - 5b:37:78:83:60:75:45:c6:bd:74:aa:b6:9f:a4:3a: - 01:50:17:c4:39:69:b9:f1:4f:ef:82:c1:ca:f3:4a: - db:cc:9e:50:4f:4d:40:a3:3a:90:e7:86:66:bc:f0: - 3e:76:28:4c:d1:75:80:9e:6a:35:14:35:03:9e:db: - 0c:8c:c2:28:ad:50:b2:ce:f6:91:a3:c3:a5:0a:58: - 49:f6:75:44:6c:ba:f9:ce:e9:ab:3a:02:e0:4d:f3: - ac:e2:7a:e0:60:22:05:3c:82:d3:52:e2:f3:9c:47: - f8:3b:d8:b2:4b:93:56:4a:bf:70:ab:3e:e9:68:c8: - 1d:8f:58:1d:2a:4d:5e:27:3d:ad:0a:59:2f:5a:11: - 20:40:d9:68:04:68:2d:f4:c0:84:0b:0a:1b:78:df: - ed:1a:58:dc:fb:41:5a:6d:6b:f2:ed:1c:ee:5c:32: - b6:5c:ec:d7:a6:03:32:a6:e8:de:b7:28:27:59:88: - 80:ff:7b:ad:89:58:d5:1e:14:a4:f2:b0:70:d4:a0: - 3e:a7 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Certificate Sign, CRL Sign - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication, Code Signing, E-mail Protection, Time Stamping - X509v3 Certificate Policies: - Policy: 2.16.840.1.114412.2.1 - CPS: http://www.digicert.com/ssl-cps-repository.htm - User Notice: - Explicit Text: - - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - Authority Information Access: - OCSP - URI:http://ocsp.digicert.com - CA Issuers - URI:http://www.digicert.com/CACerts/DigiCertHighAssuranceEVRootCA.crt - - X509v3 CRL Distribution Points: - URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl - URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl - - X509v3 Subject Key Identifier: - 4C:58:CB:25:F0:41:4F:52:F4:28:C8:81:43:9B:A6:A8:A0:E6:92:E5 - X509v3 Authority Key Identifier: - keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 - - Signature Algorithm: sha1WithRSAEncryption - 4c:7a:17:87:28:5d:17:bc:b2:32:73:bf:cd:2e:f5:58:31:1d: - f0:b1:71:54:9c:d6:9b:67:93:db:2f:03:3e:16:6f:1e:03:c9: - 53:84:a3:56:60:1e:78:94:1b:a2:a8:6f:a3:a4:8b:52:91:d7: - dd:5c:95:bb:ef:b5:16:49:e9:a5:42:4f:34:f2:47:ff:ae:81: - 7f:13:54:b7:20:c4:70:15:cb:81:0a:81:cb:74:57:dc:9c:df: - 24:a4:29:0c:18:f0:1c:e4:ae:07:33:ec:f1:49:3e:55:cf:6e: - 4f:0d:54:7b:d3:c9:e8:15:48:d4:c5:bb:dc:35:1c:77:45:07: - 48:45:85:bd:d7:7e:53:b8:c0:16:d9:95:cd:8b:8d:7d:c9:60: - 4f:d1:a2:9b:e3:d0:30:d6:b4:73:36:e6:d2:f9:03:b2:e3:a4: - f5:e5:b8:3e:04:49:00:ba:2e:a6:4a:72:83:72:9d:f7:0b:8c: - a9:89:e7:b3:d7:64:1f:d6:e3:60:cb:03:c4:dc:88:e9:9d:25: - 01:00:71:cb:03:b4:29:60:25:8f:f9:46:d1:7b:71:ae:cd:53: - 12:5b:84:8e:c2:0f:c7:ed:93:19:d9:c9:fa:8f:58:34:76:32: - 2f:ae:e1:50:14:61:d4:a8:58:a3:c8:30:13:23:ef:c6:25:8c: - 36:8f:1c:80 ------BEGIN CERTIFICATE----- -MIIG5jCCBc6gAwIBAgIQAze5KDR8YKauxa2xIX84YDANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA3MTEwOTEyMDAwMFoXDTIxMTExMDAwMDAwMFowaTEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTEoMCYGA1UEAxMfRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -RVYgQ0EtMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPOWYth1bhn/ -PzR8SU8xfg0ETpmB4rOFVZEwscCvcLssqOcYqj9495BoUoYBiJfiOwZlkKq9ZXbC -7L4QWzd4g2B1Rca9dKq2n6Q6AVAXxDlpufFP74LByvNK28yeUE9NQKM6kOeGZrzw -PnYoTNF1gJ5qNRQ1A57bDIzCKK1Qss72kaPDpQpYSfZ1RGy6+c7pqzoC4E3zrOJ6 -4GAiBTyC01Li85xH+DvYskuTVkq/cKs+6WjIHY9YHSpNXic9rQpZL1oRIEDZaARo -LfTAhAsKG3jf7RpY3PtBWm1r8u0c7lwytlzs16YDMqbo3rcoJ1mIgP97rYlY1R4U -pPKwcNSgPqcCAwEAAaOCA4UwggOBMA4GA1UdDwEB/wQEAwIBhjA7BgNVHSUENDAy -BggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUH -AwgwggHEBgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwCATCCAaQwOgYIKwYBBQUH -AgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5o -dG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0 -AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1 -AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABp -AGcAaQBDAGUAcgB0ACAARQBWACAAQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBl -AGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBo -AGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg -AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAg -AGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wEgYDVR0TAQH/BAgwBgEB/wIBADCB -gwYIKwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy -dC5jb20wTQYIKwYBBQUHMAKGQWh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NBQ2Vy -dHMvRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3J0MIGPBgNVHR8EgYcw -gYQwQKA+oDyGOmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hB -c3N1cmFuY2VFVlJvb3RDQS5jcmwwQKA+oDyGOmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0 -LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwHQYDVR0OBBYE -FExYyyXwQU9S9CjIgUObpqig5pLlMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoI -Au9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQBMeheHKF0XvLIyc7/NLvVYMR3wsXFU -nNabZ5PbLwM+Fm8eA8lThKNWYB54lBuiqG+jpItSkdfdXJW777UWSemlQk808kf/ -roF/E1S3IMRwFcuBCoHLdFfcnN8kpCkMGPAc5K4HM+zxST5Vz25PDVR708noFUjU -xbvcNRx3RQdIRYW9135TuMAW2ZXNi419yWBP0aKb49Aw1rRzNubS+QOy46T15bg+ -BEkAui6mSnKDcp33C4ypieez12Qf1uNgywPE3IjpnSUBAHHLA7QpYCWP+UbRe3Gu -zVMSW4SOwg/H7ZMZ2cn6j1g0djIvruFQFGHUqFijyDATI+/GJYw2jxyA ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 1116160165 (0x428740a5) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority - Validity - Not Before: Oct 1 05:00:00 2006 GMT - Not After : Jul 26 18:15:15 2014 GMT - Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:c6:cc:e5:73:e6:fb:d4:bb:e5:2d:2d:32:a6:df: - e5:81:3f:c9:cd:25:49:b6:71:2a:c3:d5:94:34:67: - a2:0a:1c:b0:5f:69:a6:40:b1:c4:b7:b2:8f:d0:98: - a4:a9:41:59:3a:d3:dc:94:d6:3c:db:74:38:a4:4a: - cc:4d:25:82:f7:4a:a5:53:12:38:ee:f3:49:6d:71: - 91:7e:63:b6:ab:a6:5f:c3:a4:84:f8:4f:62:51:be: - f8:c5:ec:db:38:92:e3:06:e5:08:91:0c:c4:28:41: - 55:fb:cb:5a:89:15:7e:71:e8:35:bf:4d:72:09:3d: - be:3a:38:50:5b:77:31:1b:8d:b3:c7:24:45:9a:a7: - ac:6d:00:14:5a:04:b7:ba:13:eb:51:0a:98:41:41: - 22:4e:65:61:87:81:41:50:a6:79:5c:89:de:19:4a: - 57:d5:2e:e6:5d:1c:53:2c:7e:98:cd:1a:06:16:a4: - 68:73:d0:34:04:13:5c:a1:71:d3:5a:7c:55:db:5e: - 64:e1:37:87:30:56:04:e5:11:b4:29:80:12:f1:79: - 39:88:a2:02:11:7c:27:66:b7:88:b7:78:f2:ca:0a: - a8:38:ab:0a:64:c2:bf:66:5d:95:84:c1:a1:25:1e: - 87:5d:1a:50:0b:20:12:cc:41:bb:6e:0b:51:38:b8: - 4b:cb - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:1 - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication, E-mail Protection - Authority Information Access: - OCSP - URI:http://ocsp.entrust.net - - X509v3 CRL Distribution Points: - URI:http://crl.entrust.net/server1.crl - - X509v3 Subject Key Identifier: - B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 - X509v3 Key Usage: - Certificate Sign, CRL Sign - X509v3 Authority Key Identifier: - keyid:F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A - - 1.2.840.113533.7.65.0: - 0 -..V7.1.... - Signature Algorithm: sha1WithRSAEncryption - 48:0e:2b:6f:20:62:4c:28:93:a3:24:3d:58:ab:21:cf:80:f8: - 9a:97:90:6a:22:ed:5a:7c:47:36:99:e7:79:84:75:ab:24:8f: - 92:0a:d5:61:04:ae:c3:6a:5c:b2:cc:d9:e4:44:87:6f:db:8f: - 38:62:f7:44:36:9d:ba:bc:6e:07:c4:d4:8d:e8:1f:d1:0b:60: - a3:b5:9c:ce:63:be:ed:67:dc:f8:ba:de:6e:c9:25:cb:5b:b5: - 9d:76:70:0b:df:42:72:f8:4f:41:11:64:a5:d2:ea:fc:d5:af: - 11:f4:15:38:67:9c:20:a8:4b:77:5a:91:32:42:32:e7:85:b3: - df:36 ------BEGIN CERTIFICATE----- -MIIEQjCCA6ugAwIBAgIEQodApTANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC -VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u -ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc -KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u -ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEw -MDEwNTAwMDBaFw0xNDA3MjYxODE1MTVaMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQK -EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNV -BAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGzOVz5vvUu+UtLTKm3+WBP8nNJUm2cSrD -1ZQ0Z6IKHLBfaaZAscS3so/QmKSpQVk609yU1jzbdDikSsxNJYL3SqVTEjju80lt -cZF+Y7arpl/DpIT4T2JRvvjF7Ns4kuMG5QiRDMQoQVX7y1qJFX5x6DW/TXIJPb46 -OFBbdzEbjbPHJEWap6xtABRaBLe6E+tRCphBQSJOZWGHgUFQpnlcid4ZSlfVLuZd -HFMsfpjNGgYWpGhz0DQEE1yhcdNafFXbXmThN4cwVgTlEbQpgBLxeTmIogIRfCdm -t4i3ePLKCqg4qwpkwr9mXZWEwaElHoddGlALIBLMQbtuC1E4uEvLAgMBAAGjggET -MIIBDzASBgNVHRMBAf8ECDAGAQH/AgEBMCcGA1UdJQQgMB4GCCsGAQUFBwMBBggr -BgEFBQcDAgYIKwYBBQUHAwQwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdo -dHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8v -Y3JsLmVudHJ1c3QubmV0L3NlcnZlcjEuY3JsMB0GA1UdDgQWBBSxPsNpA/i/RwHU -mCYaCALvY2QrwzALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7 -UISX8+1i0BowGQYJKoZIhvZ9B0EABAwwChsEVjcuMQMCAIEwDQYJKoZIhvcNAQEF -BQADgYEASA4rbyBiTCiToyQ9WKshz4D4mpeQaiLtWnxHNpnneYR1qySPkgrVYQSu -w2pcsszZ5ESHb9uPOGL3RDadurxuB8TUjegf0Qtgo7WczmO+7Wfc+Lrebskly1u1 -nXZwC99CcvhPQRFkpdLq/NWvEfQVOGecIKhLd1qRMkIy54Wz3zY= ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 927650371 (0x374ad243) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority - Validity - Not Before: May 25 16:09:40 1999 GMT - Not After : May 25 16:39:40 2019 GMT - Subject: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (1024 bit) - Modulus (1024 bit): - 00:cd:28:83:34:54:1b:89:f3:0f:af:37:91:31:ff: - af:31:60:c9:a8:e8:b2:10:68:ed:9f:e7:93:36:f1: - 0a:64:bb:47:f5:04:17:3f:23:47:4d:c5:27:19:81: - 26:0c:54:72:0d:88:2d:d9:1f:9a:12:9f:bc:b3:71: - d3:80:19:3f:47:66:7b:8c:35:28:d2:b9:0a:df:24: - da:9c:d6:50:79:81:7a:5a:d3:37:f7:c2:4a:d8:29: - 92:26:64:d1:e4:98:6c:3a:00:8a:f5:34:9b:65:f8: - ed:e3:10:ff:fd:b8:49:58:dc:a0:de:82:39:6b:81: - b1:16:19:61:b9:54:b6:e6:43 - Exponent: 3 (0x3) - X509v3 extensions: - Netscape Cert Type: - SSL CA, S/MIME CA, Object Signing CA - X509v3 CRL Distribution Points: - DirName:/C=US/O=Entrust.net/OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/OU=(c) 1999 Entrust.net Limited/CN=Entrust.net Secure Server Certification Authority/CN=CRL1 - URI:http://www.entrust.net/CRL/net1.crl - - X509v3 Private Key Usage Period: - Not Before: May 25 16:09:40 1999 GMT, Not After: May 25 16:09:40 2019 GMT - X509v3 Key Usage: - Certificate Sign, CRL Sign - X509v3 Authority Key Identifier: - keyid:F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A - - X509v3 Subject Key Identifier: - F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A - X509v3 Basic Constraints: - CA:TRUE - 1.2.840.113533.7.65.0: - 0 -..V4.0.... - Signature Algorithm: sha1WithRSAEncryption - 90:dc:30:02:fa:64:74:c2:a7:0a:a5:7c:21:8d:34:17:a8:fb: - 47:0e:ff:25:7c:8d:13:0a:fb:e4:98:b5:ef:8c:f8:c5:10:0d: - f7:92:be:f1:c3:d5:d5:95:6a:04:bb:2c:ce:26:36:65:c8:31: - c6:e7:ee:3f:e3:57:75:84:7a:11:ef:46:4f:18:f4:d3:98:bb: - a8:87:32:ba:72:f6:3c:e2:3d:9f:d7:1d:d9:c3:60:43:8c:58: - 0e:22:96:2f:62:a3:2c:1f:ba:ad:05:ef:ab:32:78:87:a0:54: - 73:19:b5:5c:05:f9:52:3e:6d:2d:45:0b:f7:0a:93:ea:ed:06: - f9:b2 ------BEGIN CERTIFICATE----- -MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC -VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u -ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc -KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u -ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1 -MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE -ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j -b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF -bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg -U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA -A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/ -I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3 -wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC -AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb -oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5 -BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p -dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk -MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp -b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu -dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0 -MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi -E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa -MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI -hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN -95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd -2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= ------END CERTIFICATE----- \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/ca-bundle.crt b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/ca-bundle.crt deleted file mode 100644 index 1681842..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/ca-bundle.crt +++ /dev/null @@ -1,563 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 145105 (0x236d1) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA - Validity - Not Before: Feb 19 22:45:05 2010 GMT - Not After : Feb 18 22:45:05 2020 GMT - Subject: C=US, O=GeoTrust, Inc., CN=RapidSSL CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:c7:71:f8:56:c7:1e:d9:cc:b5:ad:f6:b4:97:a3: - fb:a1:e6:0b:50:5f:50:aa:3a:da:0f:fc:3d:29:24: - 43:c6:10:29:c1:fc:55:40:72:ee:bd:ea:df:9f:b6: - 41:f4:48:4b:c8:6e:fe:4f:57:12:8b:5b:fa:92:dd: - 5e:e8:ad:f3:f0:1b:b1:7b:4d:fb:cf:fd:d1:e5:f8: - e3:dc:e7:f5:73:7f:df:01:49:cf:8c:56:c1:bd:37: - e3:5b:be:b5:4f:8b:8b:f0:da:4f:c7:e3:dd:55:47: - 69:df:f2:5b:7b:07:4f:3d:e5:ac:21:c1:c8:1d:7a: - e8:e7:f6:0f:a1:aa:f5:6f:de:a8:65:4f:10:89:9c: - 03:f3:89:7a:a5:5e:01:72:33:ed:a9:e9:5a:1e:79: - f3:87:c8:df:c8:c5:fc:37:c8:9a:9a:d7:b8:76:cc: - b0:3e:e7:fd:e6:54:ea:df:5f:52:41:78:59:57:ad: - f1:12:d6:7f:bc:d5:9f:70:d3:05:6c:fa:a3:7d:67: - 58:dd:26:62:1d:31:92:0c:79:79:1c:8e:cf:ca:7b: - c1:66:af:a8:74:48:fb:8e:82:c2:9e:2c:99:5c:7b: - 2d:5d:9b:bc:5b:57:9e:7c:3a:7a:13:ad:f2:a3:18: - 5b:2b:59:0f:cd:5c:3a:eb:68:33:c6:28:1d:82:d1: - 50:8b - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Certificate Sign, CRL Sign - X509v3 Subject Key Identifier: - 6B:69:3D:6A:18:42:4A:DD:8F:02:65:39:FD:35:24:86:78:91:16:30 - X509v3 Authority Key Identifier: - keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E - - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - X509v3 CRL Distribution Points: - URI:http://crl.geotrust.com/crls/gtglobal.crl - - Authority Information Access: - OCSP - URI:http://ocsp.geotrust.com - - Signature Algorithm: sha1WithRSAEncryption - ab:bc:bc:0a:5d:18:94:e3:c1:b1:c3:a8:4c:55:d6:be:b4:98: - f1:ee:3c:1c:cd:cf:f3:24:24:5c:96:03:27:58:fc:36:ae:a2: - 2f:8f:f1:fe:da:2b:02:c3:33:bd:c8:dd:48:22:2b:60:0f:a5: - 03:10:fd:77:f8:d0:ed:96:67:4f:fd:ea:47:20:70:54:dc:a9: - 0c:55:7e:e1:96:25:8a:d9:b5:da:57:4a:be:8d:8e:49:43:63: - a5:6c:4e:27:87:25:eb:5b:6d:fe:a2:7f:38:28:e0:36:ab:ad: - 39:a5:a5:62:c4:b7:5c:58:2c:aa:5d:01:60:a6:62:67:a3:c0: - c7:62:23:f4:e7:6c:46:ee:b5:d3:80:6a:22:13:d2:2d:3f:74: - 4f:ea:af:8c:5f:b4:38:9c:db:ae:ce:af:84:1e:a6:f6:34:51: - 59:79:d3:e3:75:dc:bc:d7:f3:73:df:92:ec:d2:20:59:6f:9c: - fb:95:f8:92:76:18:0a:7c:0f:2c:a6:ca:de:8a:62:7b:d8:f3: - ce:5f:68:bd:8f:3e:c1:74:bb:15:72:3a:16:83:a9:0b:e6:4d: - 99:9c:d8:57:ec:a8:01:51:c7:6f:57:34:5e:ab:4a:2c:42:f6: - 4f:1c:89:78:de:26:4e:f5:6f:93:4c:15:6b:27:56:4d:00:54: - 6c:7a:b7:b7 ------BEGIN CERTIFICATE----- -MIID1TCCAr2gAwIBAgIDAjbRMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT -MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i -YWwgQ0EwHhcNMTAwMjE5MjI0NTA1WhcNMjAwMjE4MjI0NTA1WjA8MQswCQYDVQQG -EwJVUzEXMBUGA1UEChMOR2VvVHJ1c3QsIEluYy4xFDASBgNVBAMTC1JhcGlkU1NM -IENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx3H4Vsce2cy1rfa0 -l6P7oeYLUF9QqjraD/w9KSRDxhApwfxVQHLuverfn7ZB9EhLyG7+T1cSi1v6kt1e -6K3z8Buxe037z/3R5fjj3Of1c3/fAUnPjFbBvTfjW761T4uL8NpPx+PdVUdp3/Jb -ewdPPeWsIcHIHXro5/YPoar1b96oZU8QiZwD84l6pV4BcjPtqelaHnnzh8jfyMX8 -N8iamte4dsywPuf95lTq319SQXhZV63xEtZ/vNWfcNMFbPqjfWdY3SZiHTGSDHl5 -HI7PynvBZq+odEj7joLCniyZXHstXZu8W1eefDp6E63yoxhbK1kPzVw662gzxigd -gtFQiwIDAQABo4HZMIHWMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUa2k9ahhC -St2PAmU5/TUkhniRFjAwHwYDVR0jBBgwFoAUwHqYaI2J+6sFZAwRfap9ZbjKzE4w -EgYDVR0TAQH/BAgwBgEB/wIBADA6BgNVHR8EMzAxMC+gLaArhilodHRwOi8vY3Js -Lmdlb3RydXN0LmNvbS9jcmxzL2d0Z2xvYmFsLmNybDA0BggrBgEFBQcBAQQoMCYw -JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmdlb3RydXN0LmNvbTANBgkqhkiG9w0B -AQUFAAOCAQEAq7y8Cl0YlOPBscOoTFXWvrSY8e48HM3P8yQkXJYDJ1j8Nq6iL4/x -/torAsMzvcjdSCIrYA+lAxD9d/jQ7ZZnT/3qRyBwVNypDFV+4ZYlitm12ldKvo2O -SUNjpWxOJ4cl61tt/qJ/OCjgNqutOaWlYsS3XFgsql0BYKZiZ6PAx2Ij9OdsRu61 -04BqIhPSLT90T+qvjF+0OJzbrs6vhB6m9jRRWXnT43XcvNfzc9+S7NIgWW+c+5X4 -knYYCnwPLKbK3opie9jzzl9ovY8+wXS7FXI6FoOpC+ZNmZzYV+yoAVHHb1c0XqtK -LEL2TxyJeN4mTvVvk0wVaydWTQBUbHq3tw== ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 144470 (0x23456) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA - Validity - Not Before: May 21 04:00:00 2002 GMT - Not After : May 21 04:00:00 2022 GMT - Subject: C=US, O=GeoTrust Inc., CN=GeoTrust Global CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:da:cc:18:63:30:fd:f4:17:23:1a:56:7e:5b:df: - 3c:6c:38:e4:71:b7:78:91:d4:bc:a1:d8:4c:f8:a8: - 43:b6:03:e9:4d:21:07:08:88:da:58:2f:66:39:29: - bd:05:78:8b:9d:38:e8:05:b7:6a:7e:71:a4:e6:c4: - 60:a6:b0:ef:80:e4:89:28:0f:9e:25:d6:ed:83:f3: - ad:a6:91:c7:98:c9:42:18:35:14:9d:ad:98:46:92: - 2e:4f:ca:f1:87:43:c1:16:95:57:2d:50:ef:89:2d: - 80:7a:57:ad:f2:ee:5f:6b:d2:00:8d:b9:14:f8:14: - 15:35:d9:c0:46:a3:7b:72:c8:91:bf:c9:55:2b:cd: - d0:97:3e:9c:26:64:cc:df:ce:83:19:71:ca:4e:e6: - d4:d5:7b:a9:19:cd:55:de:c8:ec:d2:5e:38:53:e5: - 5c:4f:8c:2d:fe:50:23:36:fc:66:e6:cb:8e:a4:39: - 19:00:b7:95:02:39:91:0b:0e:fe:38:2e:d1:1d:05: - 9a:f6:4d:3e:6f:0f:07:1d:af:2c:1e:8f:60:39:e2: - fa:36:53:13:39:d4:5e:26:2b:db:3d:a8:14:bd:32: - eb:18:03:28:52:04:71:e5:ab:33:3d:e1:38:bb:07: - 36:84:62:9c:79:ea:16:30:f4:5f:c0:2b:e8:71:6b: - e4:f9 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE - X509v3 Subject Key Identifier: - C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E - X509v3 Authority Key Identifier: - keyid:C0:7A:98:68:8D:89:FB:AB:05:64:0C:11:7D:AA:7D:65:B8:CA:CC:4E - - Signature Algorithm: sha1WithRSAEncryption - 35:e3:29:6a:e5:2f:5d:54:8e:29:50:94:9f:99:1a:14:e4:8f: - 78:2a:62:94:a2:27:67:9e:d0:cf:1a:5e:47:e9:c1:b2:a4:cf: - dd:41:1a:05:4e:9b:4b:ee:4a:6f:55:52:b3:24:a1:37:0a:eb: - 64:76:2a:2e:2c:f3:fd:3b:75:90:bf:fa:71:d8:c7:3d:37:d2: - b5:05:95:62:b9:a6:de:89:3d:36:7b:38:77:48:97:ac:a6:20: - 8f:2e:a6:c9:0c:c2:b2:99:45:00:c7:ce:11:51:22:22:e0:a5: - ea:b6:15:48:09:64:ea:5e:4f:74:f7:05:3e:c7:8a:52:0c:db: - 15:b4:bd:6d:9b:e5:c6:b1:54:68:a9:e3:69:90:b6:9a:a5:0f: - b8:b9:3f:20:7d:ae:4a:b5:b8:9c:e4:1d:b6:ab:e6:94:a5:c1: - c7:83:ad:db:f5:27:87:0e:04:6c:d5:ff:dd:a0:5d:ed:87:52: - b7:2b:15:02:ae:39:a6:6a:74:e9:da:c4:e7:bc:4d:34:1e:a9: - 5c:4d:33:5f:92:09:2f:88:66:5d:77:97:c7:1d:76:13:a9:d5: - e5:f1:16:09:11:35:d5:ac:db:24:71:70:2c:98:56:0b:d9:17: - b4:d1:e3:51:2b:5e:75:e8:d5:d0:dc:4f:34:ed:c2:05:66:80: - a1:cb:e6:33 ------BEGIN CERTIFICATE----- -MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT -MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i -YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG -EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg -R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 -9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq -fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv -iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU -1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ -bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW -MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA -ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l -uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn -Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS -tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF -PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un -hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV -5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 03:37:b9:28:34:7c:60:a6:ae:c5:ad:b1:21:7f:38:60 - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA - Validity - Not Before: Nov 9 12:00:00 2007 GMT - Not After : Nov 10 00:00:00 2021 GMT - Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV CA-1 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:f3:96:62:d8:75:6e:19:ff:3f:34:7c:49:4f:31: - 7e:0d:04:4e:99:81:e2:b3:85:55:91:30:b1:c0:af: - 70:bb:2c:a8:e7:18:aa:3f:78:f7:90:68:52:86:01: - 88:97:e2:3b:06:65:90:aa:bd:65:76:c2:ec:be:10: - 5b:37:78:83:60:75:45:c6:bd:74:aa:b6:9f:a4:3a: - 01:50:17:c4:39:69:b9:f1:4f:ef:82:c1:ca:f3:4a: - db:cc:9e:50:4f:4d:40:a3:3a:90:e7:86:66:bc:f0: - 3e:76:28:4c:d1:75:80:9e:6a:35:14:35:03:9e:db: - 0c:8c:c2:28:ad:50:b2:ce:f6:91:a3:c3:a5:0a:58: - 49:f6:75:44:6c:ba:f9:ce:e9:ab:3a:02:e0:4d:f3: - ac:e2:7a:e0:60:22:05:3c:82:d3:52:e2:f3:9c:47: - f8:3b:d8:b2:4b:93:56:4a:bf:70:ab:3e:e9:68:c8: - 1d:8f:58:1d:2a:4d:5e:27:3d:ad:0a:59:2f:5a:11: - 20:40:d9:68:04:68:2d:f4:c0:84:0b:0a:1b:78:df: - ed:1a:58:dc:fb:41:5a:6d:6b:f2:ed:1c:ee:5c:32: - b6:5c:ec:d7:a6:03:32:a6:e8:de:b7:28:27:59:88: - 80:ff:7b:ad:89:58:d5:1e:14:a4:f2:b0:70:d4:a0: - 3e:a7 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Certificate Sign, CRL Sign - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication, Code Signing, E-mail Protection, Time Stamping - X509v3 Certificate Policies: - Policy: 2.16.840.1.114412.2.1 - CPS: http://www.digicert.com/ssl-cps-repository.htm - User Notice: - Explicit Text: - - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - Authority Information Access: - OCSP - URI:http://ocsp.digicert.com - CA Issuers - URI:http://www.digicert.com/CACerts/DigiCertHighAssuranceEVRootCA.crt - - X509v3 CRL Distribution Points: - URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl - URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl - - X509v3 Subject Key Identifier: - 4C:58:CB:25:F0:41:4F:52:F4:28:C8:81:43:9B:A6:A8:A0:E6:92:E5 - X509v3 Authority Key Identifier: - keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 - - Signature Algorithm: sha1WithRSAEncryption - 4c:7a:17:87:28:5d:17:bc:b2:32:73:bf:cd:2e:f5:58:31:1d: - f0:b1:71:54:9c:d6:9b:67:93:db:2f:03:3e:16:6f:1e:03:c9: - 53:84:a3:56:60:1e:78:94:1b:a2:a8:6f:a3:a4:8b:52:91:d7: - dd:5c:95:bb:ef:b5:16:49:e9:a5:42:4f:34:f2:47:ff:ae:81: - 7f:13:54:b7:20:c4:70:15:cb:81:0a:81:cb:74:57:dc:9c:df: - 24:a4:29:0c:18:f0:1c:e4:ae:07:33:ec:f1:49:3e:55:cf:6e: - 4f:0d:54:7b:d3:c9:e8:15:48:d4:c5:bb:dc:35:1c:77:45:07: - 48:45:85:bd:d7:7e:53:b8:c0:16:d9:95:cd:8b:8d:7d:c9:60: - 4f:d1:a2:9b:e3:d0:30:d6:b4:73:36:e6:d2:f9:03:b2:e3:a4: - f5:e5:b8:3e:04:49:00:ba:2e:a6:4a:72:83:72:9d:f7:0b:8c: - a9:89:e7:b3:d7:64:1f:d6:e3:60:cb:03:c4:dc:88:e9:9d:25: - 01:00:71:cb:03:b4:29:60:25:8f:f9:46:d1:7b:71:ae:cd:53: - 12:5b:84:8e:c2:0f:c7:ed:93:19:d9:c9:fa:8f:58:34:76:32: - 2f:ae:e1:50:14:61:d4:a8:58:a3:c8:30:13:23:ef:c6:25:8c: - 36:8f:1c:80 ------BEGIN CERTIFICATE----- -MIIG5jCCBc6gAwIBAgIQAze5KDR8YKauxa2xIX84YDANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA3MTEwOTEyMDAwMFoXDTIxMTExMDAwMDAwMFowaTEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTEoMCYGA1UEAxMfRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -RVYgQ0EtMTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPOWYth1bhn/ -PzR8SU8xfg0ETpmB4rOFVZEwscCvcLssqOcYqj9495BoUoYBiJfiOwZlkKq9ZXbC -7L4QWzd4g2B1Rca9dKq2n6Q6AVAXxDlpufFP74LByvNK28yeUE9NQKM6kOeGZrzw -PnYoTNF1gJ5qNRQ1A57bDIzCKK1Qss72kaPDpQpYSfZ1RGy6+c7pqzoC4E3zrOJ6 -4GAiBTyC01Li85xH+DvYskuTVkq/cKs+6WjIHY9YHSpNXic9rQpZL1oRIEDZaARo -LfTAhAsKG3jf7RpY3PtBWm1r8u0c7lwytlzs16YDMqbo3rcoJ1mIgP97rYlY1R4U -pPKwcNSgPqcCAwEAAaOCA4UwggOBMA4GA1UdDwEB/wQEAwIBhjA7BgNVHSUENDAy -BggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMDBggrBgEFBQcDBAYIKwYBBQUH -AwgwggHEBgNVHSAEggG7MIIBtzCCAbMGCWCGSAGG/WwCATCCAaQwOgYIKwYBBQUH -AgEWLmh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL3NzbC1jcHMtcmVwb3NpdG9yeS5o -dG0wggFkBggrBgEFBQcCAjCCAVYeggFSAEEAbgB5ACAAdQBzAGUAIABvAGYAIAB0 -AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEAdABlACAAYwBvAG4AcwB0AGkAdAB1 -AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMAZQAgAG8AZgAgAHQAaABlACAARABp -AGcAaQBDAGUAcgB0ACAARQBWACAAQwBQAFMAIABhAG4AZAAgAHQAaABlACAAUgBl -AGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEAZwByAGUAZQBtAGUAbgB0ACAAdwBo -AGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEAYgBpAGwAaQB0AHkAIABhAG4AZAAg -AGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEAdABlAGQAIABoAGUAcgBlAGkAbgAg -AGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4wEgYDVR0TAQH/BAgwBgEB/wIBADCB -gwYIKwYBBQUHAQEEdzB1MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2Vy -dC5jb20wTQYIKwYBBQUHMAKGQWh0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NBQ2Vy -dHMvRGlnaUNlcnRIaWdoQXNzdXJhbmNlRVZSb290Q0EuY3J0MIGPBgNVHR8EgYcw -gYQwQKA+oDyGOmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hB -c3N1cmFuY2VFVlJvb3RDQS5jcmwwQKA+oDyGOmh0dHA6Ly9jcmw0LmRpZ2ljZXJ0 -LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RDQS5jcmwwHQYDVR0OBBYE -FExYyyXwQU9S9CjIgUObpqig5pLlMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSYJhoI -Au9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQBMeheHKF0XvLIyc7/NLvVYMR3wsXFU -nNabZ5PbLwM+Fm8eA8lThKNWYB54lBuiqG+jpItSkdfdXJW777UWSemlQk808kf/ -roF/E1S3IMRwFcuBCoHLdFfcnN8kpCkMGPAc5K4HM+zxST5Vz25PDVR708noFUjU -xbvcNRx3RQdIRYW9135TuMAW2ZXNi419yWBP0aKb49Aw1rRzNubS+QOy46T15bg+ -BEkAui6mSnKDcp33C4ypieez12Qf1uNgywPE3IjpnSUBAHHLA7QpYCWP+UbRe3Gu -zVMSW4SOwg/H7ZMZ2cn6j1g0djIvruFQFGHUqFijyDATI+/GJYw2jxyA ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 1116160165 (0x428740a5) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority - Validity - Not Before: Oct 1 05:00:00 2006 GMT - Not After : Jul 26 18:15:15 2014 GMT - Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:c6:cc:e5:73:e6:fb:d4:bb:e5:2d:2d:32:a6:df: - e5:81:3f:c9:cd:25:49:b6:71:2a:c3:d5:94:34:67: - a2:0a:1c:b0:5f:69:a6:40:b1:c4:b7:b2:8f:d0:98: - a4:a9:41:59:3a:d3:dc:94:d6:3c:db:74:38:a4:4a: - cc:4d:25:82:f7:4a:a5:53:12:38:ee:f3:49:6d:71: - 91:7e:63:b6:ab:a6:5f:c3:a4:84:f8:4f:62:51:be: - f8:c5:ec:db:38:92:e3:06:e5:08:91:0c:c4:28:41: - 55:fb:cb:5a:89:15:7e:71:e8:35:bf:4d:72:09:3d: - be:3a:38:50:5b:77:31:1b:8d:b3:c7:24:45:9a:a7: - ac:6d:00:14:5a:04:b7:ba:13:eb:51:0a:98:41:41: - 22:4e:65:61:87:81:41:50:a6:79:5c:89:de:19:4a: - 57:d5:2e:e6:5d:1c:53:2c:7e:98:cd:1a:06:16:a4: - 68:73:d0:34:04:13:5c:a1:71:d3:5a:7c:55:db:5e: - 64:e1:37:87:30:56:04:e5:11:b4:29:80:12:f1:79: - 39:88:a2:02:11:7c:27:66:b7:88:b7:78:f2:ca:0a: - a8:38:ab:0a:64:c2:bf:66:5d:95:84:c1:a1:25:1e: - 87:5d:1a:50:0b:20:12:cc:41:bb:6e:0b:51:38:b8: - 4b:cb - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:1 - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication, E-mail Protection - Authority Information Access: - OCSP - URI:http://ocsp.entrust.net - - X509v3 CRL Distribution Points: - URI:http://crl.entrust.net/server1.crl - - X509v3 Subject Key Identifier: - B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 - X509v3 Key Usage: - Certificate Sign, CRL Sign - X509v3 Authority Key Identifier: - keyid:F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A - - 1.2.840.113533.7.65.0: - 0 -..V7.1.... - Signature Algorithm: sha1WithRSAEncryption - 48:0e:2b:6f:20:62:4c:28:93:a3:24:3d:58:ab:21:cf:80:f8: - 9a:97:90:6a:22:ed:5a:7c:47:36:99:e7:79:84:75:ab:24:8f: - 92:0a:d5:61:04:ae:c3:6a:5c:b2:cc:d9:e4:44:87:6f:db:8f: - 38:62:f7:44:36:9d:ba:bc:6e:07:c4:d4:8d:e8:1f:d1:0b:60: - a3:b5:9c:ce:63:be:ed:67:dc:f8:ba:de:6e:c9:25:cb:5b:b5: - 9d:76:70:0b:df:42:72:f8:4f:41:11:64:a5:d2:ea:fc:d5:af: - 11:f4:15:38:67:9c:20:a8:4b:77:5a:91:32:42:32:e7:85:b3: - df:36 ------BEGIN CERTIFICATE----- -MIIEQjCCA6ugAwIBAgIEQodApTANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC -VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u -ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc -KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u -ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEw -MDEwNTAwMDBaFw0xNDA3MjYxODE1MTVaMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQK -EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNV -BAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGzOVz5vvUu+UtLTKm3+WBP8nNJUm2cSrD -1ZQ0Z6IKHLBfaaZAscS3so/QmKSpQVk609yU1jzbdDikSsxNJYL3SqVTEjju80lt -cZF+Y7arpl/DpIT4T2JRvvjF7Ns4kuMG5QiRDMQoQVX7y1qJFX5x6DW/TXIJPb46 -OFBbdzEbjbPHJEWap6xtABRaBLe6E+tRCphBQSJOZWGHgUFQpnlcid4ZSlfVLuZd -HFMsfpjNGgYWpGhz0DQEE1yhcdNafFXbXmThN4cwVgTlEbQpgBLxeTmIogIRfCdm -t4i3ePLKCqg4qwpkwr9mXZWEwaElHoddGlALIBLMQbtuC1E4uEvLAgMBAAGjggET -MIIBDzASBgNVHRMBAf8ECDAGAQH/AgEBMCcGA1UdJQQgMB4GCCsGAQUFBwMBBggr -BgEFBQcDAgYIKwYBBQUHAwQwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdo -dHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8v -Y3JsLmVudHJ1c3QubmV0L3NlcnZlcjEuY3JsMB0GA1UdDgQWBBSxPsNpA/i/RwHU -mCYaCALvY2QrwzALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7 -UISX8+1i0BowGQYJKoZIhvZ9B0EABAwwChsEVjcuMQMCAIEwDQYJKoZIhvcNAQEF -BQADgYEASA4rbyBiTCiToyQ9WKshz4D4mpeQaiLtWnxHNpnneYR1qySPkgrVYQSu -w2pcsszZ5ESHb9uPOGL3RDadurxuB8TUjegf0Qtgo7WczmO+7Wfc+Lrebskly1u1 -nXZwC99CcvhPQRFkpdLq/NWvEfQVOGecIKhLd1qRMkIy54Wz3zY= ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 927650371 (0x374ad243) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority - Validity - Not Before: May 25 16:09:40 1999 GMT - Not After : May 25 16:39:40 2019 GMT - Subject: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (1024 bit) - Modulus (1024 bit): - 00:cd:28:83:34:54:1b:89:f3:0f:af:37:91:31:ff: - af:31:60:c9:a8:e8:b2:10:68:ed:9f:e7:93:36:f1: - 0a:64:bb:47:f5:04:17:3f:23:47:4d:c5:27:19:81: - 26:0c:54:72:0d:88:2d:d9:1f:9a:12:9f:bc:b3:71: - d3:80:19:3f:47:66:7b:8c:35:28:d2:b9:0a:df:24: - da:9c:d6:50:79:81:7a:5a:d3:37:f7:c2:4a:d8:29: - 92:26:64:d1:e4:98:6c:3a:00:8a:f5:34:9b:65:f8: - ed:e3:10:ff:fd:b8:49:58:dc:a0:de:82:39:6b:81: - b1:16:19:61:b9:54:b6:e6:43 - Exponent: 3 (0x3) - X509v3 extensions: - Netscape Cert Type: - SSL CA, S/MIME CA, Object Signing CA - X509v3 CRL Distribution Points: - DirName:/C=US/O=Entrust.net/OU=www.entrust.net/CPS incorp. by ref. (limits liab.)/OU=(c) 1999 Entrust.net Limited/CN=Entrust.net Secure Server Certification Authority/CN=CRL1 - URI:http://www.entrust.net/CRL/net1.crl - - X509v3 Private Key Usage Period: - Not Before: May 25 16:09:40 1999 GMT, Not After: May 25 16:09:40 2019 GMT - X509v3 Key Usage: - Certificate Sign, CRL Sign - X509v3 Authority Key Identifier: - keyid:F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A - - X509v3 Subject Key Identifier: - F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A - X509v3 Basic Constraints: - CA:TRUE - 1.2.840.113533.7.65.0: - 0 -..V4.0.... - Signature Algorithm: sha1WithRSAEncryption - 90:dc:30:02:fa:64:74:c2:a7:0a:a5:7c:21:8d:34:17:a8:fb: - 47:0e:ff:25:7c:8d:13:0a:fb:e4:98:b5:ef:8c:f8:c5:10:0d: - f7:92:be:f1:c3:d5:d5:95:6a:04:bb:2c:ce:26:36:65:c8:31: - c6:e7:ee:3f:e3:57:75:84:7a:11:ef:46:4f:18:f4:d3:98:bb: - a8:87:32:ba:72:f6:3c:e2:3d:9f:d7:1d:d9:c3:60:43:8c:58: - 0e:22:96:2f:62:a3:2c:1f:ba:ad:05:ef:ab:32:78:87:a0:54: - 73:19:b5:5c:05:f9:52:3e:6d:2d:45:0b:f7:0a:93:ea:ed:06: - f9:b2 ------BEGIN CERTIFICATE----- -MIIE2DCCBEGgAwIBAgIEN0rSQzANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC -VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u -ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc -KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u -ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw05OTA1 -MjUxNjA5NDBaFw0xOTA1MjUxNjM5NDBaMIHDMQswCQYDVQQGEwJVUzEUMBIGA1UE -ChMLRW50cnVzdC5uZXQxOzA5BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5j -b3JwLiBieSByZWYuIChsaW1pdHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBF -bnRydXN0Lm5ldCBMaW1pdGVkMTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUg -U2VydmVyIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGdMA0GCSqGSIb3DQEBAQUA -A4GLADCBhwKBgQDNKIM0VBuJ8w+vN5Ex/68xYMmo6LIQaO2f55M28Qpku0f1BBc/ -I0dNxScZgSYMVHINiC3ZH5oSn7yzcdOAGT9HZnuMNSjSuQrfJNqc1lB5gXpa0zf3 -wkrYKZImZNHkmGw6AIr1NJtl+O3jEP/9uElY3KDegjlrgbEWGWG5VLbmQwIBA6OC -AdcwggHTMBEGCWCGSAGG+EIBAQQEAwIABzCCARkGA1UdHwSCARAwggEMMIHeoIHb -oIHYpIHVMIHSMQswCQYDVQQGEwJVUzEUMBIGA1UEChMLRW50cnVzdC5uZXQxOzA5 -BgNVBAsTMnd3dy5lbnRydXN0Lm5ldC9DUFMgaW5jb3JwLiBieSByZWYuIChsaW1p -dHMgbGlhYi4pMSUwIwYDVQQLExwoYykgMTk5OSBFbnRydXN0Lm5ldCBMaW1pdGVk -MTowOAYDVQQDEzFFbnRydXN0Lm5ldCBTZWN1cmUgU2VydmVyIENlcnRpZmljYXRp -b24gQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMCmgJ6AlhiNodHRwOi8vd3d3LmVu -dHJ1c3QubmV0L0NSTC9uZXQxLmNybDArBgNVHRAEJDAigA8xOTk5MDUyNTE2MDk0 -MFqBDzIwMTkwNTI1MTYwOTQwWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8Bdi -E1U9s/8KAGv7UISX8+1i0BowHQYDVR0OBBYEFPAXYhNVPbP/CgBr+1CEl/PtYtAa -MAwGA1UdEwQFMAMBAf8wGQYJKoZIhvZ9B0EABAwwChsEVjQuMAMCBJAwDQYJKoZI -hvcNAQEFBQADgYEAkNwwAvpkdMKnCqV8IY00F6j7Rw7/JXyNEwr75Ji174z4xRAN -95K+8cPV1ZVqBLssziY2ZcgxxufuP+NXdYR6Ee9GTxj005i7qIcyunL2POI9n9cd -2cNgQ4xYDiKWL2KjLB+6rQXvqzJ4h6BUcxm1XAX5Uj5tLUUL9wqT6u0G+bI= ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 0a:5f:11:4d:03:5b:17:91:17:d2:ef:d4:03:8c:3f:3b - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA - Validity - Not Before: Apr 2 12:00:00 2008 GMT - Not After : Apr 3 00:00:00 2022 GMT - Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance CA-3 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) - Modulus: - 00:bf:61:0a:29:10:1f:5e:fe:34:37:51:08:f8:1e: - fb:22:ed:61:be:0b:0d:70:4c:50:63:26:75:15:b9: - 41:88:97:b6:f0:a0:15:bb:08:60:e0:42:e8:05:29: - 10:87:36:8a:28:65:a8:ef:31:07:74:6d:36:97:2f: - 28:46:66:04:c7:2a:79:26:7a:99:d5:8e:c3:6d:4f: - a0:5e:ad:bc:3d:91:c2:59:7b:5e:36:6c:c0:53:cf: - 00:08:32:3e:10:64:58:10:13:69:c7:0c:ee:9c:42: - 51:00:f9:05:44:ee:24:ce:7a:1f:ed:8c:11:bd:12: - a8:f3:15:f4:1c:7a:31:69:01:1b:a7:e6:5d:c0:9a: - 6c:7e:09:9e:e7:52:44:4a:10:3a:23:e4:9b:b6:03: - af:a8:9c:b4:5b:9f:d4:4b:ad:92:8c:ce:b5:11:2a: - aa:37:18:8d:b4:c2:b8:d8:5c:06:8c:f8:ff:23:bd: - 35:5e:d4:7c:3e:7e:83:0e:91:96:05:98:c3:b2:1f: - e3:c8:65:eb:a9:7b:5d:a0:2c:cc:fc:3c:d9:6d:ed: - cc:fa:4b:43:8c:c9:d4:b8:a5:61:1c:b2:40:b6:28: - 12:df:b9:f8:5f:fe:d3:b2:c9:ef:3d:b4:1e:4b:7c: - 1c:4c:99:36:9e:3d:eb:ec:a7:68:5e:1d:df:67:6e: - 5e:fb - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Certificate Sign, CRL Sign - X509v3 Certificate Policies: - Policy: 2.16.840.1.114412.1.3.0.2 - CPS: http://www.digicert.com/ssl-cps-repository.htm - User Notice: - Explicit Text: - - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - Authority Information Access: - OCSP - URI:http://ocsp.digicert.com - - X509v3 CRL Distribution Points: - - Full Name: - URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl - - Full Name: - URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl - - X509v3 Authority Key Identifier: - keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 - - X509v3 Subject Key Identifier: - 50:EA:73:89:DB:29:FB:10:8F:9E:E5:01:20:D4:DE:79:99:48:83:F7 - Signature Algorithm: sha1WithRSAEncryption - 1e:e2:a5:48:9e:6c:db:53:38:0f:ef:a6:1a:2a:ac:e2:03:43: - ed:9a:bc:3e:8e:75:1b:f0:fd:2e:22:59:ac:13:c0:61:e2:e7: - fa:e9:99:cd:87:09:75:54:28:bf:46:60:dc:be:51:2c:92:f3: - 1b:91:7c:31:08:70:e2:37:b9:c1:5b:a8:bd:a3:0b:00:fb:1a: - 15:fd:03:ad:58:6a:c5:c7:24:99:48:47:46:31:1e:92:ef:b4: - 5f:4e:34:c7:90:bf:31:c1:f8:b1:84:86:d0:9c:01:aa:df:8a: - 56:06:ce:3a:e9:0e:ae:97:74:5d:d7:71:9a:42:74:5f:de:8d: - 43:7c:de:e9:55:ed:69:00:cb:05:e0:7a:61:61:33:d1:19:4d: - f9:08:ee:a0:39:c5:25:35:b7:2b:c4:0f:b2:dd:f1:a5:b7:0e: - 24:c4:26:28:8d:79:77:f5:2f:f0:57:ba:7c:07:d4:e1:fc:cd: - 5a:30:57:7e:86:10:47:dd:31:1f:d7:fc:a2:c2:bf:30:7c:5d: - 24:aa:e8:f9:ae:5f:6a:74:c2:ce:6b:b3:46:d8:21:be:29:d4: - 8e:5e:15:d6:42:4a:e7:32:6f:a4:b1:6b:51:83:58:be:3f:6d: - c7:fb:da:03:21:cb:6a:16:19:4e:0a:f0:ad:84:ca:5d:94:b3: - 5a:76:f7:61 ------BEGIN CERTIFICATE----- -MIIGWDCCBUCgAwIBAgIQCl8RTQNbF5EX0u/UA4w/OzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA4MDQwMjEyMDAwMFoXDTIyMDQwMzAwMDAwMFowZjEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTElMCMGA1UEAxMcRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -Q0EtMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9hCikQH17+NDdR -CPge+yLtYb4LDXBMUGMmdRW5QYiXtvCgFbsIYOBC6AUpEIc2iihlqO8xB3RtNpcv -KEZmBMcqeSZ6mdWOw21PoF6tvD2Rwll7XjZswFPPAAgyPhBkWBATaccM7pxCUQD5 -BUTuJM56H+2MEb0SqPMV9Bx6MWkBG6fmXcCabH4JnudSREoQOiPkm7YDr6ictFuf -1EutkozOtREqqjcYjbTCuNhcBoz4/yO9NV7UfD5+gw6RlgWYw7If48hl66l7XaAs -zPw82W3tzPpLQ4zJ1LilYRyyQLYoEt+5+F/+07LJ7z20Hkt8HEyZNp496+ynaF4d -32duXvsCAwEAAaOCAvowggL2MA4GA1UdDwEB/wQEAwIBhjCCAcYGA1UdIASCAb0w -ggG5MIIBtQYLYIZIAYb9bAEDAAIwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3 -LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH -AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy -AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj -AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg -AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ -AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt -AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj -AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl -AHIAZQBuAGMAZQAuMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYIKwYBBQUHAQEEKDAm -MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgY8GA1UdHwSB -hzCBhDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGln -aEFzc3VyYW5jZUVWUm9vdENBLmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNl -cnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDAfBgNVHSME -GDAWgBSxPsNpA/i/RwHUmCYaCALvY2QrwzAdBgNVHQ4EFgQUUOpzidsp+xCPnuUB -INTeeZlIg/cwDQYJKoZIhvcNAQEFBQADggEBAB7ipUiebNtTOA/vphoqrOIDQ+2a -vD6OdRvw/S4iWawTwGHi5/rpmc2HCXVUKL9GYNy+USyS8xuRfDEIcOI3ucFbqL2j -CwD7GhX9A61YasXHJJlIR0YxHpLvtF9ONMeQvzHB+LGEhtCcAarfilYGzjrpDq6X -dF3XcZpCdF/ejUN83ulV7WkAywXgemFhM9EZTfkI7qA5xSU1tyvED7Ld8aW3DiTE -JiiNeXf1L/BXunwH1OH8zVowV36GEEfdMR/X/KLCvzB8XSSq6PmuX2p0ws5rs0bY -Ib4p1I5eFdZCSucyb6Sxa1GDWL4/bcf72gMhy2oWGU4K8K2Eyl2Us1p292E= ------END CERTIFICATE----- \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/d867a7b2aecc46f9c31afc4f2f50de05 b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/d867a7b2aecc46f9c31afc4f2f50de05 deleted file mode 100644 index 4ebe436..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/certs/d867a7b2aecc46f9c31afc4f2f50de05 +++ /dev/null @@ -1,197 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: - 0a:5f:11:4d:03:5b:17:91:17:d2:ef:d4:03:8c:3f:3b - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA - Validity - Not Before: Apr 2 12:00:00 2008 GMT - Not After : Apr 3 00:00:00 2022 GMT - Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance CA-3 - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:bf:61:0a:29:10:1f:5e:fe:34:37:51:08:f8:1e: - fb:22:ed:61:be:0b:0d:70:4c:50:63:26:75:15:b9: - 41:88:97:b6:f0:a0:15:bb:08:60:e0:42:e8:05:29: - 10:87:36:8a:28:65:a8:ef:31:07:74:6d:36:97:2f: - 28:46:66:04:c7:2a:79:26:7a:99:d5:8e:c3:6d:4f: - a0:5e:ad:bc:3d:91:c2:59:7b:5e:36:6c:c0:53:cf: - 00:08:32:3e:10:64:58:10:13:69:c7:0c:ee:9c:42: - 51:00:f9:05:44:ee:24:ce:7a:1f:ed:8c:11:bd:12: - a8:f3:15:f4:1c:7a:31:69:01:1b:a7:e6:5d:c0:9a: - 6c:7e:09:9e:e7:52:44:4a:10:3a:23:e4:9b:b6:03: - af:a8:9c:b4:5b:9f:d4:4b:ad:92:8c:ce:b5:11:2a: - aa:37:18:8d:b4:c2:b8:d8:5c:06:8c:f8:ff:23:bd: - 35:5e:d4:7c:3e:7e:83:0e:91:96:05:98:c3:b2:1f: - e3:c8:65:eb:a9:7b:5d:a0:2c:cc:fc:3c:d9:6d:ed: - cc:fa:4b:43:8c:c9:d4:b8:a5:61:1c:b2:40:b6:28: - 12:df:b9:f8:5f:fe:d3:b2:c9:ef:3d:b4:1e:4b:7c: - 1c:4c:99:36:9e:3d:eb:ec:a7:68:5e:1d:df:67:6e: - 5e:fb - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Key Usage: critical - Digital Signature, Certificate Sign, CRL Sign - X509v3 Certificate Policies: - Policy: 2.16.840.1.114412.1.3.0.2 - CPS: http://www.digicert.com/ssl-cps-repository.htm - User Notice: - Explicit Text: - - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:0 - Authority Information Access: - OCSP - URI:http://ocsp.digicert.com - - X509v3 CRL Distribution Points: - URI:http://crl3.digicert.com/DigiCertHighAssuranceEVRootCA.crl - URI:http://crl4.digicert.com/DigiCertHighAssuranceEVRootCA.crl - - X509v3 Authority Key Identifier: - keyid:B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 - - X509v3 Subject Key Identifier: - 50:EA:73:89:DB:29:FB:10:8F:9E:E5:01:20:D4:DE:79:99:48:83:F7 - Signature Algorithm: sha1WithRSAEncryption - 1e:e2:a5:48:9e:6c:db:53:38:0f:ef:a6:1a:2a:ac:e2:03:43: - ed:9a:bc:3e:8e:75:1b:f0:fd:2e:22:59:ac:13:c0:61:e2:e7: - fa:e9:99:cd:87:09:75:54:28:bf:46:60:dc:be:51:2c:92:f3: - 1b:91:7c:31:08:70:e2:37:b9:c1:5b:a8:bd:a3:0b:00:fb:1a: - 15:fd:03:ad:58:6a:c5:c7:24:99:48:47:46:31:1e:92:ef:b4: - 5f:4e:34:c7:90:bf:31:c1:f8:b1:84:86:d0:9c:01:aa:df:8a: - 56:06:ce:3a:e9:0e:ae:97:74:5d:d7:71:9a:42:74:5f:de:8d: - 43:7c:de:e9:55:ed:69:00:cb:05:e0:7a:61:61:33:d1:19:4d: - f9:08:ee:a0:39:c5:25:35:b7:2b:c4:0f:b2:dd:f1:a5:b7:0e: - 24:c4:26:28:8d:79:77:f5:2f:f0:57:ba:7c:07:d4:e1:fc:cd: - 5a:30:57:7e:86:10:47:dd:31:1f:d7:fc:a2:c2:bf:30:7c:5d: - 24:aa:e8:f9:ae:5f:6a:74:c2:ce:6b:b3:46:d8:21:be:29:d4: - 8e:5e:15:d6:42:4a:e7:32:6f:a4:b1:6b:51:83:58:be:3f:6d: - c7:fb:da:03:21:cb:6a:16:19:4e:0a:f0:ad:84:ca:5d:94:b3: - 5a:76:f7:61 ------BEGIN CERTIFICATE----- -MIIGWDCCBUCgAwIBAgIQCl8RTQNbF5EX0u/UA4w/OzANBgkqhkiG9w0BAQUFADBs -MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3 -d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j -ZSBFViBSb290IENBMB4XDTA4MDQwMjEyMDAwMFoXDTIyMDQwMzAwMDAwMFowZjEL -MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3 -LmRpZ2ljZXJ0LmNvbTElMCMGA1UEAxMcRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug -Q0EtMzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL9hCikQH17+NDdR -CPge+yLtYb4LDXBMUGMmdRW5QYiXtvCgFbsIYOBC6AUpEIc2iihlqO8xB3RtNpcv -KEZmBMcqeSZ6mdWOw21PoF6tvD2Rwll7XjZswFPPAAgyPhBkWBATaccM7pxCUQD5 -BUTuJM56H+2MEb0SqPMV9Bx6MWkBG6fmXcCabH4JnudSREoQOiPkm7YDr6ictFuf -1EutkozOtREqqjcYjbTCuNhcBoz4/yO9NV7UfD5+gw6RlgWYw7If48hl66l7XaAs -zPw82W3tzPpLQ4zJ1LilYRyyQLYoEt+5+F/+07LJ7z20Hkt8HEyZNp496+ynaF4d -32duXvsCAwEAAaOCAvowggL2MA4GA1UdDwEB/wQEAwIBhjCCAcYGA1UdIASCAb0w -ggG5MIIBtQYLYIZIAYb9bAEDAAIwggGkMDoGCCsGAQUFBwIBFi5odHRwOi8vd3d3 -LmRpZ2ljZXJ0LmNvbS9zc2wtY3BzLXJlcG9zaXRvcnkuaHRtMIIBZAYIKwYBBQUH -AgIwggFWHoIBUgBBAG4AeQAgAHUAcwBlACAAbwBmACAAdABoAGkAcwAgAEMAZQBy -AHQAaQBmAGkAYwBhAHQAZQAgAGMAbwBuAHMAdABpAHQAdQB0AGUAcwAgAGEAYwBj -AGUAcAB0AGEAbgBjAGUAIABvAGYAIAB0AGgAZQAgAEQAaQBnAGkAQwBlAHIAdAAg -AEMAUAAvAEMAUABTACAAYQBuAGQAIAB0AGgAZQAgAFIAZQBsAHkAaQBuAGcAIABQ -AGEAcgB0AHkAIABBAGcAcgBlAGUAbQBlAG4AdAAgAHcAaABpAGMAaAAgAGwAaQBt -AGkAdAAgAGwAaQBhAGIAaQBsAGkAdAB5ACAAYQBuAGQAIABhAHIAZQAgAGkAbgBj -AG8AcgBwAG8AcgBhAHQAZQBkACAAaABlAHIAZQBpAG4AIABiAHkAIAByAGUAZgBl -AHIAZQBuAGMAZQAuMBIGA1UdEwEB/wQIMAYBAf8CAQAwNAYIKwYBBQUHAQEEKDAm -MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wgY8GA1UdHwSB -hzCBhDBAoD6gPIY6aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0SGln -aEFzc3VyYW5jZUVWUm9vdENBLmNybDBAoD6gPIY6aHR0cDovL2NybDQuZGlnaWNl -cnQuY29tL0RpZ2lDZXJ0SGlnaEFzc3VyYW5jZUVWUm9vdENBLmNybDAfBgNVHSME -GDAWgBSxPsNpA/i/RwHUmCYaCALvY2QrwzAdBgNVHQ4EFgQUUOpzidsp+xCPnuUB -INTeeZlIg/cwDQYJKoZIhvcNAQEFBQADggEBAB7ipUiebNtTOA/vphoqrOIDQ+2a -vD6OdRvw/S4iWawTwGHi5/rpmc2HCXVUKL9GYNy+USyS8xuRfDEIcOI3ucFbqL2j -CwD7GhX9A61YasXHJJlIR0YxHpLvtF9ONMeQvzHB+LGEhtCcAarfilYGzjrpDq6X -dF3XcZpCdF/ejUN83ulV7WkAywXgemFhM9EZTfkI7qA5xSU1tyvED7Ld8aW3DiTE -JiiNeXf1L/BXunwH1OH8zVowV36GEEfdMR/X/KLCvzB8XSSq6PmuX2p0ws5rs0bY -Ib4p1I5eFdZCSucyb6Sxa1GDWL4/bcf72gMhy2oWGU4K8K2Eyl2Us1p292E= ------END CERTIFICATE----- -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 1116160165 (0x428740a5) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, O=Entrust.net, OU=www.entrust.net/CPS incorp. by ref. (limits liab.), OU=(c) 1999 Entrust.net Limited, CN=Entrust.net Secure Server Certification Authority - Validity - Not Before: Oct 1 05:00:00 2006 GMT - Not After : Jul 26 18:15:15 2014 GMT - Subject: C=US, O=DigiCert Inc, OU=www.digicert.com, CN=DigiCert High Assurance EV Root CA - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - RSA Public Key: (2048 bit) - Modulus (2048 bit): - 00:c6:cc:e5:73:e6:fb:d4:bb:e5:2d:2d:32:a6:df: - e5:81:3f:c9:cd:25:49:b6:71:2a:c3:d5:94:34:67: - a2:0a:1c:b0:5f:69:a6:40:b1:c4:b7:b2:8f:d0:98: - a4:a9:41:59:3a:d3:dc:94:d6:3c:db:74:38:a4:4a: - cc:4d:25:82:f7:4a:a5:53:12:38:ee:f3:49:6d:71: - 91:7e:63:b6:ab:a6:5f:c3:a4:84:f8:4f:62:51:be: - f8:c5:ec:db:38:92:e3:06:e5:08:91:0c:c4:28:41: - 55:fb:cb:5a:89:15:7e:71:e8:35:bf:4d:72:09:3d: - be:3a:38:50:5b:77:31:1b:8d:b3:c7:24:45:9a:a7: - ac:6d:00:14:5a:04:b7:ba:13:eb:51:0a:98:41:41: - 22:4e:65:61:87:81:41:50:a6:79:5c:89:de:19:4a: - 57:d5:2e:e6:5d:1c:53:2c:7e:98:cd:1a:06:16:a4: - 68:73:d0:34:04:13:5c:a1:71:d3:5a:7c:55:db:5e: - 64:e1:37:87:30:56:04:e5:11:b4:29:80:12:f1:79: - 39:88:a2:02:11:7c:27:66:b7:88:b7:78:f2:ca:0a: - a8:38:ab:0a:64:c2:bf:66:5d:95:84:c1:a1:25:1e: - 87:5d:1a:50:0b:20:12:cc:41:bb:6e:0b:51:38:b8: - 4b:cb - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:TRUE, pathlen:1 - X509v3 Extended Key Usage: - TLS Web Server Authentication, TLS Web Client Authentication, E-mail Protection - Authority Information Access: - OCSP - URI:http://ocsp.entrust.net - - X509v3 CRL Distribution Points: - URI:http://crl.entrust.net/server1.crl - - X509v3 Subject Key Identifier: - B1:3E:C3:69:03:F8:BF:47:01:D4:98:26:1A:08:02:EF:63:64:2B:C3 - X509v3 Key Usage: - Certificate Sign, CRL Sign - X509v3 Authority Key Identifier: - keyid:F0:17:62:13:55:3D:B3:FF:0A:00:6B:FB:50:84:97:F3:ED:62:D0:1A - - 1.2.840.113533.7.65.0: - 0 -..V7.1.... - Signature Algorithm: sha1WithRSAEncryption - 48:0e:2b:6f:20:62:4c:28:93:a3:24:3d:58:ab:21:cf:80:f8: - 9a:97:90:6a:22:ed:5a:7c:47:36:99:e7:79:84:75:ab:24:8f: - 92:0a:d5:61:04:ae:c3:6a:5c:b2:cc:d9:e4:44:87:6f:db:8f: - 38:62:f7:44:36:9d:ba:bc:6e:07:c4:d4:8d:e8:1f:d1:0b:60: - a3:b5:9c:ce:63:be:ed:67:dc:f8:ba:de:6e:c9:25:cb:5b:b5: - 9d:76:70:0b:df:42:72:f8:4f:41:11:64:a5:d2:ea:fc:d5:af: - 11:f4:15:38:67:9c:20:a8:4b:77:5a:91:32:42:32:e7:85:b3: - df:36 ------BEGIN CERTIFICATE----- -MIIEQjCCA6ugAwIBAgIEQodApTANBgkqhkiG9w0BAQUFADCBwzELMAkGA1UEBhMC -VVMxFDASBgNVBAoTC0VudHJ1c3QubmV0MTswOQYDVQQLEzJ3d3cuZW50cnVzdC5u -ZXQvQ1BTIGluY29ycC4gYnkgcmVmLiAobGltaXRzIGxpYWIuKTElMCMGA1UECxMc -KGMpIDE5OTkgRW50cnVzdC5uZXQgTGltaXRlZDE6MDgGA1UEAxMxRW50cnVzdC5u -ZXQgU2VjdXJlIFNlcnZlciBDZXJ0aWZpY2F0aW9uIEF1dGhvcml0eTAeFw0wNjEw -MDEwNTAwMDBaFw0xNDA3MjYxODE1MTVaMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQK -EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNV -BAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwggEiMA0GCSqG -SIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGzOVz5vvUu+UtLTKm3+WBP8nNJUm2cSrD -1ZQ0Z6IKHLBfaaZAscS3so/QmKSpQVk609yU1jzbdDikSsxNJYL3SqVTEjju80lt -cZF+Y7arpl/DpIT4T2JRvvjF7Ns4kuMG5QiRDMQoQVX7y1qJFX5x6DW/TXIJPb46 -OFBbdzEbjbPHJEWap6xtABRaBLe6E+tRCphBQSJOZWGHgUFQpnlcid4ZSlfVLuZd -HFMsfpjNGgYWpGhz0DQEE1yhcdNafFXbXmThN4cwVgTlEbQpgBLxeTmIogIRfCdm -t4i3ePLKCqg4qwpkwr9mXZWEwaElHoddGlALIBLMQbtuC1E4uEvLAgMBAAGjggET -MIIBDzASBgNVHRMBAf8ECDAGAQH/AgEBMCcGA1UdJQQgMB4GCCsGAQUFBwMBBggr -BgEFBQcDAgYIKwYBBQUHAwQwMwYIKwYBBQUHAQEEJzAlMCMGCCsGAQUFBzABhhdo -dHRwOi8vb2NzcC5lbnRydXN0Lm5ldDAzBgNVHR8ELDAqMCigJqAkhiJodHRwOi8v -Y3JsLmVudHJ1c3QubmV0L3NlcnZlcjEuY3JsMB0GA1UdDgQWBBSxPsNpA/i/RwHU -mCYaCALvY2QrwzALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAU8BdiE1U9s/8KAGv7 -UISX8+1i0BowGQYJKoZIhvZ9B0EABAwwChsEVjcuMQMCAIEwDQYJKoZIhvcNAQEF -BQADgYEASA4rbyBiTCiToyQ9WKshz4D4mpeQaiLtWnxHNpnneYR1qySPkgrVYQSu -w2pcsszZ5ESHb9uPOGL3RDadurxuB8TUjegf0Qtgo7WczmO+7Wfc+Lrebskly1u1 -nXZwC99CcvhPQRFkpdLq/NWvEfQVOGecIKhLd1qRMkIy54Wz3zY= ------END CERTIFICATE----- \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-channel.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-channel.json new file mode 100644 index 0000000..75aeac3 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-channel.json @@ -0,0 +1,64 @@ +{ + "schema_version": "2.0", + + // All repositories must be an HTTP or HTTPS URL. HTTPS is vastly superior + // since verification of the source server is performed on SSL certificates. + "repositories": [ + "http://sublime.wbond.net/packages.json", + "https://github.com/buymeasoda/soda-theme", + "https://github.com/SublimeText" + ], + + // The "packages_cache" is completely optional, but allows the + // channel to cache and deliver package data from multiple + // repositories in a single HTTP request, allowing for significantly + // improved performance. + "packages_cache": { + + // The first level keys are the repository URLs + "http://sublime.wbond.net/packages.json": [ + + // Each repository has an array of packages with their fully + // expanded info. This means that the "details" key must be expanded + // into the various keys it provides. + { + "name": "Alignment", + "description": "Multi-line and multiple selection alignment plugin", + "author": "wbond", + "homepage": "http://wbond.net/sublime_packages/alignment", + "releases": [ + { + "version": "2.0.0", + "url": "https://sublime.wbond.net/Alignment.sublime-package", + "date": "2011-09-18 20:12:41" + } + ] + } + ] + }, + + // Package Control ships with the SSL Certificate Authority (CA) cert for the + // SSL certificate that secures and identifies sublime.wbond.net. After this + // initial connection is made, the channel server provides a list of CA certs + // for the various URLs that Package Control need to connect to. This way the + // default channel (https://sublime.wbond.net/channel.json) can provide + // real-time updates to CA certs in the case that a CA is compromised. The + // CA certs are extracted from openssl, and the server runs on an LTS version + // of Ubuntu, which automatically applies security patches from the official + // Ubuntu repositories. This architecture helps to ensure that the packages + // being downloaded are from the source listed and that users are very + // unlikely to be the subject of the man-in-the-middle attack. + "certs": { + + // All certs have the domain they apply to as the key + "sublime.wbond.net": [ + // The value is an array of two elements, the first being an md5 + // hash of the contents of the certificate. This helps in detecting + // CA cert changes. The second element is the URL where the cert + // can be downloaded, if it is not already installed on the user’s + // copy of Sublime Text. + "7f4f8622b4fd001c7f648e09aae7edaa", + "https://sublime.wbond.net/certs/7f4f8622b4fd001c7f648e09aae7edaa" + ] + } +} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-packages.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-packages.json deleted file mode 100644 index 96945cc..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-packages.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "schema_version": "1.2", - "packages": [ - { - "name": "GitHub Example", - "description": "An example from GitHub, be sure to use the zipball URL", - "author": "John Smith", - "homepage": "http://example.com", - "last_modified": "2011-12-12 05:04:31", - "platforms": { - "*": [ - { - "version": "1.1", - "url": "http://nodeload.github.com/john_smith/github_example/zipball/master" - } - ] - } - }, - { - "name": "BitBucket Example", - "description": "An example from BitBucket, be sure to use the zip URL", - "author": "John Smith", - "homepage": "http://example.com", - "last_modified": "2011-08-12 12:21:09", - "platforms": { - "*": [ - { - "version": "1.0", - "url": "https://bitbucket.org/john_smith/bitbucket_example/get/tip.zip" - } - ] - } - }, - { - "name": "Tortoise", - "description": "Keyboard shortcuts and menu entries to execute TortoiseSVN, TortoiseHg and TortoiseGit commands", - "author": "Will Bond", - "homepage": "http://sublime.wbond.net", - "last_modified": "2011-11-30 22:55:52", - "platforms": { - "windows": [ - { - "version": "1.0", - "url": "http://sublime.wbond.net/Tortoise.sublime-package" - } - ] - } - } - ], - "renamed_packages": { - "sublime-old-package": "NewPackage", - "OldPackage": "NewName" - } -} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-repositories.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-repositories.json deleted file mode 100644 index 9e03b1e..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-repositories.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "schema_version": "1.2", - "repositories": [ - "http://sublime.wbond.net/packages.json", - "https://github.com/buymeasoda/soda-theme", - "https://github.com/SublimeText" - ], - "package_name_map": { - "soda-theme": "Theme - Soda" - }, - "renamed_packages": { - "old-name": "New Name" - }, - "packages": { - "http://sublime.wbond.net/packages.json": [ - { - "name": "GitHub Example", - "description": "An example from GitHub, be sure to use the zipball URL", - "author": "John Smith", - "homepage": "http://example.com", - "platforms": { - "*": [ - { - "version": "1.1", - "url": "http://nodeload.github.com/john_smith/github_example/zipball/master" - } - ] - } - }, - { - "name": "BitBucket Example", - "description": "An example from BitBucket, be sure to use the zip URL", - "author": "John Smith", - "homepage": "http://example.com", - "platforms": { - "*": [ - { - "version": "1.0", - "url": "https://bitbucket.org/john_smith/bitbucket_example/get/tip.zip" - } - ] - } - }, - { - "name": "Tortoise", - "description": "Keyboard shortcuts and menu entries to execute TortoiseSVN, TortoiseHg and TortoiseGit commands", - "author": "Will Bond", - "homepage": "http://sublime.wbond.net", - "platforms": { - "windows": [ - { - "version": "1.0", - "url": "http://sublime.wbond.net/Tortoise.sublime-package" - } - ] - } - } - ] - } -} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-repository.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-repository.json new file mode 100644 index 0000000..39fe43d --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/example-repository.json @@ -0,0 +1,275 @@ +{ + "schema_version": "2.0", + + // Packages can be specified with a simple URL to a GitHub or BitBucket + // repository, but details can be overridden for every field. It is + // also possible not utilize GitHub or BitBucket at all, but just to + // host your packages on any server with an SSL certificate. + "packages": [ + + // This is what most packages should aim to model. + // + // The majority of the information about a package ("name", + // "description", "author") are all pulled from the GitHub (or + // BitBucket) repository info. + // + // If the word "sublime" exists in the repository name, the name + // can be overridden by the "name" key. + // + // A release is created from the the tag that is the highest semantic + // versioning version number in the list of tags. + { + "name": "Alignment", + "details": "https://github.com/wbond/sublime_alignment", + "releases": [ + { + "details": "https://github.com/wbond/sublime_alignment/tags" + } + ] + }, + + // Here is an equivalent package being pulled from BitBucket + { + "name": "Alignment", + "details": "https://bitbucket.org/wbond/sublime_alignment", + "releases": [ + { + "details": "https://bitbucket.org/wbond/sublime_alignment#tags" + } + ] + }, + + // Pull most details from GitHub, releases from master branch. + // This form is discouraged because users will upgrade to every single + // commit you make to master. + { + "details": "https://github.com/wbond/sublime_alignment" + }, + + // Pull most details from a BitBucket repository and releases from + // the branch "default" or "master", depending on how your repository + // is configured. + // Similar to the above example, this form is discouraged because users + // will upgrade to every single commit you make to master. + { + "details": "https://bitbucket.org/wbond/sublime_alignment" + }, + + // Use a custom name instead of just the URL slug + { + "name": "Alignment", + "details": "https://github.com/wbond/sublime_alignment" + }, + + // You can also override the homepage and author + { + "name": "Alignment", + "details": "https://github.com/wbond/sublime_alignment", + "homepage": "http://wbond.net/sublime_packages/alignment", + "author": "wbond" + }, + + // It is possible to provide the URL to a readme file. This URL + // should be to the raw source of the file, not rendered HTML. + // GitHub and BitBucket repositories will automatically provide + // these. + // + // The following extensions will be rendered: + // + // .markdown, .mdown, .mkd, .md + // .texttile + // .creole + // .rst + // + // All others are treated as plaintext. + { + "details": "https://github.com/wbond/sublime_alignment", + "readme": "https://raw.github.com/wbond/sublime_alignment/master/readme.creole" + }, + + // If a package has a public bug tracker, the URL should be + // included via the "issues" key. Both GitHub and BitBucket + // repositories will automatically provide this if they have + // issues enabled. + { + "details": "https://github.com/wbond/sublime_alignment", + "issues": "https://github.com/wbond/sublime_alignment/issues" + }, + + // The URL to donate to support the development of a package. + // GitHub and BitBucket repositories will default to: + // + // https://www.gittip.com/{username}/ + // + // Other URLs with special integration include: + // + // https://flattr.com/profile/{username} + // https://www.dwolla.com/hub/{username} + // + // This may also contain a URL to another other donation-type site + // where users may support the author for their development of the + // package. + { + "details": "https://github.com/wbond/sublime_alignment", + "donate": "https://www.gittip.com/wbond/" + }, + + // The URL to purchase a license to the package + { + "details": "https://github.com/wbond/sublime_alignment", + "buy": "https://wbond.net/sublime_packages/alignment/buy" + }, + + // If you rename a package, you can provide the previous name(s) + // so that users with the old package name can be automatically + // upgraded to the new one. + { + "name": "Alignment", + "details": "https://github.com/wbond/sublime_alignment", + "previous_names": ["sublime_alignment"] + }, + + // Packages can be labelled for the purpose of creating a + // folksonomy so users may more easily find relevant packages. + // Labels should be all lower case and should use spaces instead + // of _ or - to separate words. + // + // Some suggested labels are listed below, however, anything can + // be used as a label: + // + // auto-complete + // browser integration + // build system + // code navigation + // code sharing + // color scheme + // deprecated + // diff/merge + // editor emulation + // file creation + // file navigation + // formatting + // ftp + // language syntax + // linting + // minification + // search + // snippets + // terminal/shell/repl + // testing + // text manipulation + // text navigation + // theme + // todo + // vcs + { + "details": "https://github.com/wbond/sublime_alignment", + "labels": ["text manipulation", "formatting"] + }, + + // In addition to the recommendation above of pulling releases + // from tags that are semantic version numbers, releases can also + // comefrom a custom branch. + { + "details": "https://github.com/wbond/sublime_alignment", + "releases": [ + { + "details": "https://github.com/wbond/sublime_alignment/tree/custom_branch" + } + ] + }, + + // An equivalent package being pulled from BitBucket. + { + "details": "https://bitbucket.org/wbond/sublime_alignment", + "releases": [ + { + "details": "https://bitbucket.org/wbond/sublime_alignment/src/custom_branch" + } + ] + }, + + // If your package is only compatible with specific builds of + // Sublime Text, this will cause the package to be hidden from + // users with incompatible versions. + { + "details": "https://github.com/wbond/sublime_alignment", + "releases": [ + { + // Could also be >2999 for ST3. Leaving this out indicates + // the package works with both ST2 and ST3. + "sublime_text": "<3000", + "details": "https://github.com/wbond/sublime_alignment" + } + ] + }, + + // The "platforms" key allows specifying what platform(s) the release + // is valid for. As shown, there can be multiple releases of a package + // at any given time. However, only the latest version for any given + // platform/arch will be shown to the user. + // + // The "platforms" key allows specifying a single platform, or a list + // of platforms. Valid platform indentifiers include: + // + // "*" + // "windows", "windows-x64", "windows-x32" + // "osx", "osx-x64" + // "linux", "linux-x32", "linux-x64" + { + "details": "https://github.com/wbond/sublime_alignment", + "releases": [ + { + // Defaults to "*", or all platforms. + "platforms": ["osx", "linux"], + "details": "https://github.com/wbond/sublime_alignment/tree/posix" + }, + { + "platforms": "windows", + "details": "https://github.com/wbond/sublime_alignment/tree/win32" + } + ] + }, + + // If you don't use a "details" key for a "releases" entry, you need to + // specify the "version", "url" and "date" manually. + { + "details": "https://github.com/wbond/sublime_alignment", + "releases": [ + { + // The version number needs to be a semantic version number per + // http://semver.org 2.x.x + "version": "2.0.0", + + // The URL needs to be a zip file containing the package. It is permissible + // for the zip file to contain a single root folder with any name. All + // file will be extracted out of this single root folder. This allows + // zip files from GitHub and BitBucket to be used a sources. + "url": "https://codeload.github.com/wbond/sublime_alignment/zip/v2.0.0", + + // The date MUST be in the form "YYYY-MM-DD HH:MM:SS" and SHOULD be UTC + "date": "2011-09-18 20:12:41" + } + ] + } + ], + + // If you need/want to split your repository up into multiple smaller files + // for the sake of organization, the "includes" key allows you to enter + // URL paths that will be combined together and dynamically inserted + // into the "packages" key. These URLs these can be relative or absolute. + "includes": [ + + // Here is an example of how relative paths work for URLs. If this file + // was loaded from: + // "https://sublime.wbond.net/example-repository.json" + // then the following files would be loaded from: + // "https://sublime.wbond.net/repository/0-9.json" + // "https://sublime.wbond.net/repository/a.json" + "./repository/0-9.json", + "./repository/a.json", + + // An example of an absolute URL + "https://sublime.wbond.net/repository/b.json" + ] +} diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/all/semver.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/all/semver.py deleted file mode 100644 index 73d4ea2..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/all/semver.py +++ /dev/null @@ -1,86 +0,0 @@ -# -*- coding: utf-8 -*- -# This code is copyright Konstantine Rybnikov , and is -# available at https://github.com/k-bx/python-semver and is licensed under the -# BSD License - -import re - -_REGEX = re.compile('^(?P[0-9]+)' - '\.(?P[0-9]+)' - '\.(?P[0-9]+)' - '(\-(?P[0-9A-Za-z]+(\.[0-9A-Za-z]+)*))?' - '(\+(?P[0-9A-Za-z]+(\.[0-9A-Za-z]+)*))?$') - -if 'cmp' not in __builtins__: - cmp = lambda a,b: (a > b) - (a < b) - -def parse(version): - """ - Parse version to major, minor, patch, pre-release, build parts. - """ - match = _REGEX.match(version) - if match is None: - raise ValueError('%s is not valid SemVer string' % version) - - verinfo = match.groupdict() - - verinfo['major'] = int(verinfo['major']) - verinfo['minor'] = int(verinfo['minor']) - verinfo['patch'] = int(verinfo['patch']) - - return verinfo - - -def compare(ver1, ver2): - def nat_cmp(a, b): - a, b = a or '', b or '' - convert = lambda text: text.isdigit() and int(text) or text.lower() - alphanum_key = lambda key: [convert(c) for c in re.split('([0-9]+)', key)] - return cmp(alphanum_key(a), alphanum_key(b)) - - def compare_by_keys(d1, d2): - for key in ['major', 'minor', 'patch']: - v = cmp(d1.get(key), d2.get(key)) - if v: - return v - rc1, rc2 = d1.get('prerelease'), d2.get('prerelease') - build1, build2 = d1.get('build'), d2.get('build') - rccmp = nat_cmp(rc1, rc2) - buildcmp = nat_cmp(build1, build2) - if not (rc1 or rc2): - return buildcmp - elif not rc1: - return 1 - elif not rc2: - return -1 - return rccmp or buildcmp or 0 - - v1, v2 = parse(ver1), parse(ver2) - - return compare_by_keys(v1, v2) - - -def match(version, match_expr): - prefix = match_expr[:2] - if prefix in ('>=', '<=', '=='): - match_version = match_expr[2:] - elif prefix and prefix[0] in ('>', '<', '='): - prefix = prefix[0] - match_version = match_expr[1:] - else: - raise ValueError("match_expr parameter should be in format , " - "where is one of ['<', '>', '==', '<=', '>=']. " - "You provided: %r" % match_expr) - - possibilities_dict = { - '>': (1,), - '<': (-1,), - '==': (0,), - '>=': (0, 1), - '<=': (-1, 0) - } - - possibilities = possibilities_dict[prefix] - cmp_res = compare(version, match_version) - - return cmp_res in possibilities \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/U32.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/U32.py deleted file mode 100644 index 7b3e4c8..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/U32.py +++ /dev/null @@ -1,113 +0,0 @@ -# This file is part of 'NTLM Authorization Proxy Server' http://sourceforge.net/projects/ntlmaps/ -# Copyright 2001 Dmitry A. Rozmanov -# -# This library 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 library 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 library. If not, see or . - - -C = 0x1000000000L - -def norm(n): - return n & 0xFFFFFFFFL - - -class U32: - v = 0L - - def __init__(self, value = 0): - self.v = C + norm(abs(long(value))) - - def set(self, value = 0): - self.v = C + norm(abs(long(value))) - - def __repr__(self): - return hex(norm(self.v)) - - def __long__(self): return long(norm(self.v)) - def __int__(self): return int(norm(self.v)) - def __chr__(self): return chr(norm(self.v)) - - def __add__(self, b): - r = U32() - r.v = C + norm(self.v + b.v) - return r - - def __sub__(self, b): - r = U32() - if self.v < b.v: - r.v = C + norm(0x100000000L - (b.v - self.v)) - else: r.v = C + norm(self.v - b.v) - return r - - def __mul__(self, b): - r = U32() - r.v = C + norm(self.v * b.v) - return r - - def __div__(self, b): - r = U32() - r.v = C + (norm(self.v) / norm(b.v)) - return r - - def __mod__(self, b): - r = U32() - r.v = C + (norm(self.v) % norm(b.v)) - return r - - def __neg__(self): return U32(self.v) - def __pos__(self): return U32(self.v) - def __abs__(self): return U32(self.v) - - def __invert__(self): - r = U32() - r.v = C + norm(~self.v) - return r - - def __lshift__(self, b): - r = U32() - r.v = C + norm(self.v << b) - return r - - def __rshift__(self, b): - r = U32() - r.v = C + (norm(self.v) >> b) - return r - - def __and__(self, b): - r = U32() - r.v = C + norm(self.v & b.v) - return r - - def __or__(self, b): - r = U32() - r.v = C + norm(self.v | b.v) - return r - - def __xor__(self, b): - r = U32() - r.v = C + norm(self.v ^ b.v) - return r - - def __not__(self): - return U32(not norm(self.v)) - - def truth(self): - return norm(self.v) - - def __cmp__(self, b): - if norm(self.v) > norm(b.v): return 1 - elif norm(self.v) < norm(b.v): return -1 - else: return 0 - - def __nonzero__(self): - return norm(self.v) \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des.py deleted file mode 100644 index 6a59964..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des.py +++ /dev/null @@ -1,92 +0,0 @@ -# This file is part of 'NTLM Authorization Proxy Server' http://sourceforge.net/projects/ntlmaps/ -# Copyright 2001 Dmitry A. Rozmanov -# -# This library 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 library 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 library. If not, see or . - -import des_c - -#--------------------------------------------------------------------- -class DES: - - des_c_obj = None - - #----------------------------------------------------------------- - def __init__(self, key_str): - "" - k = str_to_key56(key_str) - k = key56_to_key64(k) - key_str = '' - for i in k: - key_str += chr(i & 0xFF) - self.des_c_obj = des_c.DES(key_str) - - #----------------------------------------------------------------- - def encrypt(self, plain_text): - "" - return self.des_c_obj.encrypt(plain_text) - - #----------------------------------------------------------------- - def decrypt(self, crypted_text): - "" - return self.des_c_obj.decrypt(crypted_text) - -#--------------------------------------------------------------------- -#Some Helpers -#--------------------------------------------------------------------- - -DESException = 'DESException' - -#--------------------------------------------------------------------- -def str_to_key56(key_str): - "" - if type(key_str) != type(''): - #rise DESException, 'ERROR. Wrong key type.' - pass - if len(key_str) < 7: - key_str = key_str + '\000\000\000\000\000\000\000'[:(7 - len(key_str))] - key_56 = [] - for i in key_str[:7]: key_56.append(ord(i)) - - return key_56 - -#--------------------------------------------------------------------- -def key56_to_key64(key_56): - "" - key = [] - for i in range(8): key.append(0) - - key[0] = key_56[0]; - key[1] = ((key_56[0] << 7) & 0xFF) | (key_56[1] >> 1); - key[2] = ((key_56[1] << 6) & 0xFF) | (key_56[2] >> 2); - key[3] = ((key_56[2] << 5) & 0xFF) | (key_56[3] >> 3); - key[4] = ((key_56[3] << 4) & 0xFF) | (key_56[4] >> 4); - key[5] = ((key_56[4] << 3) & 0xFF) | (key_56[5] >> 5); - key[6] = ((key_56[5] << 2) & 0xFF) | (key_56[6] >> 6); - key[7] = (key_56[6] << 1) & 0xFF; - - key = set_key_odd_parity(key) - - return key - -#--------------------------------------------------------------------- -def set_key_odd_parity(key): - "" - for i in range(len(key)): - for k in range(7): - bit = 0 - t = key[i] >> k - bit = (t ^ bit) & 0x1 - key[i] = (key[i] & 0xFE) | bit - - return key diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des_c.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des_c.py deleted file mode 100644 index 61af403..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des_c.py +++ /dev/null @@ -1,328 +0,0 @@ -# This file is part of 'NTLM Authorization Proxy Server' http://sourceforge.net/projects/ntlmaps/ -# Copyright 2001 Dmitry A. Rozmanov -# -# This library 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 library 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 library. If not, see or . - -from U32 import U32 - -# --NON ASCII COMMENT ELIDED-- -#typedef unsigned char des_cblock[8]; -#define HDRSIZE 4 - -def c2l(c): - "char[4] to unsigned long" - l = U32(c[0]) - l = l | (U32(c[1]) << 8) - l = l | (U32(c[2]) << 16) - l = l | (U32(c[3]) << 24) - return l - -def c2ln(c,l1,l2,n): - "char[n] to two unsigned long???" - c = c + n - l1, l2 = U32(0), U32(0) - - f = 0 - if n == 8: - l2 = l2 | (U32(c[7]) << 24) - f = 1 - if f or (n == 7): - l2 = l2 | (U32(c[6]) << 16) - f = 1 - if f or (n == 6): - l2 = l2 | (U32(c[5]) << 8) - f = 1 - if f or (n == 5): - l2 = l2 | U32(c[4]) - f = 1 - if f or (n == 4): - l1 = l1 | (U32(c[3]) << 24) - f = 1 - if f or (n == 3): - l1 = l1 | (U32(c[2]) << 16) - f = 1 - if f or (n == 2): - l1 = l1 | (U32(c[1]) << 8) - f = 1 - if f or (n == 1): - l1 = l1 | U32(c[0]) - return (l1, l2) - -def l2c(l): - "unsigned long to char[4]" - c = [] - c.append(int(l & U32(0xFF))) - c.append(int((l >> 8) & U32(0xFF))) - c.append(int((l >> 16) & U32(0xFF))) - c.append(int((l >> 24) & U32(0xFF))) - return c - -def n2l(c, l): - "network to host long" - l = U32(c[0] << 24) - l = l | (U32(c[1]) << 16) - l = l | (U32(c[2]) << 8) - l = l | (U32(c[3])) - return l - -def l2n(l, c): - "host to network long" - c = [] - c.append(int((l >> 24) & U32(0xFF))) - c.append(int((l >> 16) & U32(0xFF))) - c.append(int((l >> 8) & U32(0xFF))) - c.append(int((l ) & U32(0xFF))) - return c - -def l2cn(l1, l2, c, n): - "" - for i in range(n): c.append(0x00) - f = 0 - if f or (n == 8): - c[7] = int((l2 >> 24) & U32(0xFF)) - f = 1 - if f or (n == 7): - c[6] = int((l2 >> 16) & U32(0xFF)) - f = 1 - if f or (n == 6): - c[5] = int((l2 >> 8) & U32(0xFF)) - f = 1 - if f or (n == 5): - c[4] = int((l2 ) & U32(0xFF)) - f = 1 - if f or (n == 4): - c[3] = int((l1 >> 24) & U32(0xFF)) - f = 1 - if f or (n == 3): - c[2] = int((l1 >> 16) & U32(0xFF)) - f = 1 - if f or (n == 2): - c[1] = int((l1 >> 8) & U32(0xFF)) - f = 1 - if f or (n == 1): - c[0] = int((l1 ) & U32(0xFF)) - f = 1 - return c[:n] - -# array of data -# static unsigned long des_SPtrans[8][64]={ -# static unsigned long des_skb[8][64]={ -from des_data import des_SPtrans, des_skb - -def D_ENCRYPT(tup, u, t, s): - L, R, S = tup - #print 'LRS1', L, R, S, u, t, '-->', - u = (R ^ s[S]) - t = R ^ s[S + 1] - t = ((t >> 4) + (t << 28)) - L = L ^ (des_SPtrans[1][int((t ) & U32(0x3f))] | \ - des_SPtrans[3][int((t >> 8) & U32(0x3f))] | \ - des_SPtrans[5][int((t >> 16) & U32(0x3f))] | \ - des_SPtrans[7][int((t >> 24) & U32(0x3f))] | \ - des_SPtrans[0][int((u ) & U32(0x3f))] | \ - des_SPtrans[2][int((u >> 8) & U32(0x3f))] | \ - des_SPtrans[4][int((u >> 16) & U32(0x3f))] | \ - des_SPtrans[6][int((u >> 24) & U32(0x3f))]) - #print 'LRS:', L, R, S, u, t - return ((L, R, S), u, t, s) - - -def PERM_OP (tup, n, m): - "tup - (a, b, t)" - a, b, t = tup - t = ((a >> n) ^ b) & m - b = b ^ t - a = a ^ (t << n) - return (a, b, t) - -def HPERM_OP (tup, n, m): - "tup - (a, t)" - a, t = tup - t = ((a << (16 - n)) ^ a) & m - a = a ^ t ^ (t >> (16 - n)) - return (a, t) - -shifts2 = [0,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0] - -class DES: - KeySched = None # des_key_schedule - - def __init__(self, key_str): - # key - UChar[8] - key = [] - for i in key_str: key.append(ord(i)) - #print 'key:', key - self.KeySched = des_set_key(key) - #print 'schedule:', self.KeySched, len(self.KeySched) - - def decrypt(self, str): - # block - UChar[] - block = [] - for i in str: block.append(ord(i)) - #print block - block = des_ecb_encrypt(block, self.KeySched, 0) - res = '' - for i in block: res = res + (chr(i)) - return res - - def encrypt(self, str): - # block - UChar[] - block = [] - for i in str: block.append(ord(i)) - block = des_ecb_encrypt(block, self.KeySched, 1) - res = '' - for i in block: res = res + (chr(i)) - return res - - - - - - -#------------------------ -def des_encript(input, ks, encrypt): - # input - U32[] - # output - U32[] - # ks - des_key_shedule - U32[2][16] - # encrypt - int - # l, r, t, u - U32 - # i - int - # s - U32[] - - l = input[0] - r = input[1] - t = U32(0) - u = U32(0) - - r, l, t = PERM_OP((r, l, t), 4, U32(0x0f0f0f0fL)) - l, r, t = PERM_OP((l, r, t), 16, U32(0x0000ffffL)) - r, l, t = PERM_OP((r, l, t), 2, U32(0x33333333L)) - l, r, t = PERM_OP((l, r, t), 8, U32(0x00ff00ffL)) - r, l, t = PERM_OP((r, l, t), 1, U32(0x55555555L)) - - t = (r << 1)|(r >> 31) - r = (l << 1)|(l >> 31) - l = t - - s = ks # ??????????????? - #print l, r - if(encrypt): - for i in range(0, 32, 4): - rtup, u, t, s = D_ENCRYPT((l, r, i + 0), u, t, s) - l = rtup[0] - r = rtup[1] - rtup, u, t, s = D_ENCRYPT((r, l, i + 2), u, t, s) - r = rtup[0] - l = rtup[1] - else: - for i in range(30, 0, -4): - rtup, u, t, s = D_ENCRYPT((l, r, i - 0), u, t, s) - l = rtup[0] - r = rtup[1] - rtup, u, t, s = D_ENCRYPT((r, l, i - 2), u, t, s) - r = rtup[0] - l = rtup[1] - #print l, r - l = (l >> 1)|(l << 31) - r = (r >> 1)|(r << 31) - - r, l, t = PERM_OP((r, l, t), 1, U32(0x55555555L)) - l, r, t = PERM_OP((l, r, t), 8, U32(0x00ff00ffL)) - r, l, t = PERM_OP((r, l, t), 2, U32(0x33333333L)) - l, r, t = PERM_OP((l, r, t), 16, U32(0x0000ffffL)) - r, l, t = PERM_OP((r, l, t), 4, U32(0x0f0f0f0fL)) - - output = [l] - output.append(r) - l, r, t, u = U32(0), U32(0), U32(0), U32(0) - return output - -def des_ecb_encrypt(input, ks, encrypt): - # input - des_cblock - UChar[8] - # output - des_cblock - UChar[8] - # ks - des_key_shedule - U32[2][16] - # encrypt - int - - #print input - l0 = c2l(input[0:4]) - l1 = c2l(input[4:8]) - ll = [l0] - ll.append(l1) - #print ll - ll = des_encript(ll, ks, encrypt) - #print ll - l0 = ll[0] - l1 = ll[1] - output = l2c(l0) - output = output + l2c(l1) - #print output - l0, l1, ll[0], ll[1] = U32(0), U32(0), U32(0), U32(0) - return output - -def des_set_key(key): - # key - des_cblock - UChar[8] - # schedule - des_key_schedule - - # register unsigned long c,d,t,s; - # register unsigned char *in; - # register unsigned long *k; - # register int i; - - #k = schedule - # in = key - - k = [] - c = c2l(key[0:4]) - d = c2l(key[4:8]) - t = U32(0) - - d, c, t = PERM_OP((d, c, t), 4, U32(0x0f0f0f0fL)) - c, t = HPERM_OP((c, t), -2, U32(0xcccc0000L)) - d, t = HPERM_OP((d, t), -2, U32(0xcccc0000L)) - d, c, t = PERM_OP((d, c, t), 1, U32(0x55555555L)) - c, d, t = PERM_OP((c, d, t), 8, U32(0x00ff00ffL)) - d, c, t = PERM_OP((d, c, t), 1, U32(0x55555555L)) - - d = (((d & U32(0x000000ffL)) << 16)|(d & U32(0x0000ff00L))|((d & U32(0x00ff0000L)) >> 16)|((c & U32(0xf0000000L)) >> 4)) - c = c & U32(0x0fffffffL) - - for i in range(16): - if (shifts2[i]): - c = ((c >> 2)|(c << 26)) - d = ((d >> 2)|(d << 26)) - else: - c = ((c >> 1)|(c << 27)) - d = ((d >> 1)|(d << 27)) - c = c & U32(0x0fffffffL) - d = d & U32(0x0fffffffL) - - s= des_skb[0][int((c ) & U32(0x3f))]|\ - des_skb[1][int(((c>> 6) & U32(0x03))|((c>> 7) & U32(0x3c)))]|\ - des_skb[2][int(((c>>13) & U32(0x0f))|((c>>14) & U32(0x30)))]|\ - des_skb[3][int(((c>>20) & U32(0x01))|((c>>21) & U32(0x06)) | ((c>>22) & U32(0x38)))] - - t= des_skb[4][int((d ) & U32(0x3f) )]|\ - des_skb[5][int(((d>> 7) & U32(0x03))|((d>> 8) & U32(0x3c)))]|\ - des_skb[6][int((d>>15) & U32(0x3f) )]|\ - des_skb[7][int(((d>>21) & U32(0x0f))|((d>>22) & U32(0x30)))] - #print s, t - - k.append(((t << 16)|(s & U32(0x0000ffffL))) & U32(0xffffffffL)) - s = ((s >> 16)|(t & U32(0xffff0000L))) - s = (s << 4)|(s >> 28) - k.append(s & U32(0xffffffffL)) - - schedule = k - - return schedule diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des_data.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des_data.py deleted file mode 100644 index 9867876..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/des_data.py +++ /dev/null @@ -1,348 +0,0 @@ -# This file is part of 'NTLM Authorization Proxy Server' http://sourceforge.net/projects/ntlmaps/ -# Copyright 2001 Dmitry A. Rozmanov -# -# This library 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 library 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 library. If not, see or . - -from U32 import U32 - -# static unsigned long des_SPtrans[8][64]={ - -des_SPtrans =\ -[ -#nibble 0 -[ -U32(0x00820200L), U32(0x00020000L), U32(0x80800000L), U32(0x80820200L), -U32(0x00800000L), U32(0x80020200L), U32(0x80020000L), U32(0x80800000L), -U32(0x80020200L), U32(0x00820200L), U32(0x00820000L), U32(0x80000200L), -U32(0x80800200L), U32(0x00800000L), U32(0x00000000L), U32(0x80020000L), -U32(0x00020000L), U32(0x80000000L), U32(0x00800200L), U32(0x00020200L), -U32(0x80820200L), U32(0x00820000L), U32(0x80000200L), U32(0x00800200L), -U32(0x80000000L), U32(0x00000200L), U32(0x00020200L), U32(0x80820000L), -U32(0x00000200L), U32(0x80800200L), U32(0x80820000L), U32(0x00000000L), -U32(0x00000000L), U32(0x80820200L), U32(0x00800200L), U32(0x80020000L), -U32(0x00820200L), U32(0x00020000L), U32(0x80000200L), U32(0x00800200L), -U32(0x80820000L), U32(0x00000200L), U32(0x00020200L), U32(0x80800000L), -U32(0x80020200L), U32(0x80000000L), U32(0x80800000L), U32(0x00820000L), -U32(0x80820200L), U32(0x00020200L), U32(0x00820000L), U32(0x80800200L), -U32(0x00800000L), U32(0x80000200L), U32(0x80020000L), U32(0x00000000L), -U32(0x00020000L), U32(0x00800000L), U32(0x80800200L), U32(0x00820200L), -U32(0x80000000L), U32(0x80820000L), U32(0x00000200L), U32(0x80020200L), -], - -#nibble 1 -[ -U32(0x10042004L), U32(0x00000000L), U32(0x00042000L), U32(0x10040000L), -U32(0x10000004L), U32(0x00002004L), U32(0x10002000L), U32(0x00042000L), -U32(0x00002000L), U32(0x10040004L), U32(0x00000004L), U32(0x10002000L), -U32(0x00040004L), U32(0x10042000L), U32(0x10040000L), U32(0x00000004L), -U32(0x00040000L), U32(0x10002004L), U32(0x10040004L), U32(0x00002000L), -U32(0x00042004L), U32(0x10000000L), U32(0x00000000L), U32(0x00040004L), -U32(0x10002004L), U32(0x00042004L), U32(0x10042000L), U32(0x10000004L), -U32(0x10000000L), U32(0x00040000L), U32(0x00002004L), U32(0x10042004L), -U32(0x00040004L), U32(0x10042000L), U32(0x10002000L), U32(0x00042004L), -U32(0x10042004L), U32(0x00040004L), U32(0x10000004L), U32(0x00000000L), -U32(0x10000000L), U32(0x00002004L), U32(0x00040000L), U32(0x10040004L), -U32(0x00002000L), U32(0x10000000L), U32(0x00042004L), U32(0x10002004L), -U32(0x10042000L), U32(0x00002000L), U32(0x00000000L), U32(0x10000004L), -U32(0x00000004L), U32(0x10042004L), U32(0x00042000L), U32(0x10040000L), -U32(0x10040004L), U32(0x00040000L), U32(0x00002004L), U32(0x10002000L), -U32(0x10002004L), U32(0x00000004L), U32(0x10040000L), U32(0x00042000L), -], - -#nibble 2 -[ -U32(0x41000000L), U32(0x01010040L), U32(0x00000040L), U32(0x41000040L), -U32(0x40010000L), U32(0x01000000L), U32(0x41000040L), U32(0x00010040L), -U32(0x01000040L), U32(0x00010000L), U32(0x01010000L), U32(0x40000000L), -U32(0x41010040L), U32(0x40000040L), U32(0x40000000L), U32(0x41010000L), -U32(0x00000000L), U32(0x40010000L), U32(0x01010040L), U32(0x00000040L), -U32(0x40000040L), U32(0x41010040L), U32(0x00010000L), U32(0x41000000L), -U32(0x41010000L), U32(0x01000040L), U32(0x40010040L), U32(0x01010000L), -U32(0x00010040L), U32(0x00000000L), U32(0x01000000L), U32(0x40010040L), -U32(0x01010040L), U32(0x00000040L), U32(0x40000000L), U32(0x00010000L), -U32(0x40000040L), U32(0x40010000L), U32(0x01010000L), U32(0x41000040L), -U32(0x00000000L), U32(0x01010040L), U32(0x00010040L), U32(0x41010000L), -U32(0x40010000L), U32(0x01000000L), U32(0x41010040L), U32(0x40000000L), -U32(0x40010040L), U32(0x41000000L), U32(0x01000000L), U32(0x41010040L), -U32(0x00010000L), U32(0x01000040L), U32(0x41000040L), U32(0x00010040L), -U32(0x01000040L), U32(0x00000000L), U32(0x41010000L), U32(0x40000040L), -U32(0x41000000L), U32(0x40010040L), U32(0x00000040L), U32(0x01010000L), -], - -#nibble 3 -[ -U32(0x00100402L), U32(0x04000400L), U32(0x00000002L), U32(0x04100402L), -U32(0x00000000L), U32(0x04100000L), U32(0x04000402L), U32(0x00100002L), -U32(0x04100400L), U32(0x04000002L), U32(0x04000000L), U32(0x00000402L), -U32(0x04000002L), U32(0x00100402L), U32(0x00100000L), U32(0x04000000L), -U32(0x04100002L), U32(0x00100400L), U32(0x00000400L), U32(0x00000002L), -U32(0x00100400L), U32(0x04000402L), U32(0x04100000L), U32(0x00000400L), -U32(0x00000402L), U32(0x00000000L), U32(0x00100002L), U32(0x04100400L), -U32(0x04000400L), U32(0x04100002L), U32(0x04100402L), U32(0x00100000L), -U32(0x04100002L), U32(0x00000402L), U32(0x00100000L), U32(0x04000002L), -U32(0x00100400L), U32(0x04000400L), U32(0x00000002L), U32(0x04100000L), -U32(0x04000402L), U32(0x00000000L), U32(0x00000400L), U32(0x00100002L), -U32(0x00000000L), U32(0x04100002L), U32(0x04100400L), U32(0x00000400L), -U32(0x04000000L), U32(0x04100402L), U32(0x00100402L), U32(0x00100000L), -U32(0x04100402L), U32(0x00000002L), U32(0x04000400L), U32(0x00100402L), -U32(0x00100002L), U32(0x00100400L), U32(0x04100000L), U32(0x04000402L), -U32(0x00000402L), U32(0x04000000L), U32(0x04000002L), U32(0x04100400L), -], - -#nibble 4 -[ -U32(0x02000000L), U32(0x00004000L), U32(0x00000100L), U32(0x02004108L), -U32(0x02004008L), U32(0x02000100L), U32(0x00004108L), U32(0x02004000L), -U32(0x00004000L), U32(0x00000008L), U32(0x02000008L), U32(0x00004100L), -U32(0x02000108L), U32(0x02004008L), U32(0x02004100L), U32(0x00000000L), -U32(0x00004100L), U32(0x02000000L), U32(0x00004008L), U32(0x00000108L), -U32(0x02000100L), U32(0x00004108L), U32(0x00000000L), U32(0x02000008L), -U32(0x00000008L), U32(0x02000108L), U32(0x02004108L), U32(0x00004008L), -U32(0x02004000L), U32(0x00000100L), U32(0x00000108L), U32(0x02004100L), -U32(0x02004100L), U32(0x02000108L), U32(0x00004008L), U32(0x02004000L), -U32(0x00004000L), U32(0x00000008L), U32(0x02000008L), U32(0x02000100L), -U32(0x02000000L), U32(0x00004100L), U32(0x02004108L), U32(0x00000000L), -U32(0x00004108L), U32(0x02000000L), U32(0x00000100L), U32(0x00004008L), -U32(0x02000108L), U32(0x00000100L), U32(0x00000000L), U32(0x02004108L), -U32(0x02004008L), U32(0x02004100L), U32(0x00000108L), U32(0x00004000L), -U32(0x00004100L), U32(0x02004008L), U32(0x02000100L), U32(0x00000108L), -U32(0x00000008L), U32(0x00004108L), U32(0x02004000L), U32(0x02000008L), -], - -#nibble 5 -[ -U32(0x20000010L), U32(0x00080010L), U32(0x00000000L), U32(0x20080800L), -U32(0x00080010L), U32(0x00000800L), U32(0x20000810L), U32(0x00080000L), -U32(0x00000810L), U32(0x20080810L), U32(0x00080800L), U32(0x20000000L), -U32(0x20000800L), U32(0x20000010L), U32(0x20080000L), U32(0x00080810L), -U32(0x00080000L), U32(0x20000810L), U32(0x20080010L), U32(0x00000000L), -U32(0x00000800L), U32(0x00000010L), U32(0x20080800L), U32(0x20080010L), -U32(0x20080810L), U32(0x20080000L), U32(0x20000000L), U32(0x00000810L), -U32(0x00000010L), U32(0x00080800L), U32(0x00080810L), U32(0x20000800L), -U32(0x00000810L), U32(0x20000000L), U32(0x20000800L), U32(0x00080810L), -U32(0x20080800L), U32(0x00080010L), U32(0x00000000L), U32(0x20000800L), -U32(0x20000000L), U32(0x00000800L), U32(0x20080010L), U32(0x00080000L), -U32(0x00080010L), U32(0x20080810L), U32(0x00080800L), U32(0x00000010L), -U32(0x20080810L), U32(0x00080800L), U32(0x00080000L), U32(0x20000810L), -U32(0x20000010L), U32(0x20080000L), U32(0x00080810L), U32(0x00000000L), -U32(0x00000800L), U32(0x20000010L), U32(0x20000810L), U32(0x20080800L), -U32(0x20080000L), U32(0x00000810L), U32(0x00000010L), U32(0x20080010L), -], - -#nibble 6 -[ -U32(0x00001000L), U32(0x00000080L), U32(0x00400080L), U32(0x00400001L), -U32(0x00401081L), U32(0x00001001L), U32(0x00001080L), U32(0x00000000L), -U32(0x00400000L), U32(0x00400081L), U32(0x00000081L), U32(0x00401000L), -U32(0x00000001L), U32(0x00401080L), U32(0x00401000L), U32(0x00000081L), -U32(0x00400081L), U32(0x00001000L), U32(0x00001001L), U32(0x00401081L), -U32(0x00000000L), U32(0x00400080L), U32(0x00400001L), U32(0x00001080L), -U32(0x00401001L), U32(0x00001081L), U32(0x00401080L), U32(0x00000001L), -U32(0x00001081L), U32(0x00401001L), U32(0x00000080L), U32(0x00400000L), -U32(0x00001081L), U32(0x00401000L), U32(0x00401001L), U32(0x00000081L), -U32(0x00001000L), U32(0x00000080L), U32(0x00400000L), U32(0x00401001L), -U32(0x00400081L), U32(0x00001081L), U32(0x00001080L), U32(0x00000000L), -U32(0x00000080L), U32(0x00400001L), U32(0x00000001L), U32(0x00400080L), -U32(0x00000000L), U32(0x00400081L), U32(0x00400080L), U32(0x00001080L), -U32(0x00000081L), U32(0x00001000L), U32(0x00401081L), U32(0x00400000L), -U32(0x00401080L), U32(0x00000001L), U32(0x00001001L), U32(0x00401081L), -U32(0x00400001L), U32(0x00401080L), U32(0x00401000L), U32(0x00001001L), -], - -#nibble 7 -[ -U32(0x08200020L), U32(0x08208000L), U32(0x00008020L), U32(0x00000000L), -U32(0x08008000L), U32(0x00200020L), U32(0x08200000L), U32(0x08208020L), -U32(0x00000020L), U32(0x08000000L), U32(0x00208000L), U32(0x00008020L), -U32(0x00208020L), U32(0x08008020L), U32(0x08000020L), U32(0x08200000L), -U32(0x00008000L), U32(0x00208020L), U32(0x00200020L), U32(0x08008000L), -U32(0x08208020L), U32(0x08000020L), U32(0x00000000L), U32(0x00208000L), -U32(0x08000000L), U32(0x00200000L), U32(0x08008020L), U32(0x08200020L), -U32(0x00200000L), U32(0x00008000L), U32(0x08208000L), U32(0x00000020L), -U32(0x00200000L), U32(0x00008000L), U32(0x08000020L), U32(0x08208020L), -U32(0x00008020L), U32(0x08000000L), U32(0x00000000L), U32(0x00208000L), -U32(0x08200020L), U32(0x08008020L), U32(0x08008000L), U32(0x00200020L), -U32(0x08208000L), U32(0x00000020L), U32(0x00200020L), U32(0x08008000L), -U32(0x08208020L), U32(0x00200000L), U32(0x08200000L), U32(0x08000020L), -U32(0x00208000L), U32(0x00008020L), U32(0x08008020L), U32(0x08200000L), -U32(0x00000020L), U32(0x08208000L), U32(0x00208020L), U32(0x00000000L), -U32(0x08000000L), U32(0x08200020L), U32(0x00008000L), U32(0x00208020L), -], -] - -#static unsigned long des_skb[8][64]={ - -des_skb = \ -[ -#for C bits (numbered as per FIPS 46) 1 2 3 4 5 6 -[ -U32(0x00000000L),U32(0x00000010L),U32(0x20000000L),U32(0x20000010L), -U32(0x00010000L),U32(0x00010010L),U32(0x20010000L),U32(0x20010010L), -U32(0x00000800L),U32(0x00000810L),U32(0x20000800L),U32(0x20000810L), -U32(0x00010800L),U32(0x00010810L),U32(0x20010800L),U32(0x20010810L), -U32(0x00000020L),U32(0x00000030L),U32(0x20000020L),U32(0x20000030L), -U32(0x00010020L),U32(0x00010030L),U32(0x20010020L),U32(0x20010030L), -U32(0x00000820L),U32(0x00000830L),U32(0x20000820L),U32(0x20000830L), -U32(0x00010820L),U32(0x00010830L),U32(0x20010820L),U32(0x20010830L), -U32(0x00080000L),U32(0x00080010L),U32(0x20080000L),U32(0x20080010L), -U32(0x00090000L),U32(0x00090010L),U32(0x20090000L),U32(0x20090010L), -U32(0x00080800L),U32(0x00080810L),U32(0x20080800L),U32(0x20080810L), -U32(0x00090800L),U32(0x00090810L),U32(0x20090800L),U32(0x20090810L), -U32(0x00080020L),U32(0x00080030L),U32(0x20080020L),U32(0x20080030L), -U32(0x00090020L),U32(0x00090030L),U32(0x20090020L),U32(0x20090030L), -U32(0x00080820L),U32(0x00080830L),U32(0x20080820L),U32(0x20080830L), -U32(0x00090820L),U32(0x00090830L),U32(0x20090820L),U32(0x20090830L), -], - -#for C bits (numbered as per FIPS 46) 7 8 10 11 12 13 -[ -U32(0x00000000L),U32(0x02000000L),U32(0x00002000L),U32(0x02002000L), -U32(0x00200000L),U32(0x02200000L),U32(0x00202000L),U32(0x02202000L), -U32(0x00000004L),U32(0x02000004L),U32(0x00002004L),U32(0x02002004L), -U32(0x00200004L),U32(0x02200004L),U32(0x00202004L),U32(0x02202004L), -U32(0x00000400L),U32(0x02000400L),U32(0x00002400L),U32(0x02002400L), -U32(0x00200400L),U32(0x02200400L),U32(0x00202400L),U32(0x02202400L), -U32(0x00000404L),U32(0x02000404L),U32(0x00002404L),U32(0x02002404L), -U32(0x00200404L),U32(0x02200404L),U32(0x00202404L),U32(0x02202404L), -U32(0x10000000L),U32(0x12000000L),U32(0x10002000L),U32(0x12002000L), -U32(0x10200000L),U32(0x12200000L),U32(0x10202000L),U32(0x12202000L), -U32(0x10000004L),U32(0x12000004L),U32(0x10002004L),U32(0x12002004L), -U32(0x10200004L),U32(0x12200004L),U32(0x10202004L),U32(0x12202004L), -U32(0x10000400L),U32(0x12000400L),U32(0x10002400L),U32(0x12002400L), -U32(0x10200400L),U32(0x12200400L),U32(0x10202400L),U32(0x12202400L), -U32(0x10000404L),U32(0x12000404L),U32(0x10002404L),U32(0x12002404L), -U32(0x10200404L),U32(0x12200404L),U32(0x10202404L),U32(0x12202404L), -], - -#for C bits (numbered as per FIPS 46) 14 15 16 17 19 20 -[ -U32(0x00000000L),U32(0x00000001L),U32(0x00040000L),U32(0x00040001L), -U32(0x01000000L),U32(0x01000001L),U32(0x01040000L),U32(0x01040001L), -U32(0x00000002L),U32(0x00000003L),U32(0x00040002L),U32(0x00040003L), -U32(0x01000002L),U32(0x01000003L),U32(0x01040002L),U32(0x01040003L), -U32(0x00000200L),U32(0x00000201L),U32(0x00040200L),U32(0x00040201L), -U32(0x01000200L),U32(0x01000201L),U32(0x01040200L),U32(0x01040201L), -U32(0x00000202L),U32(0x00000203L),U32(0x00040202L),U32(0x00040203L), -U32(0x01000202L),U32(0x01000203L),U32(0x01040202L),U32(0x01040203L), -U32(0x08000000L),U32(0x08000001L),U32(0x08040000L),U32(0x08040001L), -U32(0x09000000L),U32(0x09000001L),U32(0x09040000L),U32(0x09040001L), -U32(0x08000002L),U32(0x08000003L),U32(0x08040002L),U32(0x08040003L), -U32(0x09000002L),U32(0x09000003L),U32(0x09040002L),U32(0x09040003L), -U32(0x08000200L),U32(0x08000201L),U32(0x08040200L),U32(0x08040201L), -U32(0x09000200L),U32(0x09000201L),U32(0x09040200L),U32(0x09040201L), -U32(0x08000202L),U32(0x08000203L),U32(0x08040202L),U32(0x08040203L), -U32(0x09000202L),U32(0x09000203L),U32(0x09040202L),U32(0x09040203L), -], - -#for C bits (numbered as per FIPS 46) 21 23 24 26 27 28 -[ -U32(0x00000000L),U32(0x00100000L),U32(0x00000100L),U32(0x00100100L), -U32(0x00000008L),U32(0x00100008L),U32(0x00000108L),U32(0x00100108L), -U32(0x00001000L),U32(0x00101000L),U32(0x00001100L),U32(0x00101100L), -U32(0x00001008L),U32(0x00101008L),U32(0x00001108L),U32(0x00101108L), -U32(0x04000000L),U32(0x04100000L),U32(0x04000100L),U32(0x04100100L), -U32(0x04000008L),U32(0x04100008L),U32(0x04000108L),U32(0x04100108L), -U32(0x04001000L),U32(0x04101000L),U32(0x04001100L),U32(0x04101100L), -U32(0x04001008L),U32(0x04101008L),U32(0x04001108L),U32(0x04101108L), -U32(0x00020000L),U32(0x00120000L),U32(0x00020100L),U32(0x00120100L), -U32(0x00020008L),U32(0x00120008L),U32(0x00020108L),U32(0x00120108L), -U32(0x00021000L),U32(0x00121000L),U32(0x00021100L),U32(0x00121100L), -U32(0x00021008L),U32(0x00121008L),U32(0x00021108L),U32(0x00121108L), -U32(0x04020000L),U32(0x04120000L),U32(0x04020100L),U32(0x04120100L), -U32(0x04020008L),U32(0x04120008L),U32(0x04020108L),U32(0x04120108L), -U32(0x04021000L),U32(0x04121000L),U32(0x04021100L),U32(0x04121100L), -U32(0x04021008L),U32(0x04121008L),U32(0x04021108L),U32(0x04121108L), -], - -#for D bits (numbered as per FIPS 46) 1 2 3 4 5 6 -[ -U32(0x00000000L),U32(0x10000000L),U32(0x00010000L),U32(0x10010000L), -U32(0x00000004L),U32(0x10000004L),U32(0x00010004L),U32(0x10010004L), -U32(0x20000000L),U32(0x30000000L),U32(0x20010000L),U32(0x30010000L), -U32(0x20000004L),U32(0x30000004L),U32(0x20010004L),U32(0x30010004L), -U32(0x00100000L),U32(0x10100000L),U32(0x00110000L),U32(0x10110000L), -U32(0x00100004L),U32(0x10100004L),U32(0x00110004L),U32(0x10110004L), -U32(0x20100000L),U32(0x30100000L),U32(0x20110000L),U32(0x30110000L), -U32(0x20100004L),U32(0x30100004L),U32(0x20110004L),U32(0x30110004L), -U32(0x00001000L),U32(0x10001000L),U32(0x00011000L),U32(0x10011000L), -U32(0x00001004L),U32(0x10001004L),U32(0x00011004L),U32(0x10011004L), -U32(0x20001000L),U32(0x30001000L),U32(0x20011000L),U32(0x30011000L), -U32(0x20001004L),U32(0x30001004L),U32(0x20011004L),U32(0x30011004L), -U32(0x00101000L),U32(0x10101000L),U32(0x00111000L),U32(0x10111000L), -U32(0x00101004L),U32(0x10101004L),U32(0x00111004L),U32(0x10111004L), -U32(0x20101000L),U32(0x30101000L),U32(0x20111000L),U32(0x30111000L), -U32(0x20101004L),U32(0x30101004L),U32(0x20111004L),U32(0x30111004L), -], - -#for D bits (numbered as per FIPS 46) 8 9 11 12 13 14 -[ -U32(0x00000000L),U32(0x08000000L),U32(0x00000008L),U32(0x08000008L), -U32(0x00000400L),U32(0x08000400L),U32(0x00000408L),U32(0x08000408L), -U32(0x00020000L),U32(0x08020000L),U32(0x00020008L),U32(0x08020008L), -U32(0x00020400L),U32(0x08020400L),U32(0x00020408L),U32(0x08020408L), -U32(0x00000001L),U32(0x08000001L),U32(0x00000009L),U32(0x08000009L), -U32(0x00000401L),U32(0x08000401L),U32(0x00000409L),U32(0x08000409L), -U32(0x00020001L),U32(0x08020001L),U32(0x00020009L),U32(0x08020009L), -U32(0x00020401L),U32(0x08020401L),U32(0x00020409L),U32(0x08020409L), -U32(0x02000000L),U32(0x0A000000L),U32(0x02000008L),U32(0x0A000008L), -U32(0x02000400L),U32(0x0A000400L),U32(0x02000408L),U32(0x0A000408L), -U32(0x02020000L),U32(0x0A020000L),U32(0x02020008L),U32(0x0A020008L), -U32(0x02020400L),U32(0x0A020400L),U32(0x02020408L),U32(0x0A020408L), -U32(0x02000001L),U32(0x0A000001L),U32(0x02000009L),U32(0x0A000009L), -U32(0x02000401L),U32(0x0A000401L),U32(0x02000409L),U32(0x0A000409L), -U32(0x02020001L),U32(0x0A020001L),U32(0x02020009L),U32(0x0A020009L), -U32(0x02020401L),U32(0x0A020401L),U32(0x02020409L),U32(0x0A020409L), -], - -#for D bits (numbered as per FIPS 46) 16 17 18 19 20 21 -[ -U32(0x00000000L),U32(0x00000100L),U32(0x00080000L),U32(0x00080100L), -U32(0x01000000L),U32(0x01000100L),U32(0x01080000L),U32(0x01080100L), -U32(0x00000010L),U32(0x00000110L),U32(0x00080010L),U32(0x00080110L), -U32(0x01000010L),U32(0x01000110L),U32(0x01080010L),U32(0x01080110L), -U32(0x00200000L),U32(0x00200100L),U32(0x00280000L),U32(0x00280100L), -U32(0x01200000L),U32(0x01200100L),U32(0x01280000L),U32(0x01280100L), -U32(0x00200010L),U32(0x00200110L),U32(0x00280010L),U32(0x00280110L), -U32(0x01200010L),U32(0x01200110L),U32(0x01280010L),U32(0x01280110L), -U32(0x00000200L),U32(0x00000300L),U32(0x00080200L),U32(0x00080300L), -U32(0x01000200L),U32(0x01000300L),U32(0x01080200L),U32(0x01080300L), -U32(0x00000210L),U32(0x00000310L),U32(0x00080210L),U32(0x00080310L), -U32(0x01000210L),U32(0x01000310L),U32(0x01080210L),U32(0x01080310L), -U32(0x00200200L),U32(0x00200300L),U32(0x00280200L),U32(0x00280300L), -U32(0x01200200L),U32(0x01200300L),U32(0x01280200L),U32(0x01280300L), -U32(0x00200210L),U32(0x00200310L),U32(0x00280210L),U32(0x00280310L), -U32(0x01200210L),U32(0x01200310L),U32(0x01280210L),U32(0x01280310L), -], - -#for D bits (numbered as per FIPS 46) 22 23 24 25 27 28 -[ -U32(0x00000000L),U32(0x04000000L),U32(0x00040000L),U32(0x04040000L), -U32(0x00000002L),U32(0x04000002L),U32(0x00040002L),U32(0x04040002L), -U32(0x00002000L),U32(0x04002000L),U32(0x00042000L),U32(0x04042000L), -U32(0x00002002L),U32(0x04002002L),U32(0x00042002L),U32(0x04042002L), -U32(0x00000020L),U32(0x04000020L),U32(0x00040020L),U32(0x04040020L), -U32(0x00000022L),U32(0x04000022L),U32(0x00040022L),U32(0x04040022L), -U32(0x00002020L),U32(0x04002020L),U32(0x00042020L),U32(0x04042020L), -U32(0x00002022L),U32(0x04002022L),U32(0x00042022L),U32(0x04042022L), -U32(0x00000800L),U32(0x04000800L),U32(0x00040800L),U32(0x04040800L), -U32(0x00000802L),U32(0x04000802L),U32(0x00040802L),U32(0x04040802L), -U32(0x00002800L),U32(0x04002800L),U32(0x00042800L),U32(0x04042800L), -U32(0x00002802L),U32(0x04002802L),U32(0x00042802L),U32(0x04042802L), -U32(0x00000820L),U32(0x04000820L),U32(0x00040820L),U32(0x04040820L), -U32(0x00000822L),U32(0x04000822L),U32(0x00040822L),U32(0x04040822L), -U32(0x00002820L),U32(0x04002820L),U32(0x00042820L),U32(0x04042820L), -U32(0x00002822L),U32(0x04002822L),U32(0x00042822L),U32(0x04042822L), -] - -] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/ntlm.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/ntlm.py deleted file mode 100644 index 79d6dfe..0000000 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/ntlm.py +++ /dev/null @@ -1,466 +0,0 @@ -# This library 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 library 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 library. If not, see or . - -import struct -import base64 -import string -import des -import hashlib -import hmac -import random -from socket import gethostname - -NTLM_NegotiateUnicode = 0x00000001 -NTLM_NegotiateOEM = 0x00000002 -NTLM_RequestTarget = 0x00000004 -NTLM_Unknown9 = 0x00000008 -NTLM_NegotiateSign = 0x00000010 -NTLM_NegotiateSeal = 0x00000020 -NTLM_NegotiateDatagram = 0x00000040 -NTLM_NegotiateLanManagerKey = 0x00000080 -NTLM_Unknown8 = 0x00000100 -NTLM_NegotiateNTLM = 0x00000200 -NTLM_NegotiateNTOnly = 0x00000400 -NTLM_Anonymous = 0x00000800 -NTLM_NegotiateOemDomainSupplied = 0x00001000 -NTLM_NegotiateOemWorkstationSupplied = 0x00002000 -NTLM_Unknown6 = 0x00004000 -NTLM_NegotiateAlwaysSign = 0x00008000 -NTLM_TargetTypeDomain = 0x00010000 -NTLM_TargetTypeServer = 0x00020000 -NTLM_TargetTypeShare = 0x00040000 -NTLM_NegotiateExtendedSecurity = 0x00080000 -NTLM_NegotiateIdentify = 0x00100000 -NTLM_Unknown5 = 0x00200000 -NTLM_RequestNonNTSessionKey = 0x00400000 -NTLM_NegotiateTargetInfo = 0x00800000 -NTLM_Unknown4 = 0x01000000 -NTLM_NegotiateVersion = 0x02000000 -NTLM_Unknown3 = 0x04000000 -NTLM_Unknown2 = 0x08000000 -NTLM_Unknown1 = 0x10000000 -NTLM_Negotiate128 = 0x20000000 -NTLM_NegotiateKeyExchange = 0x40000000 -NTLM_Negotiate56 = 0x80000000 - -# we send these flags with our type 1 message -NTLM_TYPE1_FLAGS = (NTLM_NegotiateUnicode | \ - NTLM_NegotiateOEM | \ - NTLM_RequestTarget | \ - NTLM_NegotiateNTLM | \ - NTLM_NegotiateOemDomainSupplied | \ - NTLM_NegotiateOemWorkstationSupplied | \ - NTLM_NegotiateAlwaysSign | \ - NTLM_NegotiateExtendedSecurity | \ - NTLM_NegotiateVersion | \ - NTLM_Negotiate128 | \ - NTLM_Negotiate56 ) -NTLM_TYPE2_FLAGS = (NTLM_NegotiateUnicode | \ - NTLM_RequestTarget | \ - NTLM_NegotiateNTLM | \ - NTLM_NegotiateAlwaysSign | \ - NTLM_NegotiateExtendedSecurity | \ - NTLM_NegotiateTargetInfo | \ - NTLM_NegotiateVersion | \ - NTLM_Negotiate128 | \ - NTLM_Negotiate56) - -NTLM_MsvAvEOL = 0 # Indicates that this is the last AV_PAIR in the list. AvLen MUST be 0. This type of information MUST be present in the AV pair list. -NTLM_MsvAvNbComputerName = 1 # The server's NetBIOS computer name. The name MUST be in Unicode, and is not null-terminated. This type of information MUST be present in the AV_pair list. -NTLM_MsvAvNbDomainName = 2 # The server's NetBIOS domain name. The name MUST be in Unicode, and is not null-terminated. This type of information MUST be present in the AV_pair list. -NTLM_MsvAvDnsComputerName = 3 # The server's Active Directory DNS computer name. The name MUST be in Unicode, and is not null-terminated. -NTLM_MsvAvDnsDomainName = 4 # The server's Active Directory DNS domain name. The name MUST be in Unicode, and is not null-terminated. -NTLM_MsvAvDnsTreeName = 5 # The server's Active Directory (AD) DNS forest tree name. The name MUST be in Unicode, and is not null-terminated. -NTLM_MsvAvFlags = 6 # A field containing a 32-bit value indicating server or client configuration. 0x00000001: indicates to the client that the account authentication is constrained. 0x00000002: indicates that the client is providing message integrity in the MIC field (section 2.2.1.3) in the AUTHENTICATE_MESSAGE. -NTLM_MsvAvTimestamp = 7 # A FILETIME structure ([MS-DTYP] section 2.3.1) in little-endian byte order that contains the server local time.<12> -NTLM_MsAvRestrictions = 8 #A Restriction_Encoding structure (section 2.2.2.2). The Value field contains a structure representing the integrity level of the security principal, as well as a MachineID created at computer startup to identify the calling machine. <13> - - -""" -utility functions for Microsoft NTLM authentication - -References: -[MS-NLMP]: NT LAN Manager (NTLM) Authentication Protocol Specification -http://download.microsoft.com/download/a/e/6/ae6e4142-aa58-45c6-8dcf-a657e5900cd3/%5BMS-NLMP%5D.pdf - -[MS-NTHT]: NTLM Over HTTP Protocol Specification -http://download.microsoft.com/download/a/e/6/ae6e4142-aa58-45c6-8dcf-a657e5900cd3/%5BMS-NTHT%5D.pdf - -Cntlm Authentication Proxy -http://cntlm.awk.cz/ - -NTLM Authorization Proxy Server -http://sourceforge.net/projects/ntlmaps/ - -Optimized Attack for NTLM2 Session Response -http://www.blackhat.com/presentations/bh-asia-04/bh-jp-04-pdfs/bh-jp-04-seki.pdf -""" -def dump_NegotiateFlags(NegotiateFlags): - if NegotiateFlags & NTLM_NegotiateUnicode: - print "NTLM_NegotiateUnicode set" - if NegotiateFlags & NTLM_NegotiateOEM: - print "NTLM_NegotiateOEM set" - if NegotiateFlags & NTLM_RequestTarget: - print "NTLM_RequestTarget set" - if NegotiateFlags & NTLM_Unknown9: - print "NTLM_Unknown9 set" - if NegotiateFlags & NTLM_NegotiateSign: - print "NTLM_NegotiateSign set" - if NegotiateFlags & NTLM_NegotiateSeal: - print "NTLM_NegotiateSeal set" - if NegotiateFlags & NTLM_NegotiateDatagram: - print "NTLM_NegotiateDatagram set" - if NegotiateFlags & NTLM_NegotiateLanManagerKey: - print "NTLM_NegotiateLanManagerKey set" - if NegotiateFlags & NTLM_Unknown8: - print "NTLM_Unknown8 set" - if NegotiateFlags & NTLM_NegotiateNTLM: - print "NTLM_NegotiateNTLM set" - if NegotiateFlags & NTLM_NegotiateNTOnly: - print "NTLM_NegotiateNTOnly set" - if NegotiateFlags & NTLM_Anonymous: - print "NTLM_Anonymous set" - if NegotiateFlags & NTLM_NegotiateOemDomainSupplied: - print "NTLM_NegotiateOemDomainSupplied set" - if NegotiateFlags & NTLM_NegotiateOemWorkstationSupplied: - print "NTLM_NegotiateOemWorkstationSupplied set" - if NegotiateFlags & NTLM_Unknown6: - print "NTLM_Unknown6 set" - if NegotiateFlags & NTLM_NegotiateAlwaysSign: - print "NTLM_NegotiateAlwaysSign set" - if NegotiateFlags & NTLM_TargetTypeDomain: - print "NTLM_TargetTypeDomain set" - if NegotiateFlags & NTLM_TargetTypeServer: - print "NTLM_TargetTypeServer set" - if NegotiateFlags & NTLM_TargetTypeShare: - print "NTLM_TargetTypeShare set" - if NegotiateFlags & NTLM_NegotiateExtendedSecurity: - print "NTLM_NegotiateExtendedSecurity set" - if NegotiateFlags & NTLM_NegotiateIdentify: - print "NTLM_NegotiateIdentify set" - if NegotiateFlags & NTLM_Unknown5: - print "NTLM_Unknown5 set" - if NegotiateFlags & NTLM_RequestNonNTSessionKey: - print "NTLM_RequestNonNTSessionKey set" - if NegotiateFlags & NTLM_NegotiateTargetInfo: - print "NTLM_NegotiateTargetInfo set" - if NegotiateFlags & NTLM_Unknown4: - print "NTLM_Unknown4 set" - if NegotiateFlags & NTLM_NegotiateVersion: - print "NTLM_NegotiateVersion set" - if NegotiateFlags & NTLM_Unknown3: - print "NTLM_Unknown3 set" - if NegotiateFlags & NTLM_Unknown2: - print "NTLM_Unknown2 set" - if NegotiateFlags & NTLM_Unknown1: - print "NTLM_Unknown1 set" - if NegotiateFlags & NTLM_Negotiate128: - print "NTLM_Negotiate128 set" - if NegotiateFlags & NTLM_NegotiateKeyExchange: - print "NTLM_NegotiateKeyExchange set" - if NegotiateFlags & NTLM_Negotiate56: - print "NTLM_Negotiate56 set" - -def create_NTLM_NEGOTIATE_MESSAGE(user, type1_flags=NTLM_TYPE1_FLAGS): - BODY_LENGTH = 40 - Payload_start = BODY_LENGTH # in bytes - protocol = 'NTLMSSP\0' #name - - type = struct.pack(' 40: - TargetInfoLen = struct.unpack(" time.time(): + self.print_skip() + return + + self.upgrade_packages() + + def install_missing(self): + """ + Installs all packages that were listed in the list of + `installed_packages` from Package Control.sublime-settings but were not + found on the filesystem and passed as `found_packages`. + """ + + if not self.missing_packages or not self.should_install_missing: + return + + console_write(u'Installing %s missing packages' % len(self.missing_packages), True) + for package in self.missing_packages: + if self.installer.manager.install_package(package): + console_write(u'Installed missing package %s' % package, True) + + def print_skip(self): + """ + Prints a notice in the console if the automatic upgrade is skipped + due to already having been run in the last `auto_upgrade_frequency` + hours. + """ + + last_run = datetime.datetime.fromtimestamp(self.last_run) + next_run = datetime.datetime.fromtimestamp(self.next_run) + date_format = '%Y-%m-%d %H:%M:%S' + message_string = u'Skipping automatic upgrade, last run at %s, next run at %s or after' % ( + last_run.strftime(date_format), next_run.strftime(date_format)) + console_write(message_string, True) + + def upgrade_packages(self): + """ + Upgrades all packages that are not currently upgraded to the lastest + version. Also renames any installed packages to their new names. + """ + + if not self.auto_upgrade: + return + + self.package_renamer.rename_packages(self.installer) + + package_list = self.installer.make_package_list(['install', + 'reinstall', 'downgrade', 'overwrite', 'none'], + ignore_packages=self.auto_upgrade_ignore) + + # If Package Control is being upgraded, just do that and restart + for package in package_list: + if package[0] != 'Package Control': + continue + + def reset_last_run(): + # Re-save the last run time so it runs again after PC has + # been updated + self.save_last_run(self.last_run) + sublime.set_timeout(reset_last_run, 1) + package_list = [package] + break + + if not package_list: + console_write(u'No updated packages', True) + return + + console_write(u'Installing %s upgrades' % len(package_list), True) + + disabled_packages = [] + + def do_upgrades(): + # Wait so that the ignored packages can be "unloaded" + time.sleep(0.5) + + # We use a function to generate the on-complete lambda because if + # we don't, the lambda will bind to info at the current scope, and + # thus use the last value of info from the loop + def make_on_complete(name): + return lambda: self.installer.reenable_package(name) + + for info in package_list: + if info[0] in disabled_packages: + on_complete = make_on_complete(info[0]) + else: + on_complete = None + + self.installer.manager.install_package(info[0]) + + version = re.sub('^.*?(v[\d\.]+).*?$', '\\1', info[2]) + if version == info[2] and version.find('pull with') != -1: + vcs = re.sub('^pull with (\w+).*?$', '\\1', version) + version = 'latest %s commit' % vcs + message_string = u'Upgraded %s to %s' % (info[0], version) + console_write(message_string, True) + if on_complete: + sublime.set_timeout(on_complete, 1) + + # Disabling a package means changing settings, which can only be done + # in the main thread. We then create a new background thread so that + # the upgrade process does not block the UI. + def disable_packages(): + disabled_packages.extend(self.installer.disable_packages([info[0] for info in package_list])) + threading.Thread(target=do_upgrades).start() + sublime.set_timeout(disable_packages, 1) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/ca_certs.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/ca_certs.py new file mode 100644 index 0000000..d29d2e0 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/ca_certs.py @@ -0,0 +1,378 @@ +import hashlib +import os +import re +import time +import sys + +from .cmd import Cli +from .console_write import console_write +from .open_compat import open_compat, read_compat + + +# Have somewhere to store the CA bundle, even when not running in Sublime Text +try: + import sublime + ca_bundle_dir = None +except (ImportError): + ca_bundle_dir = os.path.join(os.path.expanduser('~'), '.package_control') + if not os.path.exists(ca_bundle_dir): + os.mkdir(ca_bundle_dir) + + +def find_root_ca_cert(settings, domain): + runner = OpensslCli(settings.get('openssl_binary'), settings.get('debug')) + binary = runner.retrieve_binary() + + args = [binary, 's_client', '-showcerts', '-connect', domain + ':443'] + result = runner.execute(args, os.path.dirname(binary)) + + certs = [] + temp = [] + + in_block = False + for line in result.splitlines(): + if line.find('BEGIN CERTIFICATE') != -1: + in_block = True + if in_block: + temp.append(line) + if line.find('END CERTIFICATE') != -1: + in_block = False + certs.append(u"\n".join(temp)) + temp = [] + + # Remove the cert for the domain itself, just leaving the + # chain cert and the CA cert + certs.pop(0) + + # Look for the "parent" root CA cert + subject = openssl_get_cert_subject(settings, certs[-1]) + issuer = openssl_get_cert_issuer(settings, certs[-1]) + + cert = get_ca_cert_by_subject(settings, issuer) + cert_hash = hashlib.md5(cert.encode('utf-8')).hexdigest() + + return [cert, cert_hash] + + + +def get_system_ca_bundle_path(settings): + """ + Get the filesystem path to the system CA bundle. On Linux it looks in a + number of predefined places, however on OS X it has to be programatically + exported from the SystemRootCertificates.keychain. Windows does not ship + with a CA bundle, but also we use WinINet on Windows, so we don't need to + worry about CA certs. + + :param settings: + A dict to look in for `debug` and `openssl_binary` keys + + :return: + The full filesystem path to the .ca-bundle file, or False on error + """ + + # If the sublime module is available, we bind this value at run time + # since the sublime.packages_path() is not available at import time + global ca_bundle_dir + + platform = sys.platform + debug = settings.get('debug') + + ca_path = False + + if platform == 'win32': + console_write(u"Unable to get system CA cert path since Windows does not ship with them", True) + return False + + # OS X + if platform == 'darwin': + if not ca_bundle_dir: + ca_bundle_dir = os.path.join(sublime.packages_path(), 'User') + ca_path = os.path.join(ca_bundle_dir, 'Package Control.system-ca-bundle') + + exists = os.path.exists(ca_path) + # The bundle is old if it is a week or more out of date + is_old = exists and os.stat(ca_path).st_mtime < time.time() - 604800 + + if not exists or is_old: + if debug: + console_write(u"Generating new CA bundle from system keychain", True) + _osx_create_ca_bundle(settings, ca_path) + if debug: + console_write(u"Finished generating new CA bundle at %s" % ca_path, True) + elif debug: + console_write(u"Found previously exported CA bundle at %s" % ca_path, True) + + # Linux + else: + # Common CA cert paths + paths = [ + '/usr/lib/ssl/certs/ca-certificates.crt', + '/etc/ssl/certs/ca-certificates.crt', + '/etc/pki/tls/certs/ca-bundle.crt', + '/etc/ssl/ca-bundle.pem' + ] + for path in paths: + if os.path.exists(path): + ca_path = path + break + + if debug and ca_path: + console_write(u"Found system CA bundle at %s" % ca_path, True) + + return ca_path + + +def get_ca_cert_by_subject(settings, subject): + bundle_path = get_system_ca_bundle_path(settings) + + with open_compat(bundle_path, 'r') as f: + contents = read_compat(f) + + temp = [] + + in_block = False + for line in contents.splitlines(): + if line.find('BEGIN CERTIFICATE') != -1: + in_block = True + + if in_block: + temp.append(line) + + if line.find('END CERTIFICATE') != -1: + in_block = False + cert = u"\n".join(temp) + temp = [] + + if openssl_get_cert_subject(settings, cert) == subject: + return cert + + return False + + +def openssl_get_cert_issuer(settings, cert): + """ + Uses the openssl command line client to extract the issuer of an x509 + certificate. + + :param settings: + A dict to look in for `debug` and `openssl_binary` keys + + :param cert: + A string containing the PEM-encoded x509 certificate to extract the + issuer from + + :return: + The cert issuer + """ + + runner = OpensslCli(settings.get('openssl_binary'), settings.get('debug')) + binary = runner.retrieve_binary() + args = [binary, 'x509', '-noout', '-issuer'] + output = runner.execute(args, os.path.dirname(binary), cert) + return re.sub('^issuer=\s*', '', output) + + +def openssl_get_cert_name(settings, cert): + """ + Uses the openssl command line client to extract the name of an x509 + certificate. If the commonName is set, that is used, otherwise the first + organizationalUnitName is used. This mirrors what OS X uses for storing + trust preferences. + + :param settings: + A dict to look in for `debug` and `openssl_binary` keys + + :param cert: + A string containing the PEM-encoded x509 certificate to extract the + name from + + :return: + The cert subject name, which is the commonName (if available), or the + first organizationalUnitName + """ + + runner = OpensslCli(settings.get('openssl_binary'), settings.get('debug')) + + binary = runner.retrieve_binary() + + args = [binary, 'x509', '-noout', '-subject', '-nameopt', + 'sep_multiline,lname,utf8'] + result = runner.execute(args, os.path.dirname(binary), cert) + + # First look for the common name + cn = None + # If there is no common name for the cert, the trust prefs use the first + # orginizational unit name + first_ou = None + + for line in result.splitlines(): + match = re.match('^\s+commonName=(.*)$', line) + if match: + cn = match.group(1) + break + match = re.match('^\s+organizationalUnitName=(.*)$', line) + if first_ou is None and match: + first_ou = match.group(1) + continue + + # This is the name of the cert that would be used in the trust prefs + return cn or first_ou + + +def openssl_get_cert_subject(settings, cert): + """ + Uses the openssl command line client to extract the subject of an x509 + certificate. + + :param settings: + A dict to look in for `debug` and `openssl_binary` keys + + :param cert: + A string containing the PEM-encoded x509 certificate to extract the + subject from + + :return: + The cert subject + """ + + runner = OpensslCli(settings.get('openssl_binary'), settings.get('debug')) + binary = runner.retrieve_binary() + args = [binary, 'x509', '-noout', '-subject'] + output = runner.execute(args, os.path.dirname(binary), cert) + return re.sub('^subject=\s*', '', output) + + +def _osx_create_ca_bundle(settings, destination): + """ + Uses the OS X `security` command line tool to export the system's list of + CA certs from /System/Library/Keychains/SystemRootCertificates.keychain. + Checks the cert names against the trust preferences, ensuring that + distrusted certs are not exported. + + :param settings: + A dict to look in for `debug` and `openssl_binary` keys + + :param destination: + The full filesystem path to the destination .ca-bundle file + """ + + distrusted_certs = _osx_get_distrusted_certs(settings) + + # Export the root certs + args = ['/usr/bin/security', 'export', '-k', + '/System/Library/Keychains/SystemRootCertificates.keychain', '-t', + 'certs', '-p'] + result = Cli(None, settings.get('debug')).execute(args, '/usr/bin') + + certs = [] + temp = [] + + in_block = False + for line in result.splitlines(): + if line.find('BEGIN CERTIFICATE') != -1: + in_block = True + + if in_block: + temp.append(line) + + if line.find('END CERTIFICATE') != -1: + in_block = False + cert = u"\n".join(temp) + temp = [] + + if distrusted_certs: + # If it is a distrusted cert, we move on to the next + cert_name = openssl_get_cert_name(settings, cert) + if cert_name in distrusted_certs: + if settings.get('debug'): + console_write(u'Skipping root certficate %s because it is distrusted' % cert_name, True) + continue + + certs.append(cert) + + with open_compat(destination, 'w') as f: + f.write(u"\n".join(certs)) + + +def _osx_get_distrusted_certs(settings): + """ + Uses the OS X `security` binary to get a list of admin trust settings, + which is what is set when a user changes the trust setting on a root + certificate. By looking at the SSL policy, we can properly exclude + distrusted certs from out export. + + Tested on OS X 10.6 and 10.8 + + :param settings: + A dict to look in for `debug` key + + :return: + A list of CA cert names, where the name is the commonName (if + available), or the first organizationalUnitName + """ + + args = ['/usr/bin/security', 'dump-trust-settings', '-d'] + result = Cli(None, settings.get('debug')).execute(args, '/usr/bin') + + distrusted_certs = [] + cert_name = None + ssl_policy = False + for line in result.splitlines(): + if line == '': + continue + + # Reset for each cert + match = re.match('Cert\s+\d+:\s+(.*)$', line) + if match: + cert_name = match.group(1) + continue + + # Reset for each trust setting + if re.match('^\s+Trust\s+Setting\s+\d+:', line): + ssl_policy = False + continue + + # We are only interested in SSL policies + if re.match('^\s+Policy\s+OID\s+:\s+SSL', line): + ssl_policy = True + continue + + distrusted = re.match('^\s+Result\s+Type\s+:\s+kSecTrustSettingsResultDeny', line) + if ssl_policy and distrusted and cert_name not in distrusted_certs: + if settings.get('debug'): + console_write(u'Found SSL distrust setting for root certificate %s' % cert_name, True) + distrusted_certs.append(cert_name) + + return distrusted_certs + + +class OpensslCli(Cli): + + cli_name = 'openssl' + + def retrieve_binary(self): + """ + Returns the path to the openssl executable + + :return: The string path to the executable or False on error + """ + + name = 'openssl' + if os.name == 'nt': + name += '.exe' + + binary = self.find_binary(name) + if binary and os.path.isdir(binary): + full_path = os.path.join(binary, name) + if os.path.exists(full_path): + binary = full_path + + if not binary: + show_error((u'Unable to find %s. Please set the openssl_binary ' + + u'setting by accessing the Preferences > Package Settings > ' + + u'Package Control > Settings \u2013 User menu entry. The ' + + u'Settings \u2013 Default entry can be used for reference, ' + + u'but changes to that will be overwritten upon next upgrade.') % name) + return False + + return binary diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/cache.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/cache.py new file mode 100644 index 0000000..015744c --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/cache.py @@ -0,0 +1,173 @@ +import time + + +# A cache of channel and repository info to allow users to install multiple +# packages without having to wait for the metadata to be downloaded more +# than once. The keys are managed locally by the utilizing code. +_channel_repository_cache = {} + + +def clear_cache(): + global _channel_repository_cache + _channel_repository_cache = {} + + +def get_cache(key, default=None): + """ + Gets an in-memory cache value + + :param key: + The string key + + :param default: + The value to return if the key has not been set, or the ttl expired + + :return: + The cached value, or default + """ + + struct = _channel_repository_cache.get(key, {}) + expires = struct.get('expires') + if expires and expires > time.time(): + return struct.get('data') + return default + + +def merge_cache_over_settings(destination, setting, key_prefix): + """ + Take the cached value of `key` and put it into the key `setting` of + the destination.settings dict. Merge the values by overlaying the + cached setting over the existing info. + + :param destination: + An object that has a `.settings` attribute that is a dict + + :param setting: + The dict key to use when pushing the value into the settings dict + + :param key_prefix: + The string to prefix to `setting` to make the cache key + """ + + existing = destination.settings.get(setting, {}) + value = get_cache(key_prefix + '.' + setting, {}) + if value: + existing.update(value) + destination.settings[setting] = existing + + +def merge_cache_under_settings(destination, setting, key_prefix, list_=False): + """ + Take the cached value of `key` and put it into the key `setting` of + the destination.settings dict. Merge the values by overlaying the + existing setting value over the cached info. + + :param destination: + An object that has a `.settings` attribute that is a dict + + :param setting: + The dict key to use when pushing the value into the settings dict + + :param key_prefix: + The string to prefix to `setting` to make the cache key + + :param list_: + If a list should be used instead of a dict + """ + + default = {} if not list_ else [] + existing = destination.settings.get(setting) + value = get_cache(key_prefix + '.' + setting, default) + if value: + if existing: + if list_: + # Prevent duplicate values + base = dict(zip(value, [None]*len(value))) + for val in existing: + if val in base: + continue + value.append(val) + else: + value.update(existing) + destination.settings[setting] = value + + +def set_cache(key, data, ttl=300): + """ + Sets an in-memory cache value + + :param key: + The string key + + :param data: + The data to cache + + :param ttl: + The integer number of second to cache the data for + """ + + _channel_repository_cache[key] = { + 'data': data, + 'expires': time.time() + ttl + } + + +def set_cache_over_settings(destination, setting, key_prefix, value, ttl): + """ + Take the value passed, and merge it over the current `setting`. Once + complete, take the value and set the cache `key` and destination.settings + `setting` to that value, using the `ttl` for set_cache(). + + :param destination: + An object that has a `.settings` attribute that is a dict + + :param setting: + The dict key to use when pushing the value into the settings dict + + :param key_prefix: + The string to prefix to `setting` to make the cache key + + :param value: + The value to set + + :param ttl: + The cache ttl to use + """ + + existing = destination.settings.get(setting, {}) + existing.update(value) + set_cache(key_prefix + '.' + setting, value, ttl) + destination.settings[setting] = value + + +def set_cache_under_settings(destination, setting, key_prefix, value, ttl, list_=False): + """ + Take the value passed, and merge the current `setting` over it. Once + complete, take the value and set the cache `key` and destination.settings + `setting` to that value, using the `ttl` for set_cache(). + + :param destination: + An object that has a `.settings` attribute that is a dict + + :param setting: + The dict key to use when pushing the value into the settings dict + + :param key_prefix: + The string to prefix to `setting` to make the cache key + + :param value: + The value to set + + :param ttl: + The cache ttl to use + """ + + default = {} if not list_ else [] + existing = destination.settings.get(setting, default) + if value: + if list_: + value.extend(existing) + else: + value.update(existing) + set_cache(key_prefix + '.' + setting, value, ttl) + destination.settings[setting] = value diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clear_directory.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clear_directory.py new file mode 100644 index 0000000..4ddfc07 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clear_directory.py @@ -0,0 +1,37 @@ +import os + + +def clear_directory(directory, ignore_paths=None): + """ + Tries to delete all files and folders from a directory + + :param directory: + The string directory path + + :param ignore_paths: + An array of paths to ignore while deleting files + + :return: + If all of the files and folders were successfully deleted + """ + + was_exception = False + for root, dirs, files in os.walk(directory, topdown=False): + paths = [os.path.join(root, f) for f in files] + paths.extend([os.path.join(root, d) for d in dirs]) + + for path in paths: + try: + # Don't delete the metadata file, that way we have it + # when the reinstall happens, and the appropriate + # usage info can be sent back to the server + if ignore_paths and path in ignore_paths: + continue + if os.path.isdir(path): + os.rmdir(path) + else: + os.remove(path) + except (OSError, IOError): + was_exception = True + + return not was_exception diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/__init__.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/__init__.py similarity index 100% rename from EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/lib/windows/ntlm/__init__.py rename to EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/__init__.py diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/bitbucket_client.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/bitbucket_client.py new file mode 100644 index 0000000..87a4334 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/bitbucket_client.py @@ -0,0 +1,257 @@ +import re + +from ..versions import version_sort, version_filter +from .json_api_client import JSONApiClient + + +# A predefined list of readme filenames to look for +_readme_filenames = [ + 'readme', + 'readme.txt', + 'readme.md', + 'readme.mkd', + 'readme.mdown', + 'readme.markdown', + 'readme.textile', + 'readme.creole', + 'readme.rst' +] + + +class BitBucketClient(JSONApiClient): + + def download_info(self, url): + """ + Retrieve information about downloading a package + + :param url: + The URL of the repository, in one of the forms: + https://bitbucket.org/{user}/{repo} + https://bitbucket.org/{user}/{repo}/src/{branch} + https://bitbucket.org/{user}/{repo}/#tags + If the last option, grabs the info from the newest + tag that is a valid semver version. + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + None if no match, False if no commit, or a dict with the following keys: + `version` - the version number of the download + `url` - the download URL of a zip file of the package + `date` - the ISO-8601 timestamp string when the version was published + """ + + commit_info = self._commit_info(url) + if not commit_info: + return commit_info + + return { + 'version': commit_info['version'], + 'url': 'https://bitbucket.org/%s/get/%s.zip' % (commit_info['user_repo'], commit_info['commit']), + 'date': commit_info['timestamp'] + } + + def repo_info(self, url): + """ + Retrieve general information about a repository + + :param url: + The URL to the repository, in one of the forms: + https://bitbucket.org/{user}/{repo} + https://bitbucket.org/{user}/{repo}/src/{branch} + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + None if no match, or a dict with the following keys: + `name` + `description` + `homepage` - URL of the homepage + `author` + `readme` - URL of the readme + `issues` - URL of bug tracker + `donate` - URL of a donate page + """ + + user_repo, branch = self._user_repo_branch(url) + if not user_repo: + return user_repo + + api_url = self._make_api_url(user_repo) + + info = self.fetch_json(api_url) + + issues_url = u'https://bitbucket.org/%s/issues' % user_repo + + return { + 'name': info['name'], + 'description': info['description'] or 'No description provided', + 'homepage': info['website'] or url, + 'author': info['owner'], + 'donate': u'https://www.gittip.com/on/bitbucket/%s/' % info['owner'], + 'readme': self._readme_url(user_repo, branch), + 'issues': issues_url if info['has_issues'] else None + } + + def _commit_info(self, url): + """ + Fetches info about the latest commit to a repository + + :param url: + The URL to the repository, in one of the forms: + https://bitbucket.org/{user}/{repo} + https://bitbucket.org/{user}/{repo}/src/{branch} + https://bitbucket.org/{user}/{repo}/#tags + If the last option, grabs the info from the newest + tag that is a valid semver version. + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + None if no match, False if no commit, or a dict with the following keys: + `user_repo` - the user/repo name + `timestamp` - the ISO-8601 UTC timestamp string + `commit` - the branch or tag name + `version` - the extracted version number + """ + + tags_match = re.match('https?://bitbucket.org/([^/]+/[^#/]+)/?#tags$', url) + + version = None + + if tags_match: + user_repo = tags_match.group(1) + tags_url = self._make_api_url(user_repo, '/tags') + tags_list = self.fetch_json(tags_url) + tags = version_filter(tags_list.keys(), self.settings.get('install_prereleases')) + tags = version_sort(tags, reverse=True) + if not tags: + return False + commit = tags[0] + version = re.sub('^v', '', commit) + + else: + user_repo, commit = self._user_repo_branch(url) + if not user_repo: + return user_repo + + changeset_url = self._make_api_url(user_repo, '/changesets/%s' % commit) + commit_info = self.fetch_json(changeset_url) + + commit_date = commit_info['timestamp'][0:19] + + if not version: + version = re.sub('[\-: ]', '.', commit_date) + + return { + 'user_repo': user_repo, + 'timestamp': commit_date, + 'commit': commit, + 'version': version + } + + def _main_branch_name(self, user_repo): + """ + Fetch the name of the default branch + + :param user_repo: + The user/repo name to get the main branch for + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + The name of the main branch - `master` or `default` + """ + + main_branch_url = self._make_api_url(user_repo, '/main-branch') + main_branch_info = self.fetch_json(main_branch_url, True) + return main_branch_info['name'] + + def _make_api_url(self, user_repo, suffix=''): + """ + Generate a URL for the BitBucket API + + :param user_repo: + The user/repo of the repository + + :param suffix: + The extra API path info to add to the URL + + :return: + The API URL + """ + + return 'https://api.bitbucket.org/1.0/repositories/%s%s' % (user_repo, suffix) + + def _readme_url(self, user_repo, branch, prefer_cached=False): + """ + Parse the root directory listing for the repo and return the URL + to any file that looks like a readme + + :param user_repo: + The user/repo string + + :param branch: + The branch to fetch the readme from + + :param prefer_cached: + If a cached directory listing should be used instead of a new HTTP request + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + The URL to the readme file, or None + """ + + listing_url = self._make_api_url(user_repo, '/src/%s/' % branch) + root_dir_info = self.fetch_json(listing_url, prefer_cached) + + for entry in root_dir_info['files']: + if entry['path'].lower() in _readme_filenames: + return 'https://bitbucket.org/%s/raw/%s/%s' % (user_repo, + branch, entry['path']) + + return None + + def _user_repo_branch(self, url): + """ + Extract the username/repo and branch name from the URL + + :param url: + The URL to extract the info from, in one of the forms: + https://bitbucket.org/{user}/{repo} + https://bitbucket.org/{user}/{repo}/src/{branch} + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + A tuple of (user/repo, branch name) or (None, None) if not matching + """ + + repo_match = re.match('https?://bitbucket.org/([^/]+/[^/]+)/?$', url) + branch_match = re.match('https?://bitbucket.org/([^/]+/[^/]+)/src/([^/]+)/?$', url) + + if repo_match: + user_repo = repo_match.group(1) + branch = self._main_branch_name(user_repo) + + elif branch_match: + user_repo = branch_match.group(1) + branch = branch_match.group(2) + + else: + return (None, None) + + return (user_repo, branch) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/client_exception.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/client_exception.py new file mode 100644 index 0000000..fb8dd72 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/client_exception.py @@ -0,0 +1,5 @@ +class ClientException(Exception): + """If a client could not fetch information""" + + def __str__(self): + return self.args[0] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/github_client.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/github_client.py new file mode 100644 index 0000000..bd519f7 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/github_client.py @@ -0,0 +1,292 @@ +import re + +try: + # Python 3 + from urllib.parse import urlencode, quote +except (ImportError): + # Python 2 + from urllib import urlencode, quote + +from ..versions import version_sort, version_filter +from .json_api_client import JSONApiClient +from ..downloaders.downloader_exception import DownloaderException + + +class GitHubClient(JSONApiClient): + + def download_info(self, url): + """ + Retrieve information about downloading a package + + :param url: + The URL of the repository, in one of the forms: + https://github.com/{user}/{repo} + https://github.com/{user}/{repo}/tree/{branch} + https://github.com/{user}/{repo}/tags + If the last option, grabs the info from the newest + tag that is a valid semver version. + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + None if no match, False if no commit, or a dict with the following keys: + `version` - the version number of the download + `url` - the download URL of a zip file of the package + `date` - the ISO-8601 timestamp string when the version was published + """ + + commit_info = self._commit_info(url) + if not commit_info: + return commit_info + + return { + 'version': commit_info['version'], + # We specifically use codeload.github.com here because the download + # URLs all redirect there, and some of the downloaders don't follow + # HTTP redirect headers + 'url': 'https://codeload.github.com/%s/zip/%s' % (commit_info['user_repo'], quote(commit_info['commit'])), + 'date': commit_info['timestamp'] + } + + def repo_info(self, url): + """ + Retrieve general information about a repository + + :param url: + The URL to the repository, in one of the forms: + https://github.com/{user}/{repo} + https://github.com/{user}/{repo}/tree/{branch} + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + None if no match, or a dict with the following keys: + `name` + `description` + `homepage` - URL of the homepage + `author` + `readme` - URL of the readme + `issues` - URL of bug tracker + `donate` - URL of a donate page + """ + + user_repo, branch = self._user_repo_branch(url) + if not user_repo: + return user_repo + + api_url = self._make_api_url(user_repo) + + info = self.fetch_json(api_url) + + output = self._extract_repo_info(info) + output['readme'] = None + + readme_info = self._readme_info(user_repo, branch) + if not readme_info: + return output + + output['readme'] = 'https://raw.github.com/%s/%s/%s' % (user_repo, + branch, readme_info['path']) + return output + + def user_info(self, url): + """ + Retrieve general information about all repositories that are + part of a user/organization. + + :param url: + The URL to the user/organization, in the following form: + https://github.com/{user} + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + None if no match, or am list of dicts with the following keys: + `name` + `description` + `homepage` - URL of the homepage + `author` + `readme` - URL of the readme + `issues` - URL of bug tracker + `donate` - URL of a donate page + """ + + user_match = re.match('https?://github.com/([^/]+)/?$', url) + if user_match == None: + return None + + user = user_match.group(1) + api_url = self._make_api_url(user) + + repos_info = self.fetch_json(api_url) + + output = [] + for info in repos_info: + output.append(self._extract_repo_info(info)) + return output + + def _commit_info(self, url): + """ + Fetches info about the latest commit to a repository + + :param url: + The URL to the repository, in one of the forms: + https://github.com/{user}/{repo} + https://github.com/{user}/{repo}/tree/{branch} + https://github.com/{user}/{repo}/tags + If the last option, grabs the info from the newest + tag that is a valid semver version. + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + None if no match, False is no commit, or a dict with the following keys: + `user_repo` - the user/repo name + `timestamp` - the ISO-8601 UTC timestamp string + `commit` - the branch or tag name + `version` - the extracted version number + """ + + tags_match = re.match('https?://github.com/([^/]+/[^/]+)/tags/?$', url) + + version = None + + if tags_match: + user_repo = tags_match.group(1) + tags_url = self._make_api_url(user_repo, '/tags') + tags_list = self.fetch_json(tags_url) + tags = [tag['name'] for tag in tags_list] + tags = version_filter(tags, self.settings.get('install_prereleases')) + tags = version_sort(tags, reverse=True) + if not tags: + return False + commit = tags[0] + version = re.sub('^v', '', commit) + + else: + user_repo, commit = self._user_repo_branch(url) + if not user_repo: + return user_repo + + query_string = urlencode({'sha': commit, 'per_page': 1}) + commit_url = self._make_api_url(user_repo, '/commits?%s' % query_string) + commit_info = self.fetch_json(commit_url) + + commit_date = commit_info[0]['commit']['committer']['date'][0:19].replace('T', ' ') + + if not version: + version = re.sub('[\-: ]', '.', commit_date) + + return { + 'user_repo': user_repo, + 'timestamp': commit_date, + 'commit': commit, + 'version': version + } + + def _extract_repo_info(self, result): + """ + Extracts information about a repository from the API result + + :param result: + A dict representing the data returned from the GitHub API + + :return: + A dict with the following keys: + `name` + `description` + `homepage` - URL of the homepage + `author` + `issues` - URL of bug tracker + `donate` - URL of a donate page + """ + + issues_url = u'https://github.com/%s/%s/issues' % (result['owner']['login'], result['name']) + + return { + 'name': result['name'], + 'description': result['description'] or 'No description provided', + 'homepage': result['homepage'] or result['html_url'], + 'author': result['owner']['login'], + 'issues': issues_url if result['has_issues'] else None, + 'donate': u'https://www.gittip.com/on/github/%s/' % result['owner']['login'] + } + + def _make_api_url(self, user_repo, suffix=''): + """ + Generate a URL for the BitBucket API + + :param user_repo: + The user/repo of the repository + + :param suffix: + The extra API path info to add to the URL + + :return: + The API URL + """ + + return 'https://api.github.com/repos/%s%s' % (user_repo, suffix) + + def _readme_info(self, user_repo, branch, prefer_cached=False): + """ + Fetches the raw GitHub API information about a readme + + :param user_repo: + The user/repo of the repository + + :param branch: + The branch to pull the readme from + + :param prefer_cached: + If a cached version of the info should be returned instead of making a new HTTP request + + :raises: + DownloaderException: when there is an error downloading + ClientException: when there is an error parsing the response + + :return: + A dict containing all of the info from the GitHub API, or None if no readme exists + """ + + query_string = urlencode({'ref': branch}) + readme_url = self._make_api_url(user_repo, '/readme?%s' % query_string) + try: + return self.fetch_json(readme_url, prefer_cached) + except (DownloaderException) as e: + if str(e).find('HTTP error 404') != -1: + return None + raise + + def _user_repo_branch(self, url): + """ + Extract the username/repo and branch name from the URL + + :param url: + The URL to extract the info from, in one of the forms: + https://github.com/{user}/{repo} + https://github.com/{user}/{repo}/tree/{branch} + + :return: + A tuple of (user/repo, branch name) or (None, None) if no match + """ + + branch = 'master' + branch_match = re.match('https?://github.com/[^/]+/[^/]+/tree/([^/]+)/?$', url) + if branch_match != None: + branch = branch_match.group(1) + + repo_match = re.match('https?://github.com/([^/]+/[^/]+)($|/.*$)', url) + if repo_match == None: + return (None, None) + + user_repo = repo_match.group(1) + return (user_repo, branch) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/json_api_client.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/json_api_client.py new file mode 100644 index 0000000..a847302 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/json_api_client.py @@ -0,0 +1,64 @@ +import json + +try: + # Python 3 + from urllib.parse import urlencode, urlparse +except (ImportError): + # Python 2 + from urllib import urlencode + from urlparse import urlparse + +from .client_exception import ClientException +from ..download_manager import downloader + + +class JSONApiClient(): + def __init__(self, settings): + self.settings = settings + + def fetch(self, url, prefer_cached=False): + """ + Retrieves the contents of a URL + + :param url: + The URL to download the content from + + :param prefer_cached: + If a cached copy of the content is preferred + + :return: The bytes/string + """ + + # If there are extra params for the domain name, add them + extra_params = self.settings.get('query_string_params') + domain_name = urlparse(url).netloc + if extra_params and domain_name in extra_params: + params = urlencode(extra_params[domain_name]) + joiner = '?%s' if url.find('?') == -1 else '&%s' + url += joiner % params + + with downloader(url, self.settings) as manager: + content = manager.fetch(url, 'Error downloading repository.', + prefer_cached) + return content + + def fetch_json(self, url, prefer_cached=False): + """ + Retrieves and parses the JSON from a URL + + :param url: + The URL to download the JSON from + + :param prefer_cached: + If a cached copy of the JSON is preferred + + :return: A dict or list from the JSON + """ + + repository_json = self.fetch(url, prefer_cached) + + try: + return json.loads(repository_json.decode('utf-8')) + except (ValueError): + error_string = u'Error parsing JSON from URL %s.' % url + raise ClientException(error_string) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/readme_client.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/readme_client.py new file mode 100644 index 0000000..47e2a7b --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/clients/readme_client.py @@ -0,0 +1,83 @@ +import re +import os +import base64 + +try: + # Python 3 + from urllib.parse import urlencode +except (ImportError): + # Python 2 + from urllib import urlencode + +from .json_api_client import JSONApiClient +from ..downloaders.downloader_exception import DownloaderException + + +# Used to map file extensions to formats +_readme_formats = { + '.md': 'markdown', + '.mkd': 'markdown', + '.mdown': 'markdown', + '.markdown': 'markdown', + '.textile': 'textile', + '.creole': 'creole', + '.rst': 'rst' +} + + +class ReadmeClient(JSONApiClient): + + def readme_info(self, url): + """ + Retrieve the readme and info about it + + :param url: + The URL of the readme file + + :raises: + DownloaderException: if there is an error downloading the readme + ClientException: if there is an error parsing the response + + :return: + A dict with the following keys: + `filename` + `format` - `markdown`, `textile`, `creole`, `rst` or `txt` + `contents` - contents of the readme as str/unicode + """ + + contents = None + + # Try to grab the contents of a GitHub-based readme by grabbing the cached + # content of the readme API call + github_match = re.match('https://raw.github.com/([^/]+/[^/]+)/([^/]+)/readme(\.(md|mkd|mdown|markdown|textile|creole|rst|txt))?$', url, re.I) + if github_match: + user_repo = github_match.group(1) + branch = github_match.group(2) + + query_string = urlencode({'ref': branch}) + readme_json_url = 'https://api.github.com/repos/%s/readme?%s' % (user_repo, query_string) + try: + info = self.fetch_json(readme_json_url, prefer_cached=True) + contents = base64.b64decode(info['content']) + except (ValueError) as e: + pass + + if not contents: + contents = self.fetch(url) + + basename, ext = os.path.splitext(url) + format = 'txt' + ext = ext.lower() + if ext in _readme_formats: + format = _readme_formats[ext] + + try: + contents = contents.decode('utf-8') + except (UnicodeDecodeError) as e: + contents = contents.decode('cp1252', errors='replace') + + return { + 'filename': os.path.basename(url), + 'format': format, + 'contents': contents + } diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/cmd.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/cmd.py new file mode 100644 index 0000000..0d5c999 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/cmd.py @@ -0,0 +1,167 @@ +import os +import subprocess +import re + +if os.name == 'nt': + from ctypes import windll, create_unicode_buffer + +from .console_write import console_write +from .unicode import unicode_from_os +from .show_error import show_error + +try: + # Python 2 + str_cls = unicode +except (NameError): + # Python 3 + str_cls = str + + +def create_cmd(args, basename_binary=False): + """ + Takes an array of strings to be passed to subprocess.Popen and creates + a string that can be pasted into a terminal + + :param args: + The array containing the binary name/path and all arguments + + :param basename_binary: + If only the basename of the binary should be disabled instead of the full path + + :return: + The command string + """ + + if basename_binary: + args[0] = os.path.basename(args[0]) + + if os.name == 'nt': + return subprocess.list2cmdline(args) + else: + escaped_args = [] + for arg in args: + if re.search('^[a-zA-Z0-9/_^\\-\\.:=]+$', arg) == None: + arg = u"'" + arg.replace(u"'", u"'\\''") + u"'" + escaped_args.append(arg) + return u' '.join(escaped_args) + + +class Cli(object): + """ + Base class for running command line apps + + :param binary: + The full filesystem path to the executable for the version control + system. May be set to None to allow the code to try and find it. + """ + + cli_name = None + + def __init__(self, binary, debug): + self.binary = binary + self.debug = debug + + def execute(self, args, cwd, input=None): + """ + Creates a subprocess with the executable/args + + :param args: + A list of the executable path and all arguments to it + + :param cwd: + The directory in which to run the executable + + :param input: + The input text to send to the program + + :return: A string of the executable output + """ + + startupinfo = None + if os.name == 'nt': + startupinfo = subprocess.STARTUPINFO() + startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW + + # Make sure the cwd is ascii + try: + cwd.encode('ascii') + except UnicodeEncodeError: + buf = create_unicode_buffer(512) + if windll.kernel32.GetShortPathNameW(cwd, buf, len(buf)): + cwd = buf.value + + if self.debug: + console_write(u"Trying to execute command %s" % create_cmd(args), True) + + try: + proc = subprocess.Popen(args, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + startupinfo=startupinfo, cwd=cwd) + + if input and isinstance(input, str_cls): + input = input.encode('utf-8') + output, _ = proc.communicate(input) + output = output.decode('utf-8') + output = output.replace('\r\n', '\n').rstrip(' \n\r') + + return output + + except (OSError) as e: + cmd = create_cmd(args) + error = unicode_from_os(e) + message = u"Error executing: %s\n%s\n\nTry checking your \"%s_binary\" setting?" % (cmd, error, self.cli_name) + show_error(message) + return False + + def find_binary(self, name): + """ + Locates the executable by looking in the PATH and well-known directories + + :param name: + The string filename of the executable + + :return: The filesystem path to the executable, or None if not found + """ + + if self.binary: + if self.debug: + error_string = u"Using \"%s_binary\" from settings \"%s\"" % ( + self.cli_name, self.binary) + console_write(error_string, True) + return self.binary + + # Try the path first + for dir_ in os.environ['PATH'].split(os.pathsep): + path = os.path.join(dir_, name) + if os.path.exists(path): + if self.debug: + console_write(u"Found %s at \"%s\"" % (self.cli_name, path), True) + return path + + # This is left in for backwards compatibility and for windows + # users who may have the binary, albeit in a common dir that may + # not be part of the PATH + if os.name == 'nt': + dirs = ['C:\\Program Files\\Git\\bin', + 'C:\\Program Files (x86)\\Git\\bin', + 'C:\\Program Files\\TortoiseGit\\bin', + 'C:\\Program Files\\Mercurial', + 'C:\\Program Files (x86)\\Mercurial', + 'C:\\Program Files (x86)\\TortoiseHg', + 'C:\\Program Files\\TortoiseHg', + 'C:\\cygwin\\bin'] + else: + # ST seems to launch with a minimal set of environmental variables + # on OS X, so we add some common paths for it + dirs = ['/usr/local/git/bin', '/usr/local/bin'] + + for dir_ in dirs: + path = os.path.join(dir_, name) + if os.path.exists(path): + if self.debug: + console_write(u"Found %s at \"%s\"" % (self.cli_name, path), True) + return path + + if self.debug: + console_write(u"Could not find %s on your machine" % self.cli_name, True) + return None diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/__init__.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/__init__.py new file mode 100644 index 0000000..dde03d4 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/__init__.py @@ -0,0 +1,39 @@ +import os + +from .add_channel_command import AddChannelCommand +from .add_repository_command import AddRepositoryCommand +from .create_binary_package_command import CreateBinaryPackageCommand +from .create_package_command import CreatePackageCommand +from .disable_package_command import DisablePackageCommand +from .discover_packages_command import DiscoverPackagesCommand +from .enable_package_command import EnablePackageCommand +from .grab_certs_command import GrabCertsCommand +from .install_package_command import InstallPackageCommand +from .list_packages_command import ListPackagesCommand +from .remove_package_command import RemovePackageCommand +from .upgrade_all_packages_command import UpgradeAllPackagesCommand +from .upgrade_package_command import UpgradePackageCommand +from .package_message_command import PackageMessageCommand + + +__all__ = [ + 'AddChannelCommand', + 'AddRepositoryCommand', + 'CreateBinaryPackageCommand', + 'CreatePackageCommand', + 'DisablePackageCommand', + 'DiscoverPackagesCommand', + 'EnablePackageCommand', + 'InstallPackageCommand', + 'ListPackagesCommand', + 'RemovePackageCommand', + 'UpgradeAllPackagesCommand', + 'UpgradePackageCommand', + 'PackageMessageCommand' +] + +# Windows uses the wininet downloader, so it doesn't use the CA cert bundle +# and thus does not need the ability to grab to CA certs. Additionally, +# there is no openssl.exe on Windows. +if os.name != 'nt': + __all__.append('GrabCertsCommand') diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/add_channel_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/add_channel_command.py new file mode 100644 index 0000000..5e1b8d1 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/add_channel_command.py @@ -0,0 +1,46 @@ +import re + +import sublime +import sublime_plugin + +from ..show_error import show_error + + +class AddChannelCommand(sublime_plugin.WindowCommand): + """ + A command to add a new channel (list of repositories) to the user's machine + """ + + def run(self): + self.window.show_input_panel('Channel JSON URL', '', + self.on_done, self.on_change, self.on_cancel) + + def on_done(self, input): + """ + Input panel handler - adds the provided URL as a channel + + :param input: + A string of the URL to the new channel + """ + + input = input.strip() + + if re.match('https?://', input, re.I) == None: + show_error(u"Unable to add the channel \"%s\" since it does not appear to be served via HTTP (http:// or https://)." % input) + return + + settings = sublime.load_settings('Package Control.sublime-settings') + channels = settings.get('channels', []) + if not channels: + channels = [] + channels.append(input) + settings.set('channels', channels) + sublime.save_settings('Package Control.sublime-settings') + sublime.status_message(('Channel %s successfully ' + + 'added') % input) + + def on_change(self, input): + pass + + def on_cancel(self): + pass diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/add_repository_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/add_repository_command.py new file mode 100644 index 0000000..3d04323 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/add_repository_command.py @@ -0,0 +1,46 @@ +import re + +import sublime +import sublime_plugin + +from ..show_error import show_error + + +class AddRepositoryCommand(sublime_plugin.WindowCommand): + """ + A command to add a new repository to the user's machine + """ + + def run(self): + self.window.show_input_panel('GitHub or BitBucket Web URL, or Custom' + + ' JSON Repository URL', '', self.on_done, + self.on_change, self.on_cancel) + + def on_done(self, input): + """ + Input panel handler - adds the provided URL as a repository + + :param input: + A string of the URL to the new repository + """ + + input = input.strip() + + if re.match('https?://', input, re.I) == None: + show_error(u"Unable to add the repository \"%s\" since it does not appear to be served via HTTP (http:// or https://)." % input) + return + + settings = sublime.load_settings('Package Control.sublime-settings') + repositories = settings.get('repositories', []) + if not repositories: + repositories = [] + repositories.append(input) + settings.set('repositories', repositories) + sublime.save_settings('Package Control.sublime-settings') + sublime.status_message('Repository %s successfully added' % input) + + def on_change(self, input): + pass + + def on_cancel(self): + pass diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/create_binary_package_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/create_binary_package_command.py new file mode 100644 index 0000000..491dd1c --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/create_binary_package_command.py @@ -0,0 +1,35 @@ +import sublime_plugin + +from ..package_creator import PackageCreator + + +class CreateBinaryPackageCommand(sublime_plugin.WindowCommand, PackageCreator): + """ + Command to create a binary .sublime-package file. Binary packages in + general exclude the .py source files and instead include the .pyc files. + Actual included and excluded files are controlled by settings. + """ + + def run(self): + self.show_panel() + + def on_done(self, picked): + """ + Quick panel user selection handler - processes the user package + selection and create the package file + + :param picked: + An integer of the 0-based package name index from the presented + list. -1 means the user cancelled. + """ + + if picked == -1: + return + package_name = self.packages[picked] + package_destination = self.get_package_destination() + + if self.manager.create_package(package_name, package_destination, + binary_package=True): + self.window.run_command('open_dir', {"dir": + package_destination, "file": package_name + + '.sublime-package'}) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/create_package_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/create_package_command.py new file mode 100644 index 0000000..8b0524a --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/create_package_command.py @@ -0,0 +1,32 @@ +import sublime_plugin + +from ..package_creator import PackageCreator + + +class CreatePackageCommand(sublime_plugin.WindowCommand, PackageCreator): + """ + Command to create a regular .sublime-package file + """ + + def run(self): + self.show_panel() + + def on_done(self, picked): + """ + Quick panel user selection handler - processes the user package + selection and create the package file + + :param picked: + An integer of the 0-based package name index from the presented + list. -1 means the user cancelled. + """ + + if picked == -1: + return + package_name = self.packages[picked] + package_destination = self.get_package_destination() + + if self.manager.create_package(package_name, package_destination): + self.window.run_command('open_dir', {"dir": + package_destination, "file": package_name + + '.sublime-package'}) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/disable_package_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/disable_package_command.py new file mode 100644 index 0000000..d5ebd97 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/disable_package_command.py @@ -0,0 +1,48 @@ +import sublime +import sublime_plugin + +from ..show_error import show_error +from ..package_manager import PackageManager +from ..preferences_filename import preferences_filename + + +class DisablePackageCommand(sublime_plugin.WindowCommand): + """ + A command that adds a package to Sublime Text's ignored packages list + """ + + def run(self): + manager = PackageManager() + packages = manager.list_all_packages() + self.settings = sublime.load_settings(preferences_filename()) + ignored = self.settings.get('ignored_packages') + if not ignored: + ignored = [] + self.package_list = list(set(packages) - set(ignored)) + self.package_list.sort() + if not self.package_list: + show_error('There are no enabled packages to disable.') + return + self.window.show_quick_panel(self.package_list, self.on_done) + + def on_done(self, picked): + """ + Quick panel user selection handler - disables the selected package + + :param picked: + An integer of the 0-based package name index from the presented + list. -1 means the user cancelled. + """ + + if picked == -1: + return + package = self.package_list[picked] + ignored = self.settings.get('ignored_packages') + if not ignored: + ignored = [] + ignored.append(package) + self.settings.set('ignored_packages', ignored) + sublime.save_settings(preferences_filename()) + sublime.status_message(('Package %s successfully added to list of ' + + 'disabled packages - restarting Sublime Text may be required') % + package) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/discover_packages_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/discover_packages_command.py new file mode 100644 index 0000000..78d9812 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/discover_packages_command.py @@ -0,0 +1,11 @@ +import sublime_plugin + + +class DiscoverPackagesCommand(sublime_plugin.WindowCommand): + """ + A command that opens the community package list webpage + """ + + def run(self): + self.window.run_command('open_url', + {'url': 'http://wbond.net/sublime_packages/community'}) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/enable_package_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/enable_package_command.py new file mode 100644 index 0000000..2e5e6d1 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/enable_package_command.py @@ -0,0 +1,40 @@ +import sublime +import sublime_plugin + +from ..show_error import show_error +from ..preferences_filename import preferences_filename + + +class EnablePackageCommand(sublime_plugin.WindowCommand): + """ + A command that removes a package from Sublime Text's ignored packages list + """ + + def run(self): + self.settings = sublime.load_settings(preferences_filename()) + self.disabled_packages = self.settings.get('ignored_packages') + self.disabled_packages.sort() + if not self.disabled_packages: + show_error('There are no disabled packages to enable.') + return + self.window.show_quick_panel(self.disabled_packages, self.on_done) + + def on_done(self, picked): + """ + Quick panel user selection handler - enables the selected package + + :param picked: + An integer of the 0-based package name index from the presented + list. -1 means the user cancelled. + """ + + if picked == -1: + return + package = self.disabled_packages[picked] + ignored = self.settings.get('ignored_packages') + self.settings.set('ignored_packages', + list(set(ignored) - set([package]))) + sublime.save_settings(preferences_filename()) + sublime.status_message(('Package %s successfully removed from list ' + + 'of disabled packages - restarting Sublime Text may be required') % + package) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/existing_packages_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/existing_packages_command.py new file mode 100644 index 0000000..78615d6 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/existing_packages_command.py @@ -0,0 +1,69 @@ +import os +import re + +import sublime + +from ..package_manager import PackageManager + + +class ExistingPackagesCommand(): + """ + Allows listing installed packages and their current version + """ + + def __init__(self): + self.manager = PackageManager() + + def make_package_list(self, action=''): + """ + Returns a list of installed packages suitable for displaying in the + quick panel. + + :param action: + An action to display at the beginning of the third element of the + list returned for each package + + :return: + A list of lists, each containing three strings: + 0 - package name + 1 - package description + 2 - [action] installed version; package url + """ + + packages = self.manager.list_packages() + + if action: + action += ' ' + + package_list = [] + for package in sorted(packages, key=lambda s: s.lower()): + package_entry = [package] + metadata = self.manager.get_metadata(package) + package_dir = os.path.join(sublime.packages_path(), package) + + description = metadata.get('description') + if not description: + description = 'No description provided' + package_entry.append(description) + + version = metadata.get('version') + if not version and os.path.exists(os.path.join(package_dir, + '.git')): + installed_version = 'git repository' + elif not version and os.path.exists(os.path.join(package_dir, + '.hg')): + installed_version = 'hg repository' + else: + installed_version = 'v' + version if version else \ + 'unknown version' + + url = metadata.get('url') + if url: + url = '; ' + re.sub('^https?://', '', url) + else: + url = '' + + package_entry.append(action + installed_version + url) + package_list.append(package_entry) + + return package_list diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/grab_certs_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/grab_certs_command.py new file mode 100644 index 0000000..4eb77e0 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/grab_certs_command.py @@ -0,0 +1,109 @@ +import os +import re +import socket +import threading + +try: + # Python 3 + from urllib.parse import urlparse +except (ImportError): + # Python 2 + from urlparse import urlparse + +import sublime +import sublime_plugin + +from ..show_error import show_error +from ..open_compat import open_compat +from ..ca_certs import find_root_ca_cert +from ..thread_progress import ThreadProgress +from ..package_manager import PackageManager + + +class GrabCertsCommand(sublime_plugin.WindowCommand): + """ + A command that extracts the CA certs for a domain name, allowing a user to + fetch packages from sources other than those used by the default channel + """ + + def run(self): + panel = self.window.show_input_panel('Domain Name', 'example.com', self.on_done, + None, None) + panel.sel().add(sublime.Region(0, panel.size())) + + def on_done(self, domain): + """ + Input panel handler - grabs the CA certs for the domain name presented + + :param domain: + A string of the domain name + """ + + domain = domain.strip() + + # Make sure the user enters something + if domain == '': + show_error(u"Please enter a domain name, or press cancel") + self.run() + return + + # If the user inputs a URL, extract the domain name + if domain.find('/') != -1: + parts = urlparse(domain) + if parts.hostname: + domain = parts.hostname + + # Allow _ even though it technically isn't valid, this is really + # just to try and prevent people from typing in gibberish + if re.match('^(?:[a-zA-Z0-9]+(?:[\-_]*[a-zA-Z0-9]+)*\.)+[a-zA-Z]{2,6}$', domain, re.I) == None: + show_error(u"Unable to get the CA certs for \"%s\" since it does not appear to be a validly formed domain name" % domain) + return + + # Make sure it is a real domain + try: + socket.gethostbyname(domain) + except (socket.gaierror) as e: + error = unicode_from_os(e) + show_error(u"Error trying to lookup \"%s\":\n\n%s" % (domain, error)) + return + + manager = PackageManager() + + thread = GrabCertsThread(manager.settings, domain) + thread.start() + ThreadProgress(thread, 'Grabbing CA certs for %s' % domain, + 'CA certs for %s added to settings' % domain) + + +class GrabCertsThread(threading.Thread): + """ + A thread to run openssl so that the Sublime Text UI does not become frozen + """ + + def __init__(self, settings, domain): + self.settings = settings + self.domain = domain + threading.Thread.__init__(self) + + def run(self): + cert, cert_hash = find_root_ca_cert(self.settings, self.domain) + + certs_dir = os.path.join(sublime.packages_path(), 'User', + 'Package Control.ca-certs') + if not os.path.exists(certs_dir): + os.mkdir(certs_dir) + + cert_path = os.path.join(certs_dir, self.domain + '-ca.crt') + with open_compat(cert_path, 'w') as f: + f.write(cert) + + def save_certs(): + settings = sublime.load_settings('Package Control.sublime-settings') + certs = settings.get('certs', {}) + if not certs: + certs = {} + certs[self.domain] = [cert_hash, cert_path] + settings.set('certs', certs) + sublime.save_settings('Package Control.sublime-settings') + + sublime.set_timeout(save_certs, 10) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/install_package_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/install_package_command.py new file mode 100644 index 0000000..bbe9031 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/install_package_command.py @@ -0,0 +1,50 @@ +import threading + +import sublime +import sublime_plugin + +from ..show_error import show_error +from ..package_installer import PackageInstaller +from ..thread_progress import ThreadProgress + + +class InstallPackageCommand(sublime_plugin.WindowCommand): + """ + A command that presents the list of available packages and allows the + user to pick one to install. + """ + + def run(self): + thread = InstallPackageThread(self.window) + thread.start() + ThreadProgress(thread, 'Loading repositories', '') + + +class InstallPackageThread(threading.Thread, PackageInstaller): + """ + A thread to run the action of retrieving available packages in. Uses the + default PackageInstaller.on_done quick panel handler. + """ + + def __init__(self, window): + """ + :param window: + An instance of :class:`sublime.Window` that represents the Sublime + Text window to show the available package list in. + """ + + self.window = window + self.completion_type = 'installed' + threading.Thread.__init__(self) + PackageInstaller.__init__(self) + + def run(self): + self.package_list = self.make_package_list(['upgrade', 'downgrade', + 'reinstall', 'pull', 'none']) + + def show_quick_panel(): + if not self.package_list: + show_error('There are no packages available for installation') + return + self.window.show_quick_panel(self.package_list, self.on_done) + sublime.set_timeout(show_quick_panel, 10) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/list_packages_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/list_packages_command.py new file mode 100644 index 0000000..84c57e4 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/list_packages_command.py @@ -0,0 +1,63 @@ +import threading +import os + +import sublime +import sublime_plugin + +from ..show_error import show_error +from .existing_packages_command import ExistingPackagesCommand + + +class ListPackagesCommand(sublime_plugin.WindowCommand): + """ + A command that shows a list of all installed packages in the quick panel + """ + + def run(self): + ListPackagesThread(self.window).start() + + +class ListPackagesThread(threading.Thread, ExistingPackagesCommand): + """ + A thread to prevent the listing of existing packages from freezing the UI + """ + + def __init__(self, window): + """ + :param window: + An instance of :class:`sublime.Window` that represents the Sublime + Text window to show the list of installed packages in. + """ + + self.window = window + threading.Thread.__init__(self) + ExistingPackagesCommand.__init__(self) + + def run(self): + self.package_list = self.make_package_list() + + def show_quick_panel(): + if not self.package_list: + show_error('There are no packages to list') + return + self.window.show_quick_panel(self.package_list, self.on_done) + sublime.set_timeout(show_quick_panel, 10) + + def on_done(self, picked): + """ + Quick panel user selection handler - opens the homepage for any + selected package in the user's browser + + :param picked: + An integer of the 0-based package name index from the presented + list. -1 means the user cancelled. + """ + + if picked == -1: + return + package_name = self.package_list[picked][0] + + def open_dir(): + self.window.run_command('open_dir', + {"dir": os.path.join(sublime.packages_path(), package_name)}) + sublime.set_timeout(open_dir, 10) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/package_message_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/package_message_command.py new file mode 100644 index 0000000..6e083df --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/package_message_command.py @@ -0,0 +1,11 @@ +import sublime +import sublime_plugin + + +class PackageMessageCommand(sublime_plugin.TextCommand): + """ + A command to write a package message to the Package Control messaging buffer + """ + + def run(self, edit, string=''): + self.view.insert(edit, self.view.size(), string) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/remove_package_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/remove_package_command.py new file mode 100644 index 0000000..df0350c --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/remove_package_command.py @@ -0,0 +1,88 @@ +import threading + +import sublime +import sublime_plugin + +from ..show_error import show_error +from .existing_packages_command import ExistingPackagesCommand +from ..preferences_filename import preferences_filename +from ..thread_progress import ThreadProgress + + +class RemovePackageCommand(sublime_plugin.WindowCommand, + ExistingPackagesCommand): + """ + A command that presents a list of installed packages, allowing the user to + select one to remove + """ + + def __init__(self, window): + """ + :param window: + An instance of :class:`sublime.Window` that represents the Sublime + Text window to show the list of installed packages in. + """ + + self.window = window + ExistingPackagesCommand.__init__(self) + + def run(self): + self.package_list = self.make_package_list('remove') + if not self.package_list: + show_error('There are no packages that can be removed.') + return + self.window.show_quick_panel(self.package_list, self.on_done) + + def on_done(self, picked): + """ + Quick panel user selection handler - deletes the selected package + + :param picked: + An integer of the 0-based package name index from the presented + list. -1 means the user cancelled. + """ + + if picked == -1: + return + package = self.package_list[picked][0] + + settings = sublime.load_settings(preferences_filename()) + ignored = settings.get('ignored_packages') + if not ignored: + ignored = [] + + # Don't disable Package Control so it does not get stuck disabled + if package != 'Package Control': + if not package in ignored: + ignored.append(package) + settings.set('ignored_packages', ignored) + sublime.save_settings(preferences_filename()) + ignored.remove(package) + + thread = RemovePackageThread(self.manager, package, + ignored) + thread.start() + ThreadProgress(thread, 'Removing package %s' % package, + 'Package %s successfully removed' % package) + + +class RemovePackageThread(threading.Thread): + """ + A thread to run the remove package operation in so that the Sublime Text + UI does not become frozen + """ + + def __init__(self, manager, package, ignored): + self.manager = manager + self.package = package + self.ignored = ignored + threading.Thread.__init__(self) + + def run(self): + self.result = self.manager.remove_package(self.package) + + def unignore_package(): + settings = sublime.load_settings(preferences_filename()) + settings.set('ignored_packages', self.ignored) + sublime.save_settings(preferences_filename()) + sublime.set_timeout(unignore_package, 10) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/upgrade_all_packages_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/upgrade_all_packages_command.py new file mode 100644 index 0000000..a4a730d --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/upgrade_all_packages_command.py @@ -0,0 +1,77 @@ +import time +import threading + +import sublime +import sublime_plugin + +from ..thread_progress import ThreadProgress +from ..package_installer import PackageInstaller, PackageInstallerThread +from ..package_renamer import PackageRenamer + + +class UpgradeAllPackagesCommand(sublime_plugin.WindowCommand): + """ + A command to automatically upgrade all installed packages that are + upgradable. + """ + + def run(self): + package_renamer = PackageRenamer() + package_renamer.load_settings() + + thread = UpgradeAllPackagesThread(self.window, package_renamer) + thread.start() + ThreadProgress(thread, 'Loading repositories', '') + + +class UpgradeAllPackagesThread(threading.Thread, PackageInstaller): + """ + A thread to run the action of retrieving upgradable packages in. + """ + + def __init__(self, window, package_renamer): + self.window = window + self.package_renamer = package_renamer + self.completion_type = 'upgraded' + threading.Thread.__init__(self) + PackageInstaller.__init__(self) + + def run(self): + self.package_renamer.rename_packages(self) + package_list = self.make_package_list(['install', 'reinstall', 'none']) + + disabled_packages = [] + + def do_upgrades(): + # Pause so packages can be disabled + time.sleep(0.5) + + # We use a function to generate the on-complete lambda because if + # we don't, the lambda will bind to info at the current scope, and + # thus use the last value of info from the loop + def make_on_complete(name): + return lambda: self.reenable_package(name) + + for info in package_list: + if info[0] in disabled_packages: + on_complete = make_on_complete(info[0]) + else: + on_complete = None + thread = PackageInstallerThread(self.manager, info[0], + on_complete) + thread.start() + ThreadProgress(thread, 'Upgrading package %s' % info[0], + 'Package %s successfully %s' % (info[0], + self.completion_type)) + + # Disabling a package means changing settings, which can only be done + # in the main thread. We then create a new background thread so that + # the upgrade process does not block the UI. + def disable_packages(): + package_names = [] + for info in package_list: + package_names.append(info[0]) + disabled_packages.extend(self.disable_packages(package_names)) + threading.Thread(target=do_upgrades).start() + + sublime.set_timeout(disable_packages, 1) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/upgrade_package_command.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/upgrade_package_command.py new file mode 100644 index 0000000..6c478e6 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/commands/upgrade_package_command.py @@ -0,0 +1,81 @@ +import threading + +import sublime +import sublime_plugin + +from ..show_error import show_error +from ..thread_progress import ThreadProgress +from ..package_installer import PackageInstaller, PackageInstallerThread +from ..package_renamer import PackageRenamer + + +class UpgradePackageCommand(sublime_plugin.WindowCommand): + """ + A command that presents the list of installed packages that can be upgraded + """ + + def run(self): + package_renamer = PackageRenamer() + package_renamer.load_settings() + + thread = UpgradePackageThread(self.window, package_renamer) + thread.start() + ThreadProgress(thread, 'Loading repositories', '') + + +class UpgradePackageThread(threading.Thread, PackageInstaller): + """ + A thread to run the action of retrieving upgradable packages in. + """ + + def __init__(self, window, package_renamer): + """ + :param window: + An instance of :class:`sublime.Window` that represents the Sublime + Text window to show the list of upgradable packages in. + + :param package_renamer: + An instance of :class:`PackageRenamer` + """ + self.window = window + self.package_renamer = package_renamer + self.completion_type = 'upgraded' + threading.Thread.__init__(self) + PackageInstaller.__init__(self) + + def run(self): + self.package_renamer.rename_packages(self) + + self.package_list = self.make_package_list(['install', 'reinstall', + 'none']) + + def show_quick_panel(): + if not self.package_list: + show_error('There are no packages ready for upgrade') + return + self.window.show_quick_panel(self.package_list, self.on_done) + sublime.set_timeout(show_quick_panel, 10) + + def on_done(self, picked): + """ + Quick panel user selection handler - disables a package, upgrades it, + then re-enables the package + + :param picked: + An integer of the 0-based package name index from the presented + list. -1 means the user cancelled. + """ + + if picked == -1: + return + name = self.package_list[picked][0] + + if name in self.disable_packages(name): + on_complete = lambda: self.reenable_package(name) + else: + on_complete = None + + thread = PackageInstallerThread(self.manager, name, on_complete) + thread.start() + ThreadProgress(thread, 'Upgrading package %s' % name, + 'Package %s successfully %s' % (name, self.completion_type)) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/console_write.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/console_write.py new file mode 100644 index 0000000..5fb0796 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/console_write.py @@ -0,0 +1,20 @@ +import sys + + +def console_write(string, prefix=False): + """ + Writes a value to the Sublime Text console, encoding unicode to utf-8 first + + :param string: + The value to write + + :param prefix: + If the string "Package Control: " should be prefixed to the string + """ + + if sys.version_info < (3,): + if isinstance(string, unicode): + string = string.encode('UTF-8') + if prefix: + sys.stdout.write('Package Control: ') + print(string) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/download_manager.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/download_manager.py new file mode 100644 index 0000000..a4d028d --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/download_manager.py @@ -0,0 +1,231 @@ +import sys +import re +import socket +from threading import Lock, Timer +from contextlib import contextmanager + +try: + # Python 3 + from urllib.parse import urlparse +except (ImportError): + # Python 2 + from urlparse import urlparse + +from . import __version__ + +from .show_error import show_error +from .console_write import console_write +from .cache import set_cache, get_cache +from .unicode import unicode_from_os + +from .downloaders import DOWNLOADERS +from .downloaders.binary_not_found_error import BinaryNotFoundError +from .downloaders.rate_limit_exception import RateLimitException +from .downloaders.no_ca_cert_exception import NoCaCertException +from .downloaders.downloader_exception import DownloaderException +from .http_cache import HttpCache + + +# A dict of domains - each points to a list of downloaders +_managers = {} + +# How many managers are currently checked out +_in_use = 0 + +# Make sure connection management doesn't run into threading issues +_lock = Lock() + +# A timer used to disconnect all managers after a period of no usage +_timer = None + + +@contextmanager +def downloader(url, settings): + try: + manager = _grab(url, settings) + yield manager + + finally: + _release(url, manager) + + +def _grab(url, settings): + global _managers, _lock, _in_use, _timer + + _lock.acquire() + try: + if _timer: + _timer.cancel() + _timer = None + + hostname = urlparse(url).hostname.lower() + if hostname not in _managers: + _managers[hostname] = [] + + if not _managers[hostname]: + _managers[hostname].append(DownloadManager(settings)) + + _in_use += 1 + + return _managers[hostname].pop() + + finally: + _lock.release() + + +def _release(url, manager): + global _managers, _lock, _in_use, _timer + + _lock.acquire() + try: + hostname = urlparse(url).hostname.lower() + _managers[hostname].insert(0, manager) + + _in_use -= 1 + + if _timer: + _timer.cancel() + _timer = None + + if _in_use == 0: + _timer = Timer(5.0, close_all_connections) + _timer.start() + + finally: + _lock.release() + + +def close_all_connections(): + global _managers, _lock, _in_use, _timer + + _lock.acquire() + try: + if _timer: + _timer.cancel() + _timer = None + + for domain, managers in _managers.items(): + for manager in managers: + manager.close() + _managers = {} + + finally: + _lock.release() + + +class DownloadManager(object): + def __init__(self, settings): + # Cache the downloader for re-use + self.downloader = None + + user_agent = settings.get('user_agent') + if user_agent and user_agent.find('%s') != -1: + settings['user_agent'] = user_agent % __version__ + + self.settings = settings + if settings.get('http_cache'): + cache_length = settings.get('http_cache_length', 604800) + self.settings['cache'] = HttpCache(cache_length) + + def close(self): + if self.downloader: + self.downloader.close() + self.downloader = None + + def fetch(self, url, error_message, prefer_cached=False): + """ + Downloads a URL and returns the contents + + :param url: + The string URL to download + + :param error_message: + The error message to include if the download fails + + :param prefer_cached: + If cached version of the URL content is preferred over a new request + + :raises: + DownloaderException: if there was an error downloading the URL + + :return: + The string contents of the URL + """ + + is_ssl = re.search('^https://', url) != None + + # Make sure we have a downloader, and it supports SSL if we need it + if not self.downloader or (is_ssl and not self.downloader.supports_ssl()): + for downloader_class in DOWNLOADERS: + try: + downloader = downloader_class(self.settings) + if is_ssl and not downloader.supports_ssl(): + continue + self.downloader = downloader + break + except (BinaryNotFoundError): + pass + + if not self.downloader: + error_string = u'Unable to download %s due to no ssl module available and no capable program found. Please install curl or wget.' % url + show_error(error_string) + raise DownloaderException(error_string) + + url = url.replace(' ', '%20') + hostname = urlparse(url).hostname + if hostname: + hostname = hostname.lower() + timeout = self.settings.get('timeout', 3) + + rate_limited_domains = get_cache('rate_limited_domains', []) + no_ca_cert_domains = get_cache('no_ca_cert_domains', []) + + if self.settings.get('debug'): + try: + ip = socket.gethostbyname(hostname) + except (socket.gaierror) as e: + ip = unicode_from_os(e) + except (TypeError) as e: + ip = None + + console_write(u"Download Debug", True) + console_write(u" URL: %s" % url) + console_write(u" Resolved IP: %s" % ip) + console_write(u" Timeout: %s" % str(timeout)) + + if hostname in rate_limited_domains: + error_string = u"Skipping due to hitting rate limit for %s" % hostname + if self.settings.get('debug'): + console_write(u" %s" % error_string) + raise DownloaderException(error_string) + + if hostname in no_ca_cert_domains: + error_string = u" Skipping since there are no CA certs for %s" % hostname + if self.settings.get('debug'): + console_write(u" %s" % error_string) + raise DownloaderException(error_string) + + try: + return self.downloader.download(url, error_message, timeout, 3, prefer_cached) + + except (RateLimitException) as e: + + rate_limited_domains.append(hostname) + set_cache('rate_limited_domains', rate_limited_domains, self.settings.get('cache_length')) + + error_string = (u'Hit rate limit of %s for %s, skipping all futher ' + + u'download requests for this domain') % (e.limit, e.domain) + console_write(error_string, True) + raise + + except (NoCaCertException) as e: + + no_ca_cert_domains.append(hostname) + set_cache('no_ca_cert_domains', no_ca_cert_domains, self.settings.get('cache_length')) + + error_string = (u'No CA certs available for %s, skipping all futher ' + + u'download requests for this domain. If you are on a trusted ' + + u'network, you can add the CA certs by running the "Grab ' + + u'CA Certs" command from the command palette.') % e.domain + console_write(error_string, True) + raise diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/__init__.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/__init__.py new file mode 100644 index 0000000..fb68aef --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/__init__.py @@ -0,0 +1,11 @@ +import os + +if os.name == 'nt': + from .wininet_downloader import WinINetDownloader + DOWNLOADERS = [WinINetDownloader] + +else: + from .urllib_downloader import UrlLibDownloader + from .curl_downloader import CurlDownloader + from .wget_downloader import WgetDownloader + DOWNLOADERS = [UrlLibDownloader, CurlDownloader, WgetDownloader] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/background_downloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/background_downloader.py new file mode 100644 index 0000000..250d2de --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/background_downloader.py @@ -0,0 +1,62 @@ +import threading + + +class BackgroundDownloader(threading.Thread): + """ + Downloads information from one or more URLs in the background. + Normal usage is to use one BackgroundDownloader per domain name. + + :param settings: + A dict containing at least the following fields: + `cache_length`, + `debug`, + `timeout`, + `user_agent`, + `http_proxy`, + `https_proxy`, + `proxy_username`, + `proxy_password` + + :param providers: + An array of providers that can download the URLs + """ + + def __init__(self, settings, providers): + self.settings = settings + self.urls = [] + self.providers = providers + self.used_providers = {} + threading.Thread.__init__(self) + + def add_url(self, url): + """ + Adds a URL to the list to download + + :param url: + The URL to download info about + """ + + self.urls.append(url) + + def get_provider(self, url): + """ + Returns the provider for the URL specified + + :param url: + The URL to return the provider for + + :return: + The provider object for the URL + """ + + return self.used_providers[url] + + def run(self): + for url in self.urls: + for provider_class in self.providers: + if provider_class.match_url(url): + provider = provider_class(url, self.settings) + break + + provider.prefetch() + self.used_providers[url] = provider diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/binary_not_found_error.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/binary_not_found_error.py new file mode 100644 index 0000000..a7955b9 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/binary_not_found_error.py @@ -0,0 +1,4 @@ +class BinaryNotFoundError(Exception): + """If a necessary executable is not found in the PATH on the system""" + + pass diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/caching_downloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/caching_downloader.py new file mode 100644 index 0000000..ab3d71f --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/caching_downloader.py @@ -0,0 +1,185 @@ +import sys +import re +import json +import hashlib + +from ..console_write import console_write + + +class CachingDownloader(object): + """ + A base downloader that will use a caching backend to cache HTTP requests + and make conditional requests. + """ + + def add_conditional_headers(self, url, headers): + """ + Add `If-Modified-Since` and `If-None-Match` headers to a request if a + cached copy exists + + :param headers: + A dict with the request headers + + :return: + The request headers dict, possibly with new headers added + """ + + if not self.settings.get('cache'): + return headers + + info_key = self.generate_key(url, '.info') + info_json = self.settings['cache'].get(info_key) + + if not info_json: + return headers + + # Make sure we have the cached content to use if we get a 304 + key = self.generate_key(url) + if not self.settings['cache'].has(key): + return headers + + try: + info = json.loads(info_json.decode('utf-8')) + except ValueError: + return headers + + etag = info.get('etag') + if etag: + headers['If-None-Match'] = etag + + last_modified = info.get('last-modified') + if last_modified: + headers['If-Modified-Since'] = last_modified + + return headers + + def cache_result(self, method, url, status, headers, content): + """ + Processes a request result, either caching the result, or returning + the cached version of the url. + + :param method: + The HTTP method used for the request + + :param url: + The url of the request + + :param status: + The numeric response status of the request + + :param headers: + A dict of reponse headers, with keys being lowercase + + :param content: + The response content + + :return: + The response content + """ + + debug = self.settings.get('debug', False) + + if not self.settings.get('cache'): + if debug: + console_write(u"Skipping cache since there is no cache object", True) + return content + + if method.lower() != 'get': + if debug: + console_write(u"Skipping cache since the HTTP method != GET", True) + return content + + status = int(status) + + # Don't do anything unless it was successful or not modified + if status not in [200, 304]: + if debug: + console_write(u"Skipping cache since the HTTP status code not one of: 200, 304", True) + return content + + key = self.generate_key(url) + + if status == 304: + cached_content = self.settings['cache'].get(key) + if cached_content: + if debug: + console_write(u"Using cached content for %s" % url, True) + return cached_content + + # If we got a 304, but did not have the cached content + # stop here so we don't cache an empty response + return content + + # If we got here, the status is 200 + + # Respect some basic cache control headers + cache_control = headers.get('cache-control', '') + if cache_control: + fields = re.split(',\s*', cache_control) + for field in fields: + if field == 'no-store': + return content + + # Don't ever cache zip/binary files for the sake of hard drive space + if headers.get('content-type') in ['application/zip', 'application/octet-stream']: + if debug: + console_write(u"Skipping cache since the response is a zip file", True) + return content + + etag = headers.get('etag') + last_modified = headers.get('last-modified') + + if not etag and not last_modified: + return content + + struct = {'etag': etag, 'last-modified': last_modified} + struct_json = json.dumps(struct, indent=4) + + info_key = self.generate_key(url, '.info') + if debug: + console_write(u"Caching %s in %s" % (url, key), True) + + self.settings['cache'].set(info_key, struct_json.encode('utf-8')) + self.settings['cache'].set(key, content) + + return content + + def generate_key(self, url, suffix=''): + """ + Generates a key to store the cache under + + :param url: + The URL being cached + + :param suffix: + A string to append to the key + + :return: + A string key for the URL + """ + + if sys.version_info >= (3,) or isinstance(url, unicode): + url = url.encode('utf-8') + + key = hashlib.md5(url).hexdigest() + return key + suffix + + def retrieve_cached(self, url): + """ + Tries to return the cached content for a URL + + :param url: + The URL to get the cached content for + + :return: + The cached content + """ + + key = self.generate_key(url) + if not self.settings['cache'].has(key): + return False + + if self.settings.get('debug'): + console_write(u"Using cached content for %s" % url, True) + + return self.settings['cache'].get(key) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/cert_provider.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/cert_provider.py new file mode 100644 index 0000000..f8c8c3b --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/cert_provider.py @@ -0,0 +1,203 @@ +import os +import re +import json + +import sublime + +from ..console_write import console_write +from ..open_compat import open_compat, read_compat +from ..package_io import read_package_file +from ..cache import get_cache +from ..ca_certs import get_system_ca_bundle_path +from .no_ca_cert_exception import NoCaCertException +from .downloader_exception import DownloaderException + + +class CertProvider(object): + """ + A base downloader that provides access to a ca-bundle for validating + SSL certificates. + """ + + def check_certs(self, domain, timeout): + """ + Ensures that the SSL CA cert for a domain is present on the machine + + :param domain: + The domain to ensure there is a CA cert for + + :param timeout: + The int timeout for downloading the CA cert from the channel + + :raises: + NoCaCertException: when a suitable CA cert could not be found + + :return: + The CA cert bundle path + """ + + # Try to use the system CA bundle + ca_bundle_path = get_system_ca_bundle_path(self.settings) + if ca_bundle_path: + return ca_bundle_path + + # If the system bundle did not work, fall back to our CA distribution + # system. Hopefully this will be going away soon. + if self.settings.get('debug'): + console_write(u'Unable to find system CA cert bundle, falling back to certs provided by Package Control') + + cert_match = False + + certs_list = get_cache('*.certs', self.settings.get('certs', {})) + + ca_bundle_path = os.path.join(sublime.packages_path(), 'User', 'Package Control.ca-bundle') + if not os.path.exists(ca_bundle_path) or os.stat(ca_bundle_path).st_size == 0: + bundle_contents = read_package_file('Package Control', 'Package Control.ca-bundle', True) + if not bundle_contents: + raise NoCaCertException(u'Unable to copy distributed Package Control.ca-bundle', domain) + with open_compat(ca_bundle_path, 'wb') as f: + f.write(bundle_contents) + + cert_info = certs_list.get(domain) + if cert_info: + cert_match = self.locate_cert(cert_info[0], + cert_info[1], domain, timeout) + + wildcard_info = certs_list.get('*') + if wildcard_info: + cert_match = self.locate_cert(wildcard_info[0], + wildcard_info[1], domain, timeout) or cert_match + + if not cert_match: + raise NoCaCertException(u'No CA certs available for %s' % domain, domain) + + return ca_bundle_path + + def locate_cert(self, cert_id, location, domain, timeout): + """ + Makes sure the SSL cert specified has been added to the CA cert + bundle that is present on the machine + + :param cert_id: + The identifier for CA cert(s). For those provided by the channel + system, this will be an md5 of the contents of the cert(s). For + user-provided certs, this is something they provide. + + :param location: + An http(s) URL, or absolute filesystem path to the CA cert(s) + + :param domain: + The domain to ensure there is a CA cert for + + :param timeout: + The int timeout for downloading the CA cert from the channel + + :return: + If the cert specified (by cert_id) is present on the machine and + part of the Package Control.ca-bundle file in the User package folder + """ + + ca_list_path = os.path.join(sublime.packages_path(), 'User', 'Package Control.ca-list') + if not os.path.exists(ca_list_path) or os.stat(ca_list_path).st_size == 0: + list_contents = read_package_file('Package Control', 'Package Control.ca-list') + if not list_contents: + raise NoCaCertException(u'Unable to copy distributed Package Control.ca-list', domain) + with open_compat(ca_list_path, 'w') as f: + f.write(list_contents) + + ca_certs = [] + with open_compat(ca_list_path, 'r') as f: + ca_certs = json.loads(read_compat(f)) + + if not cert_id in ca_certs: + if str(location) != '': + if re.match('^https?://', location): + contents = self.download_cert(cert_id, location, domain, + timeout) + else: + contents = self.load_cert(cert_id, location, domain) + if contents: + self.save_cert(cert_id, contents) + return True + return False + return True + + def download_cert(self, cert_id, url, domain, timeout): + """ + Downloads CA cert(s) from a URL + + :param cert_id: + The identifier for CA cert(s). For those provided by the channel + system, this will be an md5 of the contents of the cert(s). For + user-provided certs, this is something they provide. + + :param url: + An http(s) URL to the CA cert(s) + + :param domain: + The domain to ensure there is a CA cert for + + :param timeout: + The int timeout for downloading the CA cert from the channel + + :return: + The contents of the CA cert(s) + """ + + cert_downloader = self.__class__(self.settings) + if self.settings.get('debug'): + console_write(u"Downloading CA cert for %s from \"%s\"" % (domain, url), True) + return cert_downloader.download(url, + 'Error downloading CA certs for %s.' % domain, timeout, 1) + + def load_cert(self, cert_id, path, domain): + """ + Copies CA cert(s) from a file path + + :param cert_id: + The identifier for CA cert(s). For those provided by the channel + system, this will be an md5 of the contents of the cert(s). For + user-provided certs, this is something they provide. + + :param path: + The absolute filesystem path to a file containing the CA cert(s) + + :param domain: + The domain name the cert is for + + :return: + The contents of the CA cert(s) + """ + + if os.path.exists(path): + if self.settings.get('debug'): + console_write(u"Copying CA cert for %s from \"%s\"" % (domain, path), True) + with open_compat(path, 'rb') as f: + return f.read() + else: + raise NoCaCertException(u"Unable to find CA cert for %s at \"%s\"" % (domain, path), domain) + + def save_cert(self, cert_id, contents): + """ + Saves CA cert(s) to the Package Control.ca-bundle + + :param cert_id: + The identifier for CA cert(s). For those provided by the channel + system, this will be an md5 of the contents of the cert(s). For + user-provided certs, this is something they provide. + + :param contents: + The contents of the CA cert(s) + """ + + + ca_bundle_path = os.path.join(sublime.packages_path(), 'User', 'Package Control.ca-bundle') + with open_compat(ca_bundle_path, 'ab') as f: + f.write(b"\n" + contents) + + ca_list_path = os.path.join(sublime.packages_path(), 'User', 'Package Control.ca-list') + with open_compat(ca_list_path, 'r') as f: + ca_certs = json.loads(read_compat(f)) + ca_certs.append(cert_id) + with open_compat(ca_list_path, 'w') as f: + f.write(json.dumps(ca_certs, indent=4)) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/cli_downloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/cli_downloader.py new file mode 100644 index 0000000..76c42dd --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/cli_downloader.py @@ -0,0 +1,81 @@ +import os +import subprocess + +from ..console_write import console_write +from ..cmd import create_cmd +from .non_clean_exit_error import NonCleanExitError +from .binary_not_found_error import BinaryNotFoundError + + +class CliDownloader(object): + """ + Base for downloaders that use a command line program + + :param settings: + A dict of the various Package Control settings. The Sublime Text + Settings API is not used because this code is run in a thread. + """ + + def __init__(self, settings): + self.settings = settings + + def clean_tmp_file(self): + if os.path.exists(self.tmp_file): + os.remove(self.tmp_file) + + def find_binary(self, name): + """ + Finds the given executable name in the system PATH + + :param name: + The exact name of the executable to find + + :return: + The absolute path to the executable + + :raises: + BinaryNotFoundError when the executable can not be found + """ + + dirs = os.environ['PATH'].split(os.pathsep) + if os.name != 'nt': + # This is mostly for OS X, which seems to launch ST with a + # minimal set of environmental variables + dirs.append('/usr/local/bin') + + for dir_ in dirs: + path = os.path.join(dir_, name) + if os.path.exists(path): + return path + + raise BinaryNotFoundError('The binary %s could not be located' % name) + + def execute(self, args): + """ + Runs the executable and args and returns the result + + :param args: + A list of the executable path and all arguments to be passed to it + + :return: + The text output of the executable + + :raises: + NonCleanExitError when the executable exits with an error + """ + + if self.settings.get('debug'): + console_write(u"Trying to execute command %s" % create_cmd(args), True) + + proc = subprocess.Popen(args, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + + output = proc.stdout.read() + self.stderr = proc.stderr.read() + returncode = proc.wait() + if returncode != 0: + error = NonCleanExitError(returncode) + error.stderr = self.stderr + error.stdout = output + raise error + return output diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/curl_downloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/curl_downloader.py new file mode 100644 index 0000000..b09d448 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/curl_downloader.py @@ -0,0 +1,267 @@ +import tempfile +import re +import os + +from ..console_write import console_write +from ..open_compat import open_compat, read_compat +from .cli_downloader import CliDownloader +from .non_clean_exit_error import NonCleanExitError +from .rate_limit_exception import RateLimitException +from .downloader_exception import DownloaderException +from .cert_provider import CertProvider +from .limiting_downloader import LimitingDownloader +from .caching_downloader import CachingDownloader + + +class CurlDownloader(CliDownloader, CertProvider, LimitingDownloader, CachingDownloader): + """ + A downloader that uses the command line program curl + + :param settings: + A dict of the various Package Control settings. The Sublime Text + Settings API is not used because this code is run in a thread. + + :raises: + BinaryNotFoundError: when curl can not be found + """ + + def __init__(self, settings): + self.settings = settings + self.curl = self.find_binary('curl') + + def close(self): + """ + No-op for compatibility with UrllibDownloader and WinINetDownloader + """ + + pass + + def download(self, url, error_message, timeout, tries, prefer_cached=False): + """ + Downloads a URL and returns the contents + + :param url: + The URL to download + + :param error_message: + A string to include in the console error that is printed + when an error occurs + + :param timeout: + The int number of seconds to set the timeout to + + :param tries: + The int number of times to try and download the URL in the case of + a timeout or HTTP 503 error + + :param prefer_cached: + If a cached version should be returned instead of trying a new request + + :raises: + NoCaCertException: when no CA certs can be found for the url + RateLimitException: when a rate limit is hit + DownloaderException: when any other download error occurs + + :return: + The string contents of the URL + """ + + if prefer_cached: + cached = self.retrieve_cached(url) + if cached: + return cached + + self.tmp_file = tempfile.NamedTemporaryFile().name + command = [self.curl, '--user-agent', self.settings.get('user_agent'), + '--connect-timeout', str(int(timeout)), '-sSL', + # Don't be alarmed if the response from the server does not select + # one of these since the server runs a relatively new version of + # OpenSSL which supports compression on the SSL layer, and Apache + # will use that instead of HTTP-level encoding. + '--compressed', + # We have to capture the headers to check for rate limit info + '--dump-header', self.tmp_file] + + request_headers = self.add_conditional_headers(url, {}) + + for name, value in request_headers.items(): + command.extend(['--header', "%s: %s" % (name, value)]) + + secure_url_match = re.match('^https://([^/]+)', url) + if secure_url_match != None: + secure_domain = secure_url_match.group(1) + bundle_path = self.check_certs(secure_domain, timeout) + command.extend(['--cacert', bundle_path]) + + debug = self.settings.get('debug') + if debug: + command.append('-v') + + http_proxy = self.settings.get('http_proxy') + https_proxy = self.settings.get('https_proxy') + proxy_username = self.settings.get('proxy_username') + proxy_password = self.settings.get('proxy_password') + + if debug: + console_write(u"Curl Debug Proxy", True) + console_write(u" http_proxy: %s" % http_proxy) + console_write(u" https_proxy: %s" % https_proxy) + console_write(u" proxy_username: %s" % proxy_username) + console_write(u" proxy_password: %s" % proxy_password) + + if http_proxy or https_proxy: + command.append('--proxy-anyauth') + + if proxy_username or proxy_password: + command.extend(['-U', u"%s:%s" % (proxy_username, proxy_password)]) + + if http_proxy: + os.putenv('http_proxy', http_proxy) + if https_proxy: + os.putenv('HTTPS_PROXY', https_proxy) + + command.append(url) + + error_string = None + while tries > 0: + tries -= 1 + try: + output = self.execute(command) + + with open_compat(self.tmp_file, 'r') as f: + headers_str = read_compat(f) + self.clean_tmp_file() + + message = 'OK' + status = 200 + headers = {} + for header in headers_str.splitlines(): + if header[0:5] == 'HTTP/': + message = re.sub('^HTTP/\d\.\d\s+\d+\s*', '', header) + status = int(re.sub('^HTTP/\d\.\d\s+(\d+)(\s+.*)?$', '\\1', header)) + continue + if header.strip() == '': + continue + name, value = header.split(':', 1) + headers[name.lower()] = value.strip() + + if debug: + self.print_debug(self.stderr.decode('utf-8')) + + self.handle_rate_limit(headers, url) + + if status not in [200, 304]: + e = NonCleanExitError(22) + e.stderr = "%s %s" % (status, message) + raise e + + output = self.cache_result('get', url, status, headers, output) + + return output + + except (NonCleanExitError) as e: + # Stderr is used for both the error message and the debug info + # so we need to process it to extra the debug info + if self.settings.get('debug'): + if hasattr(e.stderr, 'decode'): + e.stderr = e.stderr.decode('utf-8') + e.stderr = self.print_debug(e.stderr) + + self.clean_tmp_file() + + if e.returncode == 22: + code = re.sub('^.*?(\d+)([\w\s]+)?$', '\\1', e.stderr) + if code == '503' and tries != 0: + # GitHub and BitBucket seem to rate limit via 503 + error_string = u'Downloading %s was rate limited' % url + if tries: + error_string += ', trying again' + if debug: + console_write(error_string, True) + continue + + download_error = u'HTTP error ' + code + + elif e.returncode == 6: + download_error = u'URL error host not found' + + elif e.returncode == 28: + # GitHub and BitBucket seem to time out a lot + error_string = u'Downloading %s timed out' % url + if tries: + error_string += ', trying again' + if debug: + console_write(error_string, True) + continue + + else: + download_error = e.stderr.rstrip() + + error_string = u'%s %s downloading %s.' % (error_message, download_error, url) + + break + + raise DownloaderException(error_string) + + def supports_ssl(self): + """ + Indicates if the object can handle HTTPS requests + + :return: + If the object supports HTTPS requests + """ + + return True + + def print_debug(self, string): + """ + Takes debug output from curl and groups and prints it + + :param string: + The complete debug output from curl + + :return: + A string containing any stderr output + """ + + section = 'General' + last_section = None + + output = '' + + for line in string.splitlines(): + # Placeholder for body of request + if line and line[0:2] == '{ ': + continue + if line and line[0:18] == '} [data not shown]': + continue + + if len(line) > 1: + subtract = 0 + if line[0:2] == '* ': + section = 'General' + subtract = 2 + elif line[0:2] == '> ': + section = 'Write' + subtract = 2 + elif line[0:2] == '< ': + section = 'Read' + subtract = 2 + line = line[subtract:] + + # If the line does not start with "* ", "< ", "> " or " " + # then it is a real stderr message + if subtract == 0 and line[0:2] != ' ': + output += line.rstrip() + ' ' + continue + + if line.strip() == '': + continue + + if section != last_section: + console_write(u"Curl HTTP Debug %s" % section, True) + + console_write(u' ' + line) + last_section = section + + return output.rstrip() diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/decoding_downloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/decoding_downloader.py new file mode 100644 index 0000000..bc1acf3 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/decoding_downloader.py @@ -0,0 +1,24 @@ +import gzip +import zlib + +try: + # Python 3 + from io import BytesIO as StringIO +except (ImportError): + # Python 2 + from StringIO import StringIO + + +class DecodingDownloader(object): + """ + A base for downloaders that provides the ability to decode gzipped + or deflated content. + """ + + def decode_response(self, encoding, response): + if encoding == 'gzip': + return gzip.GzipFile(fileobj=StringIO(response)).read() + elif encoding == 'deflate': + decompresser = zlib.decompressobj(-zlib.MAX_WBITS) + return decompresser.decompress(response) + decompresser.flush() + return response diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/downloader_exception.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/downloader_exception.py new file mode 100644 index 0000000..7519d8f --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/downloader_exception.py @@ -0,0 +1,5 @@ +class DownloaderException(Exception): + """If a downloader could not download a URL""" + + def __str__(self): + return self.args[0] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/http_error.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/http_error.py new file mode 100644 index 0000000..996e46d --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/http_error.py @@ -0,0 +1,9 @@ +class HttpError(Exception): + """If a downloader was able to download a URL, but the result was not a 200 or 304""" + + def __init__(self, message, code): + self.code = code + super(HttpError, self).__init__(message) + + def __str__(self): + return self.args[0] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/limiting_downloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/limiting_downloader.py new file mode 100644 index 0000000..10d2f1f --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/limiting_downloader.py @@ -0,0 +1,36 @@ +try: + # Python 3 + from urllib.parse import urlparse +except (ImportError): + # Python 2 + from urlparse import urlparse + +from .rate_limit_exception import RateLimitException + + +class LimitingDownloader(object): + """ + A base for downloaders that checks for rate limiting headers. + """ + + def handle_rate_limit(self, headers, url): + """ + Checks the headers of a response object to make sure we are obeying the + rate limit + + :param headers: + The dict-like object that contains lower-cased headers + + :param url: + The URL that was requested + + :raises: + RateLimitException when the rate limit has been hit + """ + + limit_remaining = headers.get('x-ratelimit-remaining', '1') + limit = headers.get('x-ratelimit-limit', '1') + + if str(limit_remaining) == '0': + hostname = urlparse(url).hostname + raise RateLimitException(hostname, limit) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/no_ca_cert_exception.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/no_ca_cert_exception.py new file mode 100644 index 0000000..8452bd9 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/no_ca_cert_exception.py @@ -0,0 +1,11 @@ +from .downloader_exception import DownloaderException + + +class NoCaCertException(DownloaderException): + """ + An exception for when there is no CA cert for a domain name + """ + + def __init__(self, message, domain): + self.domain = domain + super(NoCaCertException, self).__init__(message) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/non_clean_exit_error.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/non_clean_exit_error.py new file mode 100644 index 0000000..a932363 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/non_clean_exit_error.py @@ -0,0 +1,13 @@ +class NonCleanExitError(Exception): + """ + When an subprocess does not exit cleanly + + :param returncode: + The command line integer return code of the subprocess + """ + + def __init__(self, returncode): + self.returncode = returncode + + def __str__(self): + return repr(self.returncode) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/non_http_error.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/non_http_error.py new file mode 100644 index 0000000..8a45595 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/non_http_error.py @@ -0,0 +1,5 @@ +class NonHttpError(Exception): + """If a downloader had a non-clean exit, but it was not due to an HTTP error""" + + def __str__(self): + return self.args[0] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/rate_limit_exception.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/rate_limit_exception.py new file mode 100644 index 0000000..18d2b9e --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/rate_limit_exception.py @@ -0,0 +1,13 @@ +from .downloader_exception import DownloaderException + + +class RateLimitException(DownloaderException): + """ + An exception for when the rate limit of an API has been exceeded. + """ + + def __init__(self, domain, limit): + self.domain = domain + self.limit = limit + message = u'Rate limit of %s exceeded for %s' % (limit, domain) + super(RateLimitException, self).__init__(message) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/urllib_downloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/urllib_downloader.py new file mode 100644 index 0000000..aa04d31 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/urllib_downloader.py @@ -0,0 +1,291 @@ +import re +import os +import sys + +from .. import http + +try: + # Python 3 + from http.client import HTTPException, BadStatusLine + from urllib.request import ProxyHandler, HTTPPasswordMgrWithDefaultRealm, ProxyBasicAuthHandler, ProxyDigestAuthHandler, build_opener, Request + from urllib.error import HTTPError, URLError + import urllib.request as urllib_compat +except (ImportError): + # Python 2 + from httplib import HTTPException, BadStatusLine + from urllib2 import ProxyHandler, HTTPPasswordMgrWithDefaultRealm, ProxyBasicAuthHandler, ProxyDigestAuthHandler, build_opener, Request + from urllib2 import HTTPError, URLError + import urllib2 as urllib_compat + +try: + # Python 3.3 + import ConnectionError +except (ImportError): + # Python 2.6-3.2 + from socket import error as ConnectionError + +from ..console_write import console_write +from ..unicode import unicode_from_os +from ..http.validating_https_handler import ValidatingHTTPSHandler +from ..http.debuggable_http_handler import DebuggableHTTPHandler +from .rate_limit_exception import RateLimitException +from .downloader_exception import DownloaderException +from .cert_provider import CertProvider +from .decoding_downloader import DecodingDownloader +from .limiting_downloader import LimitingDownloader +from .caching_downloader import CachingDownloader + + +class UrlLibDownloader(CertProvider, DecodingDownloader, LimitingDownloader, CachingDownloader): + """ + A downloader that uses the Python urllib module + + :param settings: + A dict of the various Package Control settings. The Sublime Text + Settings API is not used because this code is run in a thread. + """ + + def __init__(self, settings): + self.opener = None + self.settings = settings + + def close(self): + """ + Closes any persistent/open connections + """ + + if not self.opener: + return + handler = self.get_handler() + if handler: + handler.close() + self.opener = None + + def download(self, url, error_message, timeout, tries, prefer_cached=False): + """ + Downloads a URL and returns the contents + + Uses the proxy settings from the Package Control.sublime-settings file, + however there seem to be a decent number of proxies that this code + does not work with. Patches welcome! + + :param url: + The URL to download + + :param error_message: + A string to include in the console error that is printed + when an error occurs + + :param timeout: + The int number of seconds to set the timeout to + + :param tries: + The int number of times to try and download the URL in the case of + a timeout or HTTP 503 error + + :param prefer_cached: + If a cached version should be returned instead of trying a new request + + :raises: + NoCaCertException: when no CA certs can be found for the url + RateLimitException: when a rate limit is hit + DownloaderException: when any other download error occurs + + :return: + The string contents of the URL + """ + + if prefer_cached: + cached = self.retrieve_cached(url) + if cached: + return cached + + self.setup_opener(url, timeout) + + debug = self.settings.get('debug') + error_string = None + while tries > 0: + tries -= 1 + try: + request_headers = { + "User-Agent": self.settings.get('user_agent'), + # Don't be alarmed if the response from the server does not + # select one of these since the server runs a relatively new + # version of OpenSSL which supports compression on the SSL + # layer, and Apache will use that instead of HTTP-level + # encoding. + "Accept-Encoding": "gzip,deflate" + } + request_headers = self.add_conditional_headers(url, request_headers) + request = Request(url, headers=request_headers) + http_file = self.opener.open(request, timeout=timeout) + self.handle_rate_limit(http_file.headers, url) + + result = http_file.read() + # Make sure the response is closed so we can re-use the connection + http_file.close() + + encoding = http_file.headers.get('content-encoding') + result = self.decode_response(encoding, result) + + return self.cache_result('get', url, http_file.getcode(), + http_file.headers, result) + + except (HTTPException) as e: + # Since we use keep-alives, it is possible the other end closed + # the connection, and we may just need to re-open + if isinstance(e, BadStatusLine): + handler = self.get_handler() + if handler and handler.use_count > 1: + self.close() + self.setup_opener(url, timeout) + tries += 1 + continue + + error_string = u'%s HTTP exception %s (%s) downloading %s.' % ( + error_message, e.__class__.__name__, unicode_from_os(e), url) + + except (HTTPError) as e: + # Make sure the response is closed so we can re-use the connection + e.read() + e.close() + + # Make sure we obey Github's rate limiting headers + self.handle_rate_limit(e.headers, url) + + # Handle cached responses + if unicode_from_os(e.code) == '304': + return self.cache_result('get', url, int(e.code), e.headers, b'') + + # Bitbucket and Github return 503 a decent amount + if unicode_from_os(e.code) == '503' and tries != 0: + error_string = u'Downloading %s was rate limited' % url + if tries: + error_string += ', trying again' + if debug: + console_write(error_string, True) + continue + + error_string = u'%s HTTP error %s downloading %s.' % ( + error_message, unicode_from_os(e.code), url) + + except (URLError) as e: + + # Bitbucket and Github timeout a decent amount + if unicode_from_os(e.reason) == 'The read operation timed out' \ + or unicode_from_os(e.reason) == 'timed out': + error_string = u'Downloading %s timed out' % url + if tries: + error_string += ', trying again' + if debug: + console_write(error_string, True) + continue + + error_string = u'%s URL error %s downloading %s.' % ( + error_message, unicode_from_os(e.reason), url) + + except (ConnectionError): + # Handle broken pipes/reset connections by creating a new opener, and + # thus getting new handlers and a new connection + error_string = u'Connection went away while trying to download %s, trying again' % url + if debug: + console_write(error_string, True) + + self.opener = None + self.setup_opener(url, timeout) + tries += 1 + + continue + + break + + raise DownloaderException(error_string) + + def get_handler(self): + """ + Get the HTTPHandler object for the current connection + """ + + if not self.opener: + return None + + for handler in self.opener.handlers: + if isinstance(handler, ValidatingHTTPSHandler) or isinstance(handler, DebuggableHTTPHandler): + return handler + + def setup_opener(self, url, timeout): + """ + Sets up a urllib OpenerDirector to be used for requests. There is a + fair amount of custom urllib code in Package Control, and part of it + is to handle proxies and keep-alives. Creating an opener the way + below is because the handlers have been customized to send the + "Connection: Keep-Alive" header and hold onto connections so they + can be re-used. + + :param url: + The URL to download + + :param timeout: + The int number of seconds to set the timeout to + """ + + if not self.opener: + http_proxy = self.settings.get('http_proxy') + https_proxy = self.settings.get('https_proxy') + if http_proxy or https_proxy: + proxies = {} + if http_proxy: + proxies['http'] = http_proxy + if https_proxy: + proxies['https'] = https_proxy + proxy_handler = ProxyHandler(proxies) + else: + proxy_handler = ProxyHandler() + + password_manager = HTTPPasswordMgrWithDefaultRealm() + proxy_username = self.settings.get('proxy_username') + proxy_password = self.settings.get('proxy_password') + if proxy_username and proxy_password: + if http_proxy: + password_manager.add_password(None, http_proxy, proxy_username, + proxy_password) + if https_proxy: + password_manager.add_password(None, https_proxy, proxy_username, + proxy_password) + + handlers = [proxy_handler] + + basic_auth_handler = ProxyBasicAuthHandler(password_manager) + digest_auth_handler = ProxyDigestAuthHandler(password_manager) + handlers.extend([digest_auth_handler, basic_auth_handler]) + + debug = self.settings.get('debug') + + if debug: + console_write(u"Urllib Debug Proxy", True) + console_write(u" http_proxy: %s" % http_proxy) + console_write(u" https_proxy: %s" % https_proxy) + console_write(u" proxy_username: %s" % proxy_username) + console_write(u" proxy_password: %s" % proxy_password) + + secure_url_match = re.match('^https://([^/]+)', url) + if secure_url_match != None: + secure_domain = secure_url_match.group(1) + bundle_path = self.check_certs(secure_domain, timeout) + bundle_path = bundle_path.encode(sys.getfilesystemencoding()) + handlers.append(ValidatingHTTPSHandler(ca_certs=bundle_path, + debug=debug, passwd=password_manager, + user_agent=self.settings.get('user_agent'))) + else: + handlers.append(DebuggableHTTPHandler(debug=debug, + passwd=password_manager)) + self.opener = build_opener(*handlers) + + def supports_ssl(self): + """ + Indicates if the object can handle HTTPS requests + + :return: + If the object supports HTTPS requests + """ + return 'ssl' in sys.modules and hasattr(urllib_compat, 'HTTPSHandler') diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/wget_downloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/wget_downloader.py new file mode 100644 index 0000000..fb83d1b --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/wget_downloader.py @@ -0,0 +1,347 @@ +import tempfile +import re +import os + +from ..console_write import console_write +from ..unicode import unicode_from_os +from ..open_compat import open_compat, read_compat +from .cli_downloader import CliDownloader +from .non_http_error import NonHttpError +from .non_clean_exit_error import NonCleanExitError +from .rate_limit_exception import RateLimitException +from .downloader_exception import DownloaderException +from .cert_provider import CertProvider +from .decoding_downloader import DecodingDownloader +from .limiting_downloader import LimitingDownloader +from .caching_downloader import CachingDownloader + + +class WgetDownloader(CliDownloader, CertProvider, DecodingDownloader, LimitingDownloader, CachingDownloader): + """ + A downloader that uses the command line program wget + + :param settings: + A dict of the various Package Control settings. The Sublime Text + Settings API is not used because this code is run in a thread. + + :raises: + BinaryNotFoundError: when wget can not be found + """ + + def __init__(self, settings): + self.settings = settings + self.debug = settings.get('debug') + self.wget = self.find_binary('wget') + + def close(self): + """ + No-op for compatibility with UrllibDownloader and WinINetDownloader + """ + + pass + + def download(self, url, error_message, timeout, tries, prefer_cached=False): + """ + Downloads a URL and returns the contents + + :param url: + The URL to download + + :param error_message: + A string to include in the console error that is printed + when an error occurs + + :param timeout: + The int number of seconds to set the timeout to + + :param tries: + The int number of times to try and download the URL in the case of + a timeout or HTTP 503 error + + :param prefer_cached: + If a cached version should be returned instead of trying a new request + + :raises: + NoCaCertException: when no CA certs can be found for the url + RateLimitException: when a rate limit is hit + DownloaderException: when any other download error occurs + + :return: + The string contents of the URL + """ + + if prefer_cached: + cached = self.retrieve_cached(url) + if cached: + return cached + + self.tmp_file = tempfile.NamedTemporaryFile().name + command = [self.wget, '--connect-timeout=' + str(int(timeout)), '-o', + self.tmp_file, '-O', '-', '-U', self.settings.get('user_agent')] + + request_headers = { + # Don't be alarmed if the response from the server does not select + # one of these since the server runs a relatively new version of + # OpenSSL which supports compression on the SSL layer, and Apache + # will use that instead of HTTP-level encoding. + 'Accept-Encoding': 'gzip,deflate' + } + request_headers = self.add_conditional_headers(url, request_headers) + + for name, value in request_headers.items(): + command.extend(['--header', "%s: %s" % (name, value)]) + + secure_url_match = re.match('^https://([^/]+)', url) + if secure_url_match != None: + secure_domain = secure_url_match.group(1) + bundle_path = self.check_certs(secure_domain, timeout) + command.append(u'--ca-certificate=' + bundle_path) + + if self.debug: + command.append('-d') + else: + command.append('-S') + + http_proxy = self.settings.get('http_proxy') + https_proxy = self.settings.get('https_proxy') + proxy_username = self.settings.get('proxy_username') + proxy_password = self.settings.get('proxy_password') + + if proxy_username: + command.append(u"--proxy-user=%s" % proxy_username) + if proxy_password: + command.append(u"--proxy-password=%s" % proxy_password) + + if self.debug: + console_write(u"Wget Debug Proxy", True) + console_write(u" http_proxy: %s" % http_proxy) + console_write(u" https_proxy: %s" % https_proxy) + console_write(u" proxy_username: %s" % proxy_username) + console_write(u" proxy_password: %s" % proxy_password) + + command.append(url) + + if http_proxy: + os.putenv('http_proxy', http_proxy) + if https_proxy: + os.putenv('https_proxy', https_proxy) + + error_string = None + while tries > 0: + tries -= 1 + try: + result = self.execute(command) + + general, headers = self.parse_output() + encoding = headers.get('content-encoding') + if encoding: + result = self.decode_response(encoding, result) + + result = self.cache_result('get', url, general['status'], + headers, result) + + return result + + except (NonCleanExitError) as e: + + try: + general, headers = self.parse_output() + self.handle_rate_limit(headers, url) + + if general['status'] == 304: + return self.cache_result('get', url, general['status'], + headers, None) + + if general['status'] == 503 and tries != 0: + # GitHub and BitBucket seem to rate limit via 503 + error_string = u'Downloading %s was rate limited' % url + if tries: + error_string += ', trying again' + if self.debug: + console_write(error_string, True) + continue + + download_error = 'HTTP error %s' % general['status'] + + except (NonHttpError) as e: + + download_error = unicode_from_os(e) + + # GitHub and BitBucket seem to time out a lot + if download_error.find('timed out') != -1: + error_string = u'Downloading %s timed out' % url + if tries: + error_string += ', trying again' + if self.debug: + console_write(error_string, True) + continue + + error_string = u'%s %s downloading %s.' % (error_message, download_error, url) + + break + + raise DownloaderException(error_string) + + def supports_ssl(self): + """ + Indicates if the object can handle HTTPS requests + + :return: + If the object supports HTTPS requests + """ + + return True + + def parse_output(self): + """ + Parses the wget output file, prints debug information and returns headers + + :return: + A tuple of (general, headers) where general is a dict with the keys: + `version` - HTTP version number (string) + `status` - HTTP status code (integer) + `message` - HTTP status message (string) + And headers is a dict with the keys being lower-case version of the + HTTP header names. + """ + + with open_compat(self.tmp_file, 'r') as f: + output = read_compat(f).splitlines() + self.clean_tmp_file() + + error = None + header_lines = [] + if self.debug: + section = 'General' + last_section = None + for line in output: + if section == 'General': + if self.skippable_line(line): + continue + + # Skip blank lines + if line.strip() == '': + continue + + # Error lines + if line[0:5] == 'wget:': + error = line[5:].strip() + if line[0:7] == 'failed:': + error = line[7:].strip() + + if line == '---request begin---': + section = 'Write' + continue + elif line == '---request end---': + section = 'General' + continue + elif line == '---response begin---': + section = 'Read' + continue + elif line == '---response end---': + section = 'General' + continue + + if section != last_section: + console_write(u"Wget HTTP Debug %s" % section, True) + + if section == 'Read': + header_lines.append(line) + + console_write(u' ' + line) + last_section = section + + else: + for line in output: + if self.skippable_line(line): + continue + + # Check the resolving and connecting to lines for errors + if re.match('(Resolving |Connecting to )', line): + failed_match = re.search(' failed: (.*)$', line) + if failed_match: + error = failed_match.group(1).strip() + + # Error lines + if line[0:5] == 'wget:': + error = line[5:].strip() + if line[0:7] == 'failed:': + error = line[7:].strip() + + if line[0:2] == ' ': + header_lines.append(line.lstrip()) + + if error: + raise NonHttpError(error) + + return self.parse_headers(header_lines) + + def skippable_line(self, line): + """ + Determines if a debug line is skippable - usually because of extraneous + or duplicate information. + + :param line: + The debug line to check + + :return: + True if the line is skippable, otherwise None + """ + + # Skip date lines + if re.match('--\d{4}-\d{2}-\d{2}', line): + return True + if re.match('\d{4}-\d{2}-\d{2}', line): + return True + # Skip HTTP status code lines since we already have that info + if re.match('\d{3} ', line): + return True + # Skip Saving to and progress lines + if re.match('(Saving to:|\s*\d+K)', line): + return True + # Skip notice about ignoring body on HTTP error + if re.match('Skipping \d+ byte', line): + return True + + def parse_headers(self, output=None): + """ + Parses HTTP headers into two dict objects + + :param output: + An array of header lines, if None, loads from temp output file + + :return: + A tuple of (general, headers) where general is a dict with the keys: + `version` - HTTP version number (string) + `status` - HTTP status code (integer) + `message` - HTTP status message (string) + And headers is a dict with the keys being lower-case version of the + HTTP header names. + """ + + if not output: + with open_compat(self.tmp_file, 'r') as f: + output = read_compat(f).splitlines() + self.clean_tmp_file() + + general = { + 'version': '0.9', + 'status': 200, + 'message': 'OK' + } + headers = {} + for line in output: + # When using the -S option, headers have two spaces before them, + # additionally, valid headers won't have spaces, so this is always + # a safe operation to perform + line = line.lstrip() + if line.find('HTTP/') == 0: + match = re.match('HTTP/(\d\.\d)\s+(\d+)(?:\s+(.*))?$', line) + general['version'] = match.group(1) + general['status'] = int(match.group(2)) + general['message'] = match.group(3) or '' + else: + name, value = line.split(':', 1) + headers[name.lower()] = value.strip() + + return (general, headers) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/wininet_downloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/wininet_downloader.py new file mode 100644 index 0000000..7134db9 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/downloaders/wininet_downloader.py @@ -0,0 +1,652 @@ +from ctypes import windll, wintypes +import ctypes +import time +import re +import datetime +import struct +import locale + +wininet = windll.wininet + +try: + # Python 3 + from urllib.parse import urlparse +except (ImportError): + # Python 2 + from urlparse import urlparse + +from ..console_write import console_write +from ..unicode import unicode_from_os +from .non_http_error import NonHttpError +from .http_error import HttpError +from .rate_limit_exception import RateLimitException +from .downloader_exception import DownloaderException +from .decoding_downloader import DecodingDownloader +from .limiting_downloader import LimitingDownloader +from .caching_downloader import CachingDownloader + + +class WinINetDownloader(DecodingDownloader, LimitingDownloader, CachingDownloader): + """ + A downloader that uses the Windows WinINet DLL to perform downloads. This + has the benefit of utilizing system-level proxy configuration and CA certs. + + :param settings: + A dict of the various Package Control settings. The Sublime Text + Settings API is not used because this code is run in a thread. + """ + + # General constants + ERROR_INSUFFICIENT_BUFFER = 122 + + # InternetOpen constants + INTERNET_OPEN_TYPE_PRECONFIG = 0 + + # InternetConnect constants + INTERNET_SERVICE_HTTP = 3 + INTERNET_FLAG_EXISTING_CONNECT = 0x20000000 + INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS = 0x00004000 + + # InternetSetOption constants + INTERNET_OPTION_CONNECT_TIMEOUT = 2 + INTERNET_OPTION_SEND_TIMEOUT = 5 + INTERNET_OPTION_RECEIVE_TIMEOUT = 6 + + # InternetQueryOption constants + INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT = 32 + INTERNET_OPTION_PROXY = 38 + INTERNET_OPTION_PROXY_USERNAME = 43 + INTERNET_OPTION_PROXY_PASSWORD = 44 + INTERNET_OPTION_CONNECTED_STATE = 50 + + # HttpOpenRequest constants + INTERNET_FLAG_KEEP_CONNECTION = 0x00400000 + INTERNET_FLAG_RELOAD = 0x80000000 + INTERNET_FLAG_NO_CACHE_WRITE = 0x04000000 + INTERNET_FLAG_PRAGMA_NOCACHE = 0x00000100 + INTERNET_FLAG_SECURE = 0x00800000 + + # HttpQueryInfo constants + HTTP_QUERY_RAW_HEADERS_CRLF = 22 + + # InternetConnectedState constants + INTERNET_STATE_CONNECTED = 1 + INTERNET_STATE_DISCONNECTED = 2 + INTERNET_STATE_DISCONNECTED_BY_USER = 0x10 + INTERNET_STATE_IDLE = 0x100 + INTERNET_STATE_BUSY = 0x200 + + + def __init__(self, settings): + self.settings = settings + self.debug = settings.get('debug') + self.network_connection = None + self.tcp_connection = None + self.use_count = 0 + self.hostname = None + self.port = None + self.scheme = None + self.was_offline = None + + def close(self): + """ + Closes any persistent/open connections + """ + + closed = False + changed_state_back = False + + if self.tcp_connection: + wininet.InternetCloseHandle(self.tcp_connection) + self.tcp_connection = None + closed = True + + if self.network_connection: + wininet.InternetCloseHandle(self.network_connection) + self.network_connection = None + closed = True + + if self.was_offline: + dw_connected_state = wintypes.DWORD(self.INTERNET_STATE_DISCONNECTED_BY_USER) + dw_flags = wintypes.DWORD(0) + connected_info = InternetConnectedInfo(dw_connected_state, dw_flags) + wininet.InternetSetOptionA(None, + self.INTERNET_OPTION_CONNECTED_STATE, ctypes.byref(connected_info), ctypes.sizeof(connected_info)) + changed_state_back = True + + if self.debug: + s = '' if self.use_count == 1 else 's' + console_write(u"WinINet %s Debug General" % self.scheme.upper(), True) + console_write(u" Closing connection to %s on port %s after %s request%s" % ( + self.hostname, self.port, self.use_count, s)) + if changed_state_back: + console_write(u" Changed Internet Explorer back to Work Offline") + + self.hostname = None + self.port = None + self.scheme = None + self.use_count = 0 + self.was_offline = None + + def download(self, url, error_message, timeout, tries, prefer_cached=False): + """ + Downloads a URL and returns the contents + + :param url: + The URL to download + + :param error_message: + A string to include in the console error that is printed + when an error occurs + + :param timeout: + The int number of seconds to set the timeout to + + :param tries: + The int number of times to try and download the URL in the case of + a timeout or HTTP 503 error + + :param prefer_cached: + If a cached version should be returned instead of trying a new request + + :raises: + RateLimitException: when a rate limit is hit + DownloaderException: when any other download error occurs + + :return: + The string contents of the URL + """ + + if prefer_cached: + cached = self.retrieve_cached(url) + if cached: + return cached + + url_info = urlparse(url) + + if not url_info.port: + port = 443 if url_info.scheme == 'https' else 80 + hostname = url_info.netloc + else: + port = url_info.port + hostname = url_info.hostname + + path = url_info.path + if url_info.params: + path += ';' + url_info.params + if url_info.query: + path += '?' + url_info.query + + request_headers = { + 'Accept-Encoding': 'gzip,deflate' + } + request_headers = self.add_conditional_headers(url, request_headers) + + created_connection = False + # If we switched Internet Explorer out of "Work Offline" mode + changed_to_online = False + + # If the user is requesting a connection to another server, close the connection + if (self.hostname and self.hostname != hostname) or (self.port and self.port != port): + self.close() + + # Reset the error info to a known clean state + ctypes.windll.kernel32.SetLastError(0) + + # Save the internet setup in the class for re-use + if not self.tcp_connection: + created_connection = True + + # Connect to the internet if necessary + state = self.read_option(None, self.INTERNET_OPTION_CONNECTED_STATE) + state = ord(state) + if state & self.INTERNET_STATE_DISCONNECTED or state & self.INTERNET_STATE_DISCONNECTED_BY_USER: + # Track the previous state so we can go back once complete + self.was_offline = True + + dw_connected_state = wintypes.DWORD(self.INTERNET_STATE_CONNECTED) + dw_flags = wintypes.DWORD(0) + connected_info = InternetConnectedInfo(dw_connected_state, dw_flags) + wininet.InternetSetOptionA(None, + self.INTERNET_OPTION_CONNECTED_STATE, ctypes.byref(connected_info), ctypes.sizeof(connected_info)) + changed_to_online = True + + self.network_connection = wininet.InternetOpenW(self.settings.get('user_agent'), + self.INTERNET_OPEN_TYPE_PRECONFIG, None, None, 0) + + if not self.network_connection: + error_string = u'%s %s during network phase of downloading %s.' % (error_message, self.extract_error(), url) + raise DownloaderException(error_string) + + win_timeout = wintypes.DWORD(int(timeout) * 1000) + # Apparently INTERNET_OPTION_CONNECT_TIMEOUT just doesn't work, leaving it in hoping they may fix in the future + wininet.InternetSetOptionA(self.network_connection, + self.INTERNET_OPTION_CONNECT_TIMEOUT, win_timeout, ctypes.sizeof(win_timeout)) + wininet.InternetSetOptionA(self.network_connection, + self.INTERNET_OPTION_SEND_TIMEOUT, win_timeout, ctypes.sizeof(win_timeout)) + wininet.InternetSetOptionA(self.network_connection, + self.INTERNET_OPTION_RECEIVE_TIMEOUT, win_timeout, ctypes.sizeof(win_timeout)) + + # Don't allow HTTPS sites to redirect to HTTP sites + tcp_flags = self.INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS + # Try to re-use an existing connection to the server + tcp_flags |= self.INTERNET_FLAG_EXISTING_CONNECT + self.tcp_connection = wininet.InternetConnectW(self.network_connection, + hostname, port, None, None, self.INTERNET_SERVICE_HTTP, tcp_flags, 0) + + if not self.tcp_connection: + error_string = u'%s %s during connection phase of downloading %s.' % (error_message, self.extract_error(), url) + raise DownloaderException(error_string) + + # Normally the proxy info would come from IE, but this allows storing it in + # the Package Control settings file. + proxy_username = self.settings.get('proxy_username') + proxy_password = self.settings.get('proxy_password') + if proxy_username and proxy_password: + username = ctypes.c_wchar_p(proxy_username) + password = ctypes.c_wchar_p(proxy_password) + wininet.InternetSetOptionW(self.tcp_connection, + self.INTERNET_OPTION_PROXY_USERNAME, ctypes.cast(username, ctypes.c_void_p), len(proxy_username)) + wininet.InternetSetOptionW(self.tcp_connection, + self.INTERNET_OPTION_PROXY_PASSWORD, ctypes.cast(password, ctypes.c_void_p), len(proxy_password)) + + self.hostname = hostname + self.port = port + self.scheme = url_info.scheme + + else: + if self.debug: + console_write(u"WinINet %s Debug General" % self.scheme.upper(), True) + console_write(u" Re-using connection to %s on port %s for request #%s" % ( + self.hostname, self.port, self.use_count)) + + error_string = None + while tries > 0: + tries -= 1 + try: + http_connection = None + + # Keep-alive for better performance + http_flags = self.INTERNET_FLAG_KEEP_CONNECTION + # Prevent caching/retrieving from cache + http_flags |= self.INTERNET_FLAG_RELOAD + http_flags |= self.INTERNET_FLAG_NO_CACHE_WRITE + http_flags |= self.INTERNET_FLAG_PRAGMA_NOCACHE + # Use SSL + if self.scheme == 'https': + http_flags |= self.INTERNET_FLAG_SECURE + + http_connection = wininet.HttpOpenRequestW(self.tcp_connection, u'GET', path, u'HTTP/1.1', None, None, http_flags, 0) + if not http_connection: + error_string = u'%s %s during HTTP connection phase of downloading %s.' % (error_message, self.extract_error(), url) + raise DownloaderException(error_string) + + request_header_lines = [] + for header, value in request_headers.items(): + request_header_lines.append(u"%s: %s" % (header, value)) + request_header_lines = u"\r\n".join(request_header_lines) + + success = wininet.HttpSendRequestW(http_connection, request_header_lines, len(request_header_lines), None, 0) + + if not success: + error_string = u'%s %s during HTTP write phase of downloading %s.' % (error_message, self.extract_error(), url) + raise DownloaderException(error_string) + + # If we try to query before here, the proxy info will not be available to the first request + if self.debug: + proxy_struct = self.read_option(self.network_connection, self.INTERNET_OPTION_PROXY) + proxy = '' + if proxy_struct.lpszProxy: + proxy = proxy_struct.lpszProxy.decode('cp1252') + proxy_bypass = '' + if proxy_struct.lpszProxyBypass: + proxy_bypass = proxy_struct.lpszProxyBypass.decode('cp1252') + + proxy_username = self.read_option(self.tcp_connection, self.INTERNET_OPTION_PROXY_USERNAME) + proxy_password = self.read_option(self.tcp_connection, self.INTERNET_OPTION_PROXY_PASSWORD) + + console_write(u"WinINet Debug Proxy", True) + console_write(u" proxy: %s" % proxy) + console_write(u" proxy bypass: %s" % proxy_bypass) + console_write(u" proxy username: %s" % proxy_username) + console_write(u" proxy password: %s" % proxy_password) + + self.use_count += 1 + + if self.debug and created_connection: + if self.scheme == 'https': + cert_struct = self.read_option(http_connection, self.INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT) + + if cert_struct.lpszIssuerInfo: + issuer_info = cert_struct.lpszIssuerInfo.decode('cp1252') + issuer_parts = issuer_info.split("\r\n") + else: + issuer_parts = ['No issuer info'] + + if cert_struct.lpszSubjectInfo: + subject_info = cert_struct.lpszSubjectInfo.decode('cp1252') + subject_parts = subject_info.split("\r\n") + else: + subject_parts = ["No subject info"] + + common_name = subject_parts[-1] + + if cert_struct.ftStart.dwLowDateTime != 0 and cert_struct.ftStart.dwHighDateTime != 0: + issue_date = self.convert_filetime_to_datetime(cert_struct.ftStart) + issue_date = issue_date.strftime('%a, %d %b %Y %H:%M:%S GMT') + else: + issue_date = u"No issue date" + + if cert_struct.ftExpiry.dwLowDateTime != 0 and cert_struct.ftExpiry.dwHighDateTime != 0: + expiration_date = self.convert_filetime_to_datetime(cert_struct.ftExpiry) + expiration_date = expiration_date.strftime('%a, %d %b %Y %H:%M:%S GMT') + else: + expiration_date = u"No expiration date" + + console_write(u"WinINet HTTPS Debug General", True) + if changed_to_online: + console_write(u" Internet Explorer was set to Work Offline, temporarily going online") + console_write(u" Server SSL Certificate:") + console_write(u" subject: %s" % ", ".join(subject_parts)) + console_write(u" issuer: %s" % ", ".join(issuer_parts)) + console_write(u" common name: %s" % common_name) + console_write(u" issue date: %s" % issue_date) + console_write(u" expire date: %s" % expiration_date) + + elif changed_to_online: + console_write(u"WinINet HTTP Debug General", True) + console_write(u" Internet Explorer was set to Work Offline, temporarily going online") + + if self.debug: + console_write(u"WinINet %s Debug Write" % self.scheme.upper(), True) + # Add in some known headers that WinINet sends since we can't get the real list + console_write(u" GET %s HTTP/1.1" % path) + for header, value in request_headers.items(): + console_write(u" %s: %s" % (header, value)) + console_write(u" User-Agent: %s" % self.settings.get('user_agent')) + console_write(u" Host: %s" % hostname) + console_write(u" Connection: Keep-Alive") + console_write(u" Cache-Control: no-cache") + + header_buffer_size = 8192 + + try_again = True + while try_again: + try_again = False + + to_read_was_read = wintypes.DWORD(header_buffer_size) + headers_buffer = ctypes.create_string_buffer(header_buffer_size) + + success = wininet.HttpQueryInfoA(http_connection, self.HTTP_QUERY_RAW_HEADERS_CRLF, ctypes.byref(headers_buffer), ctypes.byref(to_read_was_read), None) + if not success: + if ctypes.GetLastError() != self.ERROR_INSUFFICIENT_BUFFER: + error_string = u'%s %s during header read phase of downloading %s.' % (error_message, self.extract_error(), url) + raise DownloaderException(error_string) + # The error was a buffer that was too small, so try again + header_buffer_size = to_read_was_read.value + try_again = True + continue + + headers = b'' + if to_read_was_read.value > 0: + headers += headers_buffer.raw[:to_read_was_read.value] + headers = headers.decode('iso-8859-1').rstrip("\r\n").split("\r\n") + + if self.debug: + console_write(u"WinINet %s Debug Read" % self.scheme.upper(), True) + for header in headers: + console_write(u" %s" % header) + + buffer_length = 65536 + output_buffer = ctypes.create_string_buffer(buffer_length) + bytes_read = wintypes.DWORD() + + result = b'' + try_again = True + while try_again: + try_again = False + wininet.InternetReadFile(http_connection, output_buffer, buffer_length, ctypes.byref(bytes_read)) + if bytes_read.value > 0: + result += output_buffer.raw[:bytes_read.value] + try_again = True + + general, headers = self.parse_headers(headers) + self.handle_rate_limit(headers, url) + + if general['status'] == 503 and tries != 0: + # GitHub and BitBucket seem to rate limit via 503 + error_string = u'Downloading %s was rate limited' % url + if tries: + error_string += ', trying again' + if self.debug: + console_write(error_string, True) + continue + + encoding = headers.get('content-encoding') + if encoding: + result = self.decode_response(encoding, result) + + result = self.cache_result('get', url, general['status'], + headers, result) + + if general['status'] not in [200, 304]: + raise HttpError("HTTP error %s" % general['status'], general['status']) + + return result + + except (NonHttpError, HttpError) as e: + + # GitHub and BitBucket seem to time out a lot + if str(e).find('timed out') != -1: + error_string = u'Downloading %s timed out' % url + if tries: + error_string += ', trying again' + if self.debug: + console_write(error_string, True) + continue + + error_string = u'%s %s downloading %s.' % (error_message, e, url) + + finally: + if http_connection: + wininet.InternetCloseHandle(http_connection) + + break + + raise DownloaderException(error_string) + + def convert_filetime_to_datetime(self, filetime): + """ + Windows returns times as 64-bit unsigned longs that are the number + of hundreds of nanoseconds since Jan 1 1601. This converts it to + a datetime object. + + :param filetime: + A FileTime struct object + + :return: + A (UTC) datetime object + """ + + hundreds_nano_seconds = struct.unpack('>Q', struct.pack('>LL', filetime.dwHighDateTime, filetime.dwLowDateTime))[0] + seconds_since_1601 = hundreds_nano_seconds / 10000000 + epoch_seconds = seconds_since_1601 - 11644473600 # Seconds from Jan 1 1601 to Jan 1 1970 + return datetime.datetime.fromtimestamp(epoch_seconds) + + def extract_error(self): + """ + Retrieves and formats an error from WinINet + + :return: + A string with a nice description of the error + """ + + error_num = ctypes.GetLastError() + raw_error_string = ctypes.FormatError(error_num) + + error_string = unicode_from_os(raw_error_string) + + # Try to fill in some known errors + if error_string == u"": + error_lookup = { + 12007: u'host not found', + 12029: u'connection refused', + 12057: u'error checking for server certificate revocation', + 12169: u'invalid secure certificate', + 12157: u'secure channel error, server not providing SSL', + 12002: u'operation timed out' + } + if error_num in error_lookup: + error_string = error_lookup[error_num] + + if error_string == u"": + return u"(errno %s)" % error_num + + error_string = error_string[0].upper() + error_string[1:] + return u"%s (errno %s)" % (error_string, error_num) + + def supports_ssl(self): + """ + Indicates if the object can handle HTTPS requests + + :return: + If the object supports HTTPS requests + """ + + return True + + def read_option(self, handle, option): + """ + Reads information about the internet connection, which may be a string or struct + + :param handle: + The handle to query for the info + + :param option: + The (int) option to get + + :return: + A string, or one of the InternetCertificateInfo or InternetProxyInfo structs + """ + + option_buffer_size = 8192 + try_again = True + + while try_again: + try_again = False + + to_read_was_read = wintypes.DWORD(option_buffer_size) + option_buffer = ctypes.create_string_buffer(option_buffer_size) + ref = ctypes.byref(option_buffer) + + success = wininet.InternetQueryOptionA(handle, option, ref, ctypes.byref(to_read_was_read)) + if not success: + if ctypes.GetLastError() != self.ERROR_INSUFFICIENT_BUFFER: + raise NonHttpError(self.extract_error()) + # The error was a buffer that was too small, so try again + option_buffer_size = to_read_was_read.value + try_again = True + continue + + if option == self.INTERNET_OPTION_SECURITY_CERTIFICATE_STRUCT: + length = min(len(option_buffer), ctypes.sizeof(InternetCertificateInfo)) + cert_info = InternetCertificateInfo() + ctypes.memmove(ctypes.addressof(cert_info), option_buffer, length) + return cert_info + elif option == self.INTERNET_OPTION_PROXY: + length = min(len(option_buffer), ctypes.sizeof(InternetProxyInfo)) + proxy_info = InternetProxyInfo() + ctypes.memmove(ctypes.addressof(proxy_info), option_buffer, length) + return proxy_info + else: + option = b'' + if to_read_was_read.value > 0: + option += option_buffer.raw[:to_read_was_read.value] + return option.decode('cp1252').rstrip("\x00") + + def parse_headers(self, output): + """ + Parses HTTP headers into two dict objects + + :param output: + An array of header lines + + :return: + A tuple of (general, headers) where general is a dict with the keys: + `version` - HTTP version number (string) + `status` - HTTP status code (integer) + `message` - HTTP status message (string) + And headers is a dict with the keys being lower-case version of the + HTTP header names. + """ + + general = { + 'version': '0.9', + 'status': 200, + 'message': 'OK' + } + headers = {} + for line in output: + line = line.lstrip() + if line.find('HTTP/') == 0: + match = re.match('HTTP/(\d\.\d)\s+(\d+)\s+(.*)$', line) + general['version'] = match.group(1) + general['status'] = int(match.group(2)) + general['message'] = match.group(3) + else: + name, value = line.split(':', 1) + headers[name.lower()] = value.strip() + + return (general, headers) + + +class FileTime(ctypes.Structure): + """ + A Windows struct used by InternetCertificateInfo for certificate + date information + """ + + _fields_ = [ + ("dwLowDateTime", wintypes.DWORD), + ("dwHighDateTime", wintypes.DWORD) + ] + + +class InternetCertificateInfo(ctypes.Structure): + """ + A Windows struct used to store information about an SSL certificate + """ + + _fields_ = [ + ("ftExpiry", FileTime), + ("ftStart", FileTime), + ("lpszSubjectInfo", ctypes.c_char_p), + ("lpszIssuerInfo", ctypes.c_char_p), + ("lpszProtocolName", ctypes.c_char_p), + ("lpszSignatureAlgName", ctypes.c_char_p), + ("lpszEncryptionAlgName", ctypes.c_char_p), + ("dwKeySize", wintypes.DWORD) + ] + + +class InternetProxyInfo(ctypes.Structure): + """ + A Windows struct usd to store information about the configured proxy server + """ + + _fields_ = [ + ("dwAccessType", wintypes.DWORD), + ("lpszProxy", ctypes.c_char_p), + ("lpszProxyBypass", ctypes.c_char_p) + ] + + +class InternetConnectedInfo(ctypes.Structure): + """ + A Windows struct usd to store information about the global internet connection state + """ + + _fields_ = [ + ("dwConnectedState", wintypes.DWORD), + ("dwFlags", wintypes.DWORD) + ] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/file_not_found_error.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/file_not_found_error.py new file mode 100644 index 0000000..3fd4da5 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/file_not_found_error.py @@ -0,0 +1,4 @@ +class FileNotFoundError(Exception): + """If a file is not found""" + + pass diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/__init__.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/__init__.py new file mode 100644 index 0000000..e3358df --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/__init__.py @@ -0,0 +1,65 @@ +import sys + +try: + # Python 2 + import urllib2 + import httplib + + # Monkey patch AbstractBasicAuthHandler to prevent infinite recursion + def non_recursive_http_error_auth_reqed(self, authreq, host, req, headers): + authreq = headers.get(authreq, None) + + if not hasattr(self, 'retried'): + self.retried = 0 + + if self.retried > 5: + raise urllib2.HTTPError(req.get_full_url(), 401, "basic auth failed", + headers, None) + else: + self.retried += 1 + + if authreq: + mo = urllib2.AbstractBasicAuthHandler.rx.search(authreq) + if mo: + scheme, quote, realm = mo.groups() + if scheme.lower() == 'basic': + return self.retry_http_basic_auth(host, req, realm) + + urllib2.AbstractBasicAuthHandler.http_error_auth_reqed = non_recursive_http_error_auth_reqed + + # Money patch urllib2.Request and httplib.HTTPConnection so that + # HTTPS proxies work in Python 2.6.1-2 + if sys.version_info < (2, 6, 3): + + urllib2.Request._tunnel_host = None + + def py268_set_proxy(self, host, type): + if self.type == 'https' and not self._tunnel_host: + self._tunnel_host = self.host + else: + self.type = type + # The _Request prefix is to handle python private name mangling + self._Request__r_host = self._Request__original + self.host = host + urllib2.Request.set_proxy = py268_set_proxy + + if sys.version_info < (2, 6, 5): + + def py268_set_tunnel(self, host, port=None, headers=None): + """ Sets up the host and the port for the HTTP CONNECT Tunnelling. + + The headers argument should be a mapping of extra HTTP headers + to send with the CONNECT request. + """ + self._tunnel_host = host + self._tunnel_port = port + if headers: + self._tunnel_headers = headers + else: + self._tunnel_headers.clear() + httplib.HTTPConnection._set_tunnel = py268_set_tunnel + + +except (ImportError): + # Python 3 does not need to be patched + pass diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_connection.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_connection.py new file mode 100644 index 0000000..e0044a9 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_connection.py @@ -0,0 +1,72 @@ +import os +import re +import socket + +try: + # Python 3 + from http.client import HTTPConnection + from urllib.error import URLError +except (ImportError): + # Python 2 + from httplib import HTTPConnection + from urllib2 import URLError + +from ..console_write import console_write +from .debuggable_http_response import DebuggableHTTPResponse + + +class DebuggableHTTPConnection(HTTPConnection): + """ + A custom HTTPConnection that formats debugging info for Sublime Text + """ + + response_class = DebuggableHTTPResponse + _debug_protocol = 'HTTP' + + def __init__(self, host, port=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, + **kwargs): + self.passwd = kwargs.get('passwd') + + # Python 2.6.1 on OS X 10.6 does not include these + self._tunnel_host = None + self._tunnel_port = None + self._tunnel_headers = {} + if 'debug' in kwargs and kwargs['debug']: + self.debuglevel = 5 + elif 'debuglevel' in kwargs: + self.debuglevel = kwargs['debuglevel'] + + HTTPConnection.__init__(self, host, port=port, timeout=timeout) + + def connect(self): + if self.debuglevel == -1: + console_write(u'Urllib %s Debug General' % self._debug_protocol, True) + console_write(u" Connecting to %s on port %s" % (self.host, self.port)) + HTTPConnection.connect(self) + + def send(self, string): + # We have to use a positive debuglevel to get it passed to the + # HTTPResponse object, however we don't want to use it because by + # default debugging prints to the stdout and we can't capture it, so + # we temporarily set it to -1 for the standard httplib code + reset_debug = False + if self.debuglevel == 5: + reset_debug = 5 + self.debuglevel = -1 + HTTPConnection.send(self, string) + if reset_debug or self.debuglevel == -1: + if len(string.strip()) > 0: + console_write(u'Urllib %s Debug Write' % self._debug_protocol, True) + for line in string.strip().splitlines(): + console_write(u' ' + line.decode('iso-8859-1')) + if reset_debug: + self.debuglevel = reset_debug + + def request(self, method, url, body=None, headers={}): + original_headers = headers.copy() + + # By default urllib2 and urllib.request override the Connection header, + # however, it is preferred to be able to re-use it + original_headers['Connection'] = 'Keep-Alive' + + HTTPConnection.request(self, method, url, body, original_headers) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_handler.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_handler.py new file mode 100644 index 0000000..ae4b8d1 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_handler.py @@ -0,0 +1,35 @@ +import sys + +try: + # Python 3 + from urllib.request import HTTPHandler +except (ImportError): + # Python 2 + from urllib2 import HTTPHandler + +from .debuggable_http_connection import DebuggableHTTPConnection +from .persistent_handler import PersistentHandler + + +class DebuggableHTTPHandler(PersistentHandler, HTTPHandler): + """ + A custom HTTPHandler that formats debugging info for Sublime Text + """ + + def __init__(self, debuglevel=0, debug=False, **kwargs): + # This is a special value that will not trigger the standard debug + # functionality, but custom code where we can format the output + if debug: + self._debuglevel = 5 + else: + self._debuglevel = debuglevel + self.passwd = kwargs.get('passwd') + + def http_open(self, req): + def http_class_wrapper(host, **kwargs): + kwargs['passwd'] = self.passwd + if 'debuglevel' not in kwargs: + kwargs['debuglevel'] = self._debuglevel + return DebuggableHTTPConnection(host, **kwargs) + + return self.do_open(http_class_wrapper, req) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_response.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_response.py new file mode 100644 index 0000000..2dd3af6 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_http_response.py @@ -0,0 +1,66 @@ +try: + # Python 3 + from http.client import HTTPResponse, IncompleteRead +except (ImportError): + # Python 2 + from httplib import HTTPResponse, IncompleteRead + +from ..console_write import console_write + + +class DebuggableHTTPResponse(HTTPResponse): + """ + A custom HTTPResponse that formats debugging info for Sublime Text + """ + + _debug_protocol = 'HTTP' + + def __init__(self, sock, debuglevel=0, method=None, **kwargs): + # We have to use a positive debuglevel to get it passed to here, + # however we don't want to use it because by default debugging prints + # to the stdout and we can't capture it, so we use a special -1 value + if debuglevel == 5: + debuglevel = -1 + HTTPResponse.__init__(self, sock, debuglevel=debuglevel, method=method) + + def begin(self): + return_value = HTTPResponse.begin(self) + if self.debuglevel == -1: + console_write(u'Urllib %s Debug Read' % self._debug_protocol, True) + + # Python 2 + if hasattr(self.msg, 'headers'): + headers = self.msg.headers + # Python 3 + else: + headers = [] + for header in self.msg: + headers.append("%s: %s" % (header, self.msg[header])) + + versions = { + 9: 'HTTP/0.9', + 10: 'HTTP/1.0', + 11: 'HTTP/1.1' + } + status_line = versions[self.version] + ' ' + str(self.status) + ' ' + self.reason + headers.insert(0, status_line) + for line in headers: + console_write(u" %s" % line.rstrip()) + return return_value + + def is_keep_alive(self): + # Python 2 + if hasattr(self.msg, 'headers'): + connection = self.msg.getheader('connection') + # Python 3 + else: + connection = self.msg['connection'] + if connection and connection.lower() == 'keep-alive': + return True + return False + + def read(self, *args): + try: + return HTTPResponse.read(self, *args) + except (IncompleteRead) as e: + return e.partial diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_https_response.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_https_response.py new file mode 100644 index 0000000..edc9fb0 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/debuggable_https_response.py @@ -0,0 +1,9 @@ +from .debuggable_http_response import DebuggableHTTPResponse + + +class DebuggableHTTPSResponse(DebuggableHTTPResponse): + """ + A version of DebuggableHTTPResponse that sets the debug protocol to HTTPS + """ + + _debug_protocol = 'HTTPS' diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/invalid_certificate_exception.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/invalid_certificate_exception.py new file mode 100644 index 0000000..2715707 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/invalid_certificate_exception.py @@ -0,0 +1,25 @@ +try: + # Python 3 + from http.client import HTTPException + from urllib.error import URLError +except (ImportError): + # Python 2 + from httplib import HTTPException + from urllib2 import URLError + + +class InvalidCertificateException(HTTPException, URLError): + """ + An exception for when an SSL certification is not valid for the URL + it was presented for. + """ + + def __init__(self, host, cert, reason): + HTTPException.__init__(self) + self.host = host + self.cert = cert + self.reason = reason + + def __str__(self): + return ('Host %s returned an invalid certificate (%s) %s\n' % + (self.host, self.reason, self.cert)) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/persistent_handler.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/persistent_handler.py new file mode 100644 index 0000000..4bfd3d7 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/persistent_handler.py @@ -0,0 +1,116 @@ +import sys +import socket + +try: + # Python 3 + from urllib.error import URLError +except ImportError: + # Python 2 + from urllib2 import URLError + from urllib import addinfourl + +from ..console_write import console_write + + +class PersistentHandler: + connection = None + use_count = 0 + + def close(self): + if self.connection: + if self._debuglevel == 5: + s = '' if self.use_count == 1 else 's' + console_write(u"Urllib %s Debug General" % self.connection._debug_protocol, True) + console_write(u" Closing connection to %s on port %s after %s request%s" % ( + self.connection.host, self.connection.port, self.use_count, s)) + self.connection.close() + self.connection = None + self.use_count = 0 + + def do_open(self, http_class, req): + # Large portions from Python 3.3 Lib/urllib/request.py and + # Python 2.6 Lib/urllib2.py + + if sys.version_info >= (3,): + host = req.host + else: + host = req.get_host() + + if not host: + raise URLError('no host given') + + if self.connection and self.connection.host != host: + self.close() + + # Re-use the connection if possible + self.use_count += 1 + if not self.connection: + h = http_class(host, timeout=req.timeout) + else: + h = self.connection + if self._debuglevel == 5: + console_write(u"Urllib %s Debug General" % h._debug_protocol, True) + console_write(u" Re-using connection to %s on port %s for request #%s" % ( + h.host, h.port, self.use_count)) + + if sys.version_info >= (3,): + headers = dict(req.unredirected_hdrs) + headers.update(dict((k, v) for k, v in req.headers.items() + if k not in headers)) + headers = dict((name.title(), val) for name, val in headers.items()) + + else: + h.set_debuglevel(self._debuglevel) + + headers = dict(req.headers) + headers.update(req.unredirected_hdrs) + headers = dict( + (name.title(), val) for name, val in headers.items()) + + if req._tunnel_host and not self.connection: + tunnel_headers = {} + proxy_auth_hdr = "Proxy-Authorization" + if proxy_auth_hdr in headers: + tunnel_headers[proxy_auth_hdr] = headers[proxy_auth_hdr] + del headers[proxy_auth_hdr] + + if sys.version_info >= (3,): + h.set_tunnel(req._tunnel_host, headers=tunnel_headers) + else: + h._set_tunnel(req._tunnel_host, headers=tunnel_headers) + + try: + if sys.version_info >= (3,): + h.request(req.get_method(), req.selector, req.data, headers) + else: + h.request(req.get_method(), req.get_selector(), req.data, headers) + except socket.error as err: # timeout error + h.close() + raise URLError(err) + else: + r = h.getresponse() + + # Keep the connection around for re-use + if r.is_keep_alive(): + self.connection = h + else: + if self._debuglevel == 5: + s = '' if self.use_count == 1 else 's' + console_write(u"Urllib %s Debug General" % h._debug_protocol, True) + console_write(u" Closing connection to %s on port %s after %s request%s" % ( + h.host, h.port, self.use_count, s)) + self.use_count = 0 + self.connection = None + + if sys.version_info >= (3,): + r.url = req.get_full_url() + r.msg = r.reason + return r + + r.recv = r.read + fp = socket._fileobject(r, close=True) + + resp = addinfourl(fp, r.msg, req.get_full_url()) + resp.code = r.status + resp.msg = r.reason + return resp diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/validating_https_connection.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/validating_https_connection.py new file mode 100644 index 0000000..a01afdb --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/validating_https_connection.py @@ -0,0 +1,345 @@ +import re +import socket +import base64 +import hashlib +import os +import sys + +try: + # Python 3 + from http.client import HTTPS_PORT + from urllib.request import parse_keqv_list, parse_http_list +except (ImportError): + # Python 2 + from httplib import HTTPS_PORT + from urllib2 import parse_keqv_list, parse_http_list + +from ..console_write import console_write +from .debuggable_https_response import DebuggableHTTPSResponse +from .debuggable_http_connection import DebuggableHTTPConnection +from .invalid_certificate_exception import InvalidCertificateException + + +# The following code is wrapped in a try because the Linux versions of Sublime +# Text do not include the ssl module due to the fact that different distros +# have different versions +try: + import ssl + + class ValidatingHTTPSConnection(DebuggableHTTPConnection): + """ + A custom HTTPConnection class that validates SSL certificates, and + allows proxy authentication for HTTPS connections. + """ + + default_port = HTTPS_PORT + + response_class = DebuggableHTTPSResponse + _debug_protocol = 'HTTPS' + + def __init__(self, host, port=None, key_file=None, cert_file=None, + ca_certs=None, **kwargs): + passed_args = {} + if 'timeout' in kwargs: + passed_args['timeout'] = kwargs['timeout'] + if 'debug' in kwargs: + passed_args['debug'] = kwargs['debug'] + DebuggableHTTPConnection.__init__(self, host, port, **passed_args) + + self.passwd = kwargs.get('passwd') + self.key_file = key_file + self.cert_file = cert_file + self.ca_certs = ca_certs + if 'user_agent' in kwargs: + self.user_agent = kwargs['user_agent'] + if self.ca_certs: + self.cert_reqs = ssl.CERT_REQUIRED + else: + self.cert_reqs = ssl.CERT_NONE + + def get_valid_hosts_for_cert(self, cert): + """ + Returns a list of valid hostnames for an SSL certificate + + :param cert: A dict from SSLSocket.getpeercert() + + :return: An array of hostnames + """ + + if 'subjectAltName' in cert: + return [x[1] for x in cert['subjectAltName'] + if x[0].lower() == 'dns'] + else: + return [x[0][1] for x in cert['subject'] + if x[0][0].lower() == 'commonname'] + + def validate_cert_host(self, cert, hostname): + """ + Checks if the cert is valid for the hostname + + :param cert: A dict from SSLSocket.getpeercert() + + :param hostname: A string hostname to check + + :return: A boolean if the cert is valid for the hostname + """ + + hosts = self.get_valid_hosts_for_cert(cert) + for host in hosts: + host_re = host.replace('.', '\.').replace('*', '[^.]*') + if re.search('^%s$' % (host_re,), hostname, re.I): + return True + return False + + def _tunnel(self): + """ + This custom _tunnel method allows us to read and print the debug + log for the whole response before throwing an error, and adds + support for proxy authentication + """ + + self._proxy_host = self.host + self._proxy_port = self.port + self._set_hostport(self._tunnel_host, self._tunnel_port) + + self._tunnel_headers['Host'] = u"%s:%s" % (self.host, self.port) + self._tunnel_headers['User-Agent'] = self.user_agent + self._tunnel_headers['Proxy-Connection'] = 'Keep-Alive' + + request = "CONNECT %s:%d HTTP/1.1\r\n" % (self.host, self.port) + for header, value in self._tunnel_headers.items(): + request += "%s: %s\r\n" % (header, value) + request += "\r\n" + + if sys.version_info >= (3,): + request = bytes(request, 'iso-8859-1') + + self.send(request) + + response = self.response_class(self.sock, method=self._method) + (version, code, message) = response._read_status() + + status_line = u"%s %s %s" % (version, code, message.rstrip()) + headers = [status_line] + + if self.debuglevel in [-1, 5]: + console_write(u'Urllib %s Debug Read' % self._debug_protocol, True) + console_write(u" %s" % status_line) + + content_length = 0 + close_connection = False + while True: + line = response.fp.readline() + + if sys.version_info >= (3,): + line = str(line, encoding='iso-8859-1') + + if line == '\r\n': + break + + headers.append(line.rstrip()) + + parts = line.rstrip().split(': ', 1) + name = parts[0].lower() + value = parts[1].lower().strip() + if name == 'content-length': + content_length = int(value) + + if name in ['connection', 'proxy-connection'] and value == 'close': + close_connection = True + + if self.debuglevel in [-1, 5]: + console_write(u" %s" % line.rstrip()) + + # Handle proxy auth for SSL connections since regular urllib punts on this + if code == 407 and self.passwd and 'Proxy-Authorization' not in self._tunnel_headers: + if content_length: + response._safe_read(content_length) + + supported_auth_methods = {} + for line in headers: + parts = line.split(': ', 1) + if parts[0].lower() != 'proxy-authenticate': + continue + details = parts[1].split(' ', 1) + supported_auth_methods[details[0].lower()] = details[1] if len(details) > 1 else '' + + username, password = self.passwd.find_user_password(None, "%s:%s" % ( + self._proxy_host, self._proxy_port)) + + if 'digest' in supported_auth_methods: + response_value = self.build_digest_response( + supported_auth_methods['digest'], username, password) + if response_value: + self._tunnel_headers['Proxy-Authorization'] = u"Digest %s" % response_value + + elif 'basic' in supported_auth_methods: + response_value = u"%s:%s" % (username, password) + response_value = base64.b64encode(response_value).strip() + self._tunnel_headers['Proxy-Authorization'] = u"Basic %s" % response_value + + if 'Proxy-Authorization' in self._tunnel_headers: + self.host = self._proxy_host + self.port = self._proxy_port + + # If the proxy wanted the connection closed, we need to make a new connection + if close_connection: + self.sock.close() + self.sock = socket.create_connection((self.host, self.port), self.timeout) + + return self._tunnel() + + if code != 200: + self.close() + raise socket.error("Tunnel connection failed: %d %s" % (code, + message.strip())) + + def build_digest_response(self, fields, username, password): + """ + Takes a Proxy-Authenticate: Digest header and creates a response + header + + :param fields: + The string portion of the Proxy-Authenticate header after + "Digest " + + :param username: + The username to use for the response + + :param password: + The password to use for the response + + :return: + None if invalid Proxy-Authenticate header, otherwise the + string of fields for the Proxy-Authorization: Digest header + """ + + fields = parse_keqv_list(parse_http_list(fields)) + + realm = fields.get('realm') + nonce = fields.get('nonce') + qop = fields.get('qop') + algorithm = fields.get('algorithm') + if algorithm: + algorithm = algorithm.lower() + opaque = fields.get('opaque') + + if algorithm in ['md5', None]: + def md5hash(string): + return hashlib.md5(string).hexdigest() + hash = md5hash + + elif algorithm == 'sha': + def sha1hash(string): + return hashlib.sha1(string).hexdigest() + hash = sha1hash + + else: + return None + + host_port = u"%s:%s" % (self.host, self.port) + + a1 = "%s:%s:%s" % (username, realm, password) + a2 = "CONNECT:%s" % host_port + ha1 = hash(a1) + ha2 = hash(a2) + + if qop == None: + response = hash(u"%s:%s:%s" % (ha1, nonce, ha2)) + elif qop == 'auth': + nc = '00000001' + cnonce = hash(os.urandom(8))[:8] + response = hash(u"%s:%s:%s:%s:%s:%s" % (ha1, nonce, nc, cnonce, qop, ha2)) + else: + return None + + response_fields = { + 'username': username, + 'realm': realm, + 'nonce': nonce, + 'response': response, + 'uri': host_port + } + if algorithm: + response_fields['algorithm'] = algorithm + if qop == 'auth': + response_fields['nc'] = nc + response_fields['cnonce'] = cnonce + response_fields['qop'] = qop + if opaque: + response_fields['opaque'] = opaque + + return ', '.join([u"%s=\"%s\"" % (field, response_fields[field]) for field in response_fields]) + + def connect(self): + """ + Adds debugging and SSL certification validation + """ + + if self.debuglevel == -1: + console_write(u"Urllib HTTPS Debug General", True) + console_write(u" Connecting to %s on port %s" % (self.host, self.port)) + + self.sock = socket.create_connection((self.host, self.port), self.timeout) + if self._tunnel_host: + self._tunnel() + + if self.debuglevel == -1: + console_write(u"Urllib HTTPS Debug General", True) + console_write(u" Connecting to %s on port %s" % (self.host, self.port)) + console_write(u" CA certs file at %s" % (self.ca_certs.decode(sys.getfilesystemencoding()))) + + self.sock = ssl.wrap_socket(self.sock, keyfile=self.key_file, + certfile=self.cert_file, cert_reqs=self.cert_reqs, + ca_certs=self.ca_certs) + + if self.debuglevel == -1: + console_write(u" Successfully upgraded connection to %s:%s with SSL" % ( + self.host, self.port)) + + # This debugs and validates the SSL certificate + if self.cert_reqs & ssl.CERT_REQUIRED: + cert = self.sock.getpeercert() + + if self.debuglevel == -1: + subjectMap = { + 'organizationName': 'O', + 'commonName': 'CN', + 'organizationalUnitName': 'OU', + 'countryName': 'C', + 'serialNumber': 'serialNumber', + 'commonName': 'CN', + 'localityName': 'L', + 'stateOrProvinceName': 'S' + } + subject_list = list(cert['subject']) + subject_list.reverse() + subject_parts = [] + for pair in subject_list: + if pair[0][0] in subjectMap: + field_name = subjectMap[pair[0][0]] + else: + field_name = pair[0][0] + subject_parts.append(field_name + '=' + pair[0][1]) + + console_write(u" Server SSL certificate:") + console_write(u" subject: " + ','.join(subject_parts)) + if 'subjectAltName' in cert: + console_write(u" common name: " + cert['subjectAltName'][0][1]) + if 'notAfter' in cert: + console_write(u" expire date: " + cert['notAfter']) + + hostname = self.host.split(':', 0)[0] + + if not self.validate_cert_host(cert, hostname): + if self.debuglevel == -1: + console_write(u" Certificate INVALID") + + raise InvalidCertificateException(hostname, cert, + 'hostname mismatch') + + if self.debuglevel == -1: + console_write(u" Certificate validated for %s" % hostname) + +except (ImportError): + pass diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/validating_https_handler.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/validating_https_handler.py new file mode 100644 index 0000000..5b02c7a --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http/validating_https_handler.py @@ -0,0 +1,59 @@ +try: + # Python 3 + from urllib.error import URLError + import urllib.request as urllib_compat +except (ImportError): + # Python 2 + from urllib2 import URLError + import urllib2 as urllib_compat + + +# The following code is wrapped in a try because the Linux versions of Sublime +# Text do not include the ssl module due to the fact that different distros +# have different versions +try: + import ssl + + from .validating_https_connection import ValidatingHTTPSConnection + from .invalid_certificate_exception import InvalidCertificateException + from .persistent_handler import PersistentHandler + + if hasattr(urllib_compat, 'HTTPSHandler'): + class ValidatingHTTPSHandler(PersistentHandler, urllib_compat.HTTPSHandler): + """ + A urllib handler that validates SSL certificates for HTTPS requests + """ + + def __init__(self, **kwargs): + # This is a special value that will not trigger the standard debug + # functionality, but custom code where we can format the output + self._debuglevel = 0 + if 'debug' in kwargs and kwargs['debug']: + self._debuglevel = 5 + elif 'debuglevel' in kwargs: + self._debuglevel = kwargs['debuglevel'] + self._connection_args = kwargs + + def https_open(self, req): + def http_class_wrapper(host, **kwargs): + full_kwargs = dict(self._connection_args) + full_kwargs.update(kwargs) + return ValidatingHTTPSConnection(host, **full_kwargs) + + try: + return self.do_open(http_class_wrapper, req) + except URLError as e: + if type(e.reason) == ssl.SSLError and e.reason.args[0] == 1: + raise InvalidCertificateException(req.host, '', + e.reason.args[1]) + raise + + https_request = urllib_compat.AbstractHTTPHandler.do_request_ + else: + raise ImportError() + +except (ImportError) as e: + + class ValidatingHTTPSHandler(): + def __init__(self, **kwargs): + raise e diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http_cache.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http_cache.py new file mode 100644 index 0000000..2f6f3a2 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/http_cache.py @@ -0,0 +1,75 @@ +import os +import time + +import sublime + +from .open_compat import open_compat, read_compat + + +class HttpCache(object): + """ + A data store for caching HTTP response data. + """ + + def __init__(self, ttl): + self.base_path = os.path.join(sublime.packages_path(), 'User', 'Package Control.cache') + if not os.path.exists(self.base_path): + os.mkdir(self.base_path) + self.clear(int(ttl)) + + def clear(self, ttl): + """ + Removes all cache entries older than the TTL + + :param ttl: + The number of seconds a cache entry should be valid for + """ + + ttl = int(ttl) + + for filename in os.listdir(self.base_path): + path = os.path.join(self.base_path, filename) + # There should not be any folders in the cache dir, but we + # ignore to prevent an exception + if os.path.isdir(path): + continue + mtime = os.stat(path).st_mtime + if mtime < time.time() - ttl: + os.unlink(path) + + def get(self, key): + """ + Returns a cached value + + :param key: + The key to fetch the cache for + + :return: + The (binary) cached value, or False + """ + + cache_file = os.path.join(self.base_path, key) + if not os.path.exists(cache_file): + return False + + with open_compat(cache_file, 'rb') as f: + return read_compat(f) + + def has(self, key): + cache_file = os.path.join(self.base_path, key) + return os.path.exists(cache_file) + + def set(self, key, content): + """ + Saves a value in the cache + + :param key: + The key to save the cache with + + :param content: + The (binary) content to cache + """ + + cache_file = os.path.join(self.base_path, key) + with open_compat(cache_file, 'wb') as f: + f.write(content) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/open_compat.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/open_compat.py new file mode 100644 index 0000000..b22f066 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/open_compat.py @@ -0,0 +1,27 @@ +import os +import sys + +from .file_not_found_error import FileNotFoundError + + +def open_compat(path, mode='r'): + if mode in ['r', 'rb'] and not os.path.exists(path): + raise FileNotFoundError(u"The file \"%s\" could not be found" % path) + + if sys.version_info >= (3,): + encoding = 'utf-8' + errors = 'replace' + if mode in ['rb', 'wb', 'ab']: + encoding = None + errors = None + return open(path, mode, encoding=encoding, errors=errors) + + else: + return open(path, mode) + + +def read_compat(file_obj): + if sys.version_info >= (3,): + return file_obj.read() + else: + return unicode(file_obj.read(), 'utf-8', errors='replace') diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_cleanup.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_cleanup.py new file mode 100644 index 0000000..352f4d4 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_cleanup.py @@ -0,0 +1,107 @@ +import threading +import os +import shutil + +import sublime + +from .show_error import show_error +from .console_write import console_write +from .unicode import unicode_from_os +from .clear_directory import clear_directory +from .automatic_upgrader import AutomaticUpgrader +from .package_manager import PackageManager +from .package_renamer import PackageRenamer +from .open_compat import open_compat +from .package_io import package_file_exists + + +class PackageCleanup(threading.Thread, PackageRenamer): + """ + Cleans up folders for packages that were removed, but that still have files + in use. + """ + + def __init__(self): + self.manager = PackageManager() + self.load_settings() + threading.Thread.__init__(self) + + def run(self): + found_pkgs = [] + installed_pkgs = list(self.installed_packages) + for package_name in os.listdir(sublime.packages_path()): + package_dir = os.path.join(sublime.packages_path(), package_name) + + # Cleanup packages that could not be removed due to in-use files + cleanup_file = os.path.join(package_dir, 'package-control.cleanup') + if os.path.exists(cleanup_file): + try: + shutil.rmtree(package_dir) + console_write(u'Removed old directory for package %s' % package_name, True) + + except (OSError) as e: + if not os.path.exists(cleanup_file): + open_compat(cleanup_file, 'w').close() + + error_string = (u'Unable to remove old directory for package ' + + u'%s - deferring until next start: %s') % ( + package_name, unicode_from_os(e)) + console_write(error_string, True) + + # Finish reinstalling packages that could not be upgraded due to + # in-use files + reinstall = os.path.join(package_dir, 'package-control.reinstall') + if os.path.exists(reinstall): + metadata_path = os.path.join(package_dir, 'package-metadata.json') + if not clear_directory(package_dir, [metadata_path]): + if not os.path.exists(reinstall): + open_compat(reinstall, 'w').close() + # Assigning this here prevents the callback from referencing the value + # of the "package_name" variable when it is executed + restart_message = (u'An error occurred while trying to ' + + u'finish the upgrade of %s. You will most likely need to ' + + u'restart your computer to complete the upgrade.') % package_name + + def show_still_locked(): + show_error(restart_message) + sublime.set_timeout(show_still_locked, 10) + else: + self.manager.install_package(package_name) + + # This adds previously installed packages from old versions of PC + if package_file_exists(package_name, 'package-metadata.json') and \ + package_name not in self.installed_packages: + installed_pkgs.append(package_name) + params = { + 'package': package_name, + 'operation': 'install', + 'version': \ + self.manager.get_metadata(package_name).get('version') + } + self.manager.record_usage(params) + + found_pkgs.append(package_name) + + if int(sublime.version()) >= 3000: + package_files = os.listdir(sublime.installed_packages_path()) + found_pkgs += [file.replace('.sublime-package', '') for file in package_files] + + sublime.set_timeout(lambda: self.finish(installed_pkgs, found_pkgs), 10) + + def finish(self, installed_pkgs, found_pkgs): + """ + A callback that can be run the main UI thread to perform saving of the + Package Control.sublime-settings file. Also fires off the + :class:`AutomaticUpgrader`. + + :param installed_pkgs: + A list of the string package names of all "installed" packages, + even ones that do not appear to be in the filesystem. + + :param found_pkgs: + A list of the string package names of all packages that are + currently installed on the filesystem. + """ + + self.save_packages(installed_pkgs) + AutomaticUpgrader(found_pkgs).start() diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_creator.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_creator.py new file mode 100644 index 0000000..47a3087 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_creator.py @@ -0,0 +1,39 @@ +import os + +from .show_error import show_error +from .package_manager import PackageManager + + +class PackageCreator(): + """ + Abstract class for commands that create .sublime-package files + """ + + def show_panel(self): + """ + Shows a list of packages that can be turned into a .sublime-package file + """ + + self.manager = PackageManager() + self.packages = self.manager.list_packages(unpacked_only=True) + if not self.packages: + show_error('There are no packages available to be packaged') + return + self.window.show_quick_panel(self.packages, self.on_done) + + def get_package_destination(self): + """ + Retrieves the destination for .sublime-package files + + :return: + A string - the path to the folder to save .sublime-package files in + """ + + destination = self.manager.settings.get('package_destination') + + # We check destination via an if statement instead of using + # the dict.get() method since the key may be set, but to a blank value + if not destination: + destination = os.path.join(os.path.expanduser('~'), 'Desktop') + + return destination diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_installer.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_installer.py new file mode 100644 index 0000000..9c8809c --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_installer.py @@ -0,0 +1,247 @@ +import os +import re +import threading + +import sublime + +from .preferences_filename import preferences_filename +from .thread_progress import ThreadProgress +from .package_manager import PackageManager +from .upgraders.git_upgrader import GitUpgrader +from .upgraders.hg_upgrader import HgUpgrader +from .versions import version_comparable + + +class PackageInstaller(): + """ + Provides helper functionality related to installing packages + """ + + def __init__(self): + self.manager = PackageManager() + + def make_package_list(self, ignore_actions=[], override_action=None, + ignore_packages=[]): + """ + Creates a list of packages and what operation would be performed for + each. Allows filtering by the applicable action or package name. + Returns the information in a format suitable for displaying in the + quick panel. + + :param ignore_actions: + A list of actions to ignore packages by. Valid actions include: + `install`, `upgrade`, `downgrade`, `reinstall`, `overwrite`, + `pull` and `none`. `pull` andd `none` are for Git and Hg + repositories. `pull` is present when incoming changes are detected, + where as `none` is selected if no commits are available. `overwrite` + is for packages that do not include version information via the + `package-metadata.json` file. + + :param override_action: + A string action name to override the displayed action for all listed + packages. + + :param ignore_packages: + A list of packages names that should not be returned in the list + + :return: + A list of lists, each containing three strings: + 0 - package name + 1 - package description + 2 - action; [extra info;] package url + """ + + packages = self.manager.list_available_packages() + installed_packages = self.manager.list_packages() + + package_list = [] + for package in sorted(iter(packages.keys()), key=lambda s: s.lower()): + if ignore_packages and package in ignore_packages: + continue + package_entry = [package] + info = packages[package] + download = info['download'] + + if package in installed_packages: + installed = True + metadata = self.manager.get_metadata(package) + if metadata.get('version'): + installed_version = metadata['version'] + else: + installed_version = None + else: + installed = False + + installed_version_name = 'v' + installed_version if \ + installed and installed_version else 'unknown version' + new_version = 'v' + download['version'] + + vcs = None + package_dir = self.manager.get_package_dir(package) + settings = self.manager.settings + + if override_action: + action = override_action + extra = '' + + else: + if os.path.exists(os.path.join(package_dir, '.git')): + if settings.get('ignore_vcs_packages'): + continue + vcs = 'git' + incoming = GitUpgrader(settings.get('git_binary'), + settings.get('git_update_command'), package_dir, + settings.get('cache_length'), settings.get('debug') + ).incoming() + elif os.path.exists(os.path.join(package_dir, '.hg')): + if settings.get('ignore_vcs_packages'): + continue + vcs = 'hg' + incoming = HgUpgrader(settings.get('hg_binary'), + settings.get('hg_update_command'), package_dir, + settings.get('cache_length'), settings.get('debug') + ).incoming() + + if installed: + if vcs: + if incoming: + action = 'pull' + extra = ' with ' + vcs + else: + action = 'none' + extra = '' + elif not installed_version: + action = 'overwrite' + extra = ' %s with %s' % (installed_version_name, + new_version) + else: + installed_version = version_comparable(installed_version) + download_version = version_comparable(download['version']) + if download_version > installed_version: + action = 'upgrade' + extra = ' to %s from %s' % (new_version, + installed_version_name) + elif download_version < installed_version: + action = 'downgrade' + extra = ' to %s from %s' % (new_version, + installed_version_name) + else: + action = 'reinstall' + extra = ' %s' % new_version + else: + action = 'install' + extra = ' %s' % new_version + extra += ';' + + if action in ignore_actions: + continue + + description = info.get('description') + if not description: + description = 'No description provided' + package_entry.append(description) + package_entry.append(action + extra + ' ' + + re.sub('^https?://', '', info['homepage'])) + package_list.append(package_entry) + return package_list + + def disable_packages(self, packages): + """ + Disables one or more packages before installing or upgrading to prevent + errors where Sublime Text tries to read files that no longer exist, or + read a half-written file. + + :param packages: The string package name, or an array of strings + """ + + if not isinstance(packages, list): + packages = [packages] + + # Don't disable Package Control so it does not get stuck disabled + if 'Package Control' in packages: + packages.remove('Package Control') + + disabled = [] + + settings = sublime.load_settings(preferences_filename()) + ignored = settings.get('ignored_packages') + if not ignored: + ignored = [] + for package in packages: + if not package in ignored: + ignored.append(package) + disabled.append(package) + settings.set('ignored_packages', ignored) + sublime.save_settings(preferences_filename()) + return disabled + + def reenable_package(self, package): + """ + Re-enables a package after it has been installed or upgraded + + :param package: The string package name + """ + + settings = sublime.load_settings(preferences_filename()) + ignored = settings.get('ignored_packages') + if not ignored: + return + if package in ignored: + settings.set('ignored_packages', + list(set(ignored) - set([package]))) + sublime.save_settings(preferences_filename()) + + def on_done(self, picked): + """ + Quick panel user selection handler - disables a package, installs or + upgrades it, then re-enables the package + + :param picked: + An integer of the 0-based package name index from the presented + list. -1 means the user cancelled. + """ + + if picked == -1: + return + name = self.package_list[picked][0] + + if name in self.disable_packages(name): + on_complete = lambda: self.reenable_package(name) + else: + on_complete = None + + thread = PackageInstallerThread(self.manager, name, on_complete) + thread.start() + ThreadProgress(thread, 'Installing package %s' % name, + 'Package %s successfully %s' % (name, self.completion_type)) + + +class PackageInstallerThread(threading.Thread): + """ + A thread to run package install/upgrade operations in so that the main + Sublime Text thread does not get blocked and freeze the UI + """ + + def __init__(self, manager, package, on_complete): + """ + :param manager: + An instance of :class:`PackageManager` + + :param package: + The string package name to install/upgrade + + :param on_complete: + A callback to run after installing/upgrading the package + """ + + self.package = package + self.manager = manager + self.on_complete = on_complete + threading.Thread.__init__(self) + + def run(self): + try: + self.result = self.manager.install_package(self.package) + finally: + if self.on_complete: + sublime.set_timeout(self.on_complete, 1) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_io.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_io.py new file mode 100644 index 0000000..14ab134 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_io.py @@ -0,0 +1,126 @@ +import os +import zipfile + +import sublime + +from .console_write import console_write +from .open_compat import open_compat, read_compat +from .unicode import unicode_from_os +from .file_not_found_error import FileNotFoundError + + +def read_package_file(package, relative_path, binary=False, debug=False): + package_dir = _get_package_dir(package) + file_path = os.path.join(package_dir, relative_path) + + if os.path.exists(package_dir): + result = _read_regular_file(package, relative_path, binary, debug) + if result != False: + return result + + if int(sublime.version()) >= 3000: + result = _read_zip_file(package, relative_path, binary, debug) + if result != False: + return result + + if debug: + console_write(u"Unable to find file %s in the package %s" % (relative_path, package), True) + return False + + +def package_file_exists(package, relative_path): + package_dir = _get_package_dir(package) + file_path = os.path.join(package_dir, relative_path) + + if os.path.exists(package_dir): + result = _regular_file_exists(package, relative_path) + if result: + return result + + if int(sublime.version()) >= 3000: + return _zip_file_exists(package, relative_path) + + return False + + +def _get_package_dir(package): + """:return: The full filesystem path to the package directory""" + + return os.path.join(sublime.packages_path(), package) + + +def _read_regular_file(package, relative_path, binary=False, debug=False): + package_dir = _get_package_dir(package) + file_path = os.path.join(package_dir, relative_path) + try: + with open_compat(file_path, ('rb' if binary else 'r')) as f: + return read_compat(f) + + except (FileNotFoundError) as e: + if debug: + console_write(u"Unable to find file %s in the package folder for %s" % (relative_path, package), True) + return False + + +def _read_zip_file(package, relative_path, binary=False, debug=False): + zip_path = os.path.join(sublime.installed_packages_path(), + package + '.sublime-package') + + if not os.path.exists(zip_path): + if debug: + console_write(u"Unable to find a sublime-package file for %s" % package, True) + return False + + try: + package_zip = zipfile.ZipFile(zip_path, 'r') + + except (zipfile.BadZipfile): + console_write(u'An error occurred while trying to unzip the sublime-package file for %s.' % package, True) + return False + + try: + contents = package_zip.read(relative_path) + if not binary: + contents = contents.decode('utf-8') + return contents + + except (KeyError) as e: + if debug: + console_write(u"Unable to find file %s in the sublime-package file for %s" % (relative_path, package), True) + + except (IOError) as e: + message = unicode_from_os(e) + console_write(u'Unable to read file from sublime-package file for %s due to an invalid filename' % package, True) + + except (UnicodeDecodeError): + console_write(u'Unable to read file from sublime-package file for %s due to an invalid filename or character encoding issue' % package, True) + + return False + + +def _regular_file_exists(package, relative_path): + package_dir = _get_package_dir(package) + file_path = os.path.join(package_dir, relative_path) + return os.path.exists(file_path) + + +def _zip_file_exists(package, relative_path): + zip_path = os.path.join(sublime.installed_packages_path(), + package + '.sublime-package') + + if not os.path.exists(zip_path): + return False + + try: + package_zip = zipfile.ZipFile(zip_path, 'r') + + except (zipfile.BadZipfile): + console_write(u'An error occurred while trying to unzip the sublime-package file for %s.' % package_name, True) + return False + + try: + package_zip.getinfo(relative_path) + return True + + except (KeyError) as e: + return False diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_manager.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_manager.py new file mode 100644 index 0000000..03e4120 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_manager.py @@ -0,0 +1,1024 @@ +import sys +import os +import re +import socket +import json +import time +import zipfile +import shutil +from fnmatch import fnmatch +import datetime +import tempfile +import locale + +try: + # Python 3 + from urllib.parse import urlencode, urlparse + import compileall + str_cls = str +except (ImportError): + # Python 2 + from urllib import urlencode + from urlparse import urlparse + str_cls = unicode + +import sublime + +from .show_error import show_error +from .console_write import console_write +from .open_compat import open_compat, read_compat +from .unicode import unicode_from_os +from .clear_directory import clear_directory +from .cache import (clear_cache, set_cache, get_cache, merge_cache_under_settings, + merge_cache_over_settings, set_cache_under_settings, set_cache_over_settings) +from .versions import version_comparable, version_sort +from .downloaders.background_downloader import BackgroundDownloader +from .downloaders.downloader_exception import DownloaderException +from .providers.provider_exception import ProviderException +from .clients.client_exception import ClientException +from .download_manager import downloader +from .providers.channel_provider import ChannelProvider +from .upgraders.git_upgrader import GitUpgrader +from .upgraders.hg_upgrader import HgUpgrader +from .package_io import read_package_file +from .providers import CHANNEL_PROVIDERS, REPOSITORY_PROVIDERS +from . import __version__ + + +class PackageManager(): + """ + Allows downloading, creating, installing, upgrading, and deleting packages + + Delegates metadata retrieval to the CHANNEL_PROVIDERS classes. + Uses VcsUpgrader-based classes for handling git and hg repositories in the + Packages folder. Downloader classes are utilized to fetch contents of URLs. + + Also handles displaying package messaging, and sending usage information to + the usage server. + """ + + def __init__(self): + # Here we manually copy the settings since sublime doesn't like + # code accessing settings from threads + self.settings = {} + settings = sublime.load_settings('Package Control.sublime-settings') + for setting in ['timeout', 'repositories', 'channels', + 'package_name_map', 'dirs_to_ignore', 'files_to_ignore', + 'package_destination', 'cache_length', 'auto_upgrade', + 'files_to_ignore_binary', 'files_to_keep', 'dirs_to_keep', + 'git_binary', 'git_update_command', 'hg_binary', + 'hg_update_command', 'http_proxy', 'https_proxy', + 'auto_upgrade_ignore', 'auto_upgrade_frequency', + 'submit_usage', 'submit_url', 'renamed_packages', + 'files_to_include', 'files_to_include_binary', 'certs', + 'ignore_vcs_packages', 'proxy_username', 'proxy_password', + 'debug', 'user_agent', 'http_cache', 'http_cache_length', + 'install_prereleases', 'openssl_binary']: + if settings.get(setting) == None: + continue + self.settings[setting] = settings.get(setting) + + # https_proxy will inherit from http_proxy unless it is set to a + # string value or false + no_https_proxy = self.settings.get('https_proxy') in ["", None] + if no_https_proxy and self.settings.get('http_proxy'): + self.settings['https_proxy'] = self.settings.get('http_proxy') + if self.settings.get('https_proxy') == False: + self.settings['https_proxy'] = '' + + self.settings['platform'] = sublime.platform() + self.settings['version'] = sublime.version() + + # Use the cache to see if settings have changed since the last + # time the package manager was created, and clearing any cached + # values if they have. + previous_settings = get_cache('filtered_settings', {}) + + # Reduce the settings down to exclude channel info since that will + # make the settings always different + filtered_settings = self.settings.copy() + for key in ['repositories', 'channels', 'package_name_map', 'cache']: + if key in filtered_settings: + del filtered_settings[key] + + if filtered_settings != previous_settings and previous_settings != {}: + console_write(u'Settings change detected, clearing cache', True) + clear_cache() + set_cache('filtered_settings', filtered_settings) + + def get_metadata(self, package): + """ + Returns the package metadata for an installed package + + :return: + A dict with the keys: + version + url + description + or an empty dict on error + """ + + try: + debug = self.settings.get('debug') + metadata_json = read_package_file(package, 'package-metadata.json', debug=debug) + if metadata_json: + return json.loads(metadata_json) + + except (IOError, ValueError) as e: + pass + + return {} + + def list_repositories(self): + """ + Returns a master list of all repositories pulled from all sources + + These repositories come from the channels specified in the + "channels" setting, plus any repositories listed in the + "repositories" setting. + + :return: + A list of all available repositories + """ + + cache_ttl = self.settings.get('cache_length') + + repositories = self.settings.get('repositories')[:] + channels = self.settings.get('channels') + for channel in channels: + channel = channel.strip() + + # Caches various info from channels for performance + cache_key = channel + '.repositories' + channel_repositories = get_cache(cache_key) + + merge_cache_under_settings(self, 'package_name_map', channel) + merge_cache_under_settings(self, 'renamed_packages', channel) + merge_cache_under_settings(self, 'unavailable_packages', channel, list_=True) + + # If any of the info was not retrieved from the cache, we need to + # grab the channel to get it + if channel_repositories == None: + + for provider_class in CHANNEL_PROVIDERS: + if provider_class.match_url(channel): + provider = provider_class(channel, self.settings) + break + + try: + channel_repositories = provider.get_repositories() + set_cache(cache_key, channel_repositories, cache_ttl) + + for repo in channel_repositories: + repo_packages = provider.get_packages(repo) + packages_cache_key = repo + '.packages' + set_cache(packages_cache_key, repo_packages, cache_ttl) + + # Have the local name map override the one from the channel + name_map = provider.get_name_map() + set_cache_under_settings(self, 'package_name_map', channel, name_map, cache_ttl) + + renamed_packages = provider.get_renamed_packages() + set_cache_under_settings(self, 'renamed_packages', channel, renamed_packages, cache_ttl) + + unavailable_packages = provider.get_unavailable_packages() + set_cache_under_settings(self, 'unavailable_packages', channel, unavailable_packages, cache_ttl, list_=True) + + provider_certs = provider.get_certs() + certs = self.settings.get('certs', {}).copy() + certs.update(provider_certs) + # Save the master list of certs, used by downloaders/cert_provider.py + set_cache('*.certs', certs, cache_ttl) + + except (DownloaderException, ClientException, ProviderException) as e: + console_write(e, True) + continue + + repositories.extend(channel_repositories) + return [repo.strip() for repo in repositories] + + def list_available_packages(self): + """ + Returns a master list of every available package from all sources + + :return: + A dict in the format: + { + 'Package Name': { + # Package details - see example-packages.json for format + }, + ... + } + """ + + if self.settings.get('debug'): + console_write(u"Fetching list of available packages", True) + console_write(u" Platform: %s-%s" % (sublime.platform(),sublime.arch())) + console_write(u" Sublime Text Version: %s" % sublime.version()) + console_write(u" Package Control Version: %s" % __version__) + + cache_ttl = self.settings.get('cache_length') + repositories = self.list_repositories() + packages = {} + bg_downloaders = {} + active = [] + repos_to_download = [] + name_map = self.settings.get('package_name_map', {}) + + # Repositories are run in reverse order so that the ones first + # on the list will overwrite those last on the list + for repo in repositories[::-1]: + cache_key = repo + '.packages' + repository_packages = get_cache(cache_key) + + if repository_packages != None: + packages.update(repository_packages) + + else: + domain = urlparse(repo).hostname + if domain not in bg_downloaders: + bg_downloaders[domain] = BackgroundDownloader( + self.settings, REPOSITORY_PROVIDERS) + bg_downloaders[domain].add_url(repo) + repos_to_download.append(repo) + + for bg_downloader in list(bg_downloaders.values()): + bg_downloader.start() + active.append(bg_downloader) + + # Wait for all of the downloaders to finish + while active: + bg_downloader = active.pop() + bg_downloader.join() + + # Grabs the results and stuff it all in the cache + for repo in repos_to_download: + domain = urlparse(repo).hostname + bg_downloader = bg_downloaders[domain] + provider = bg_downloader.get_provider(repo) + + # Allow name mapping of packages for schema version < 2.0 + repository_packages = {} + for name, info in provider.get_packages(): + name = name_map.get(name, name) + info['name'] = name + repository_packages[name] = info + + # Display errors we encountered while fetching package info + for url, exception in provider.get_failed_sources(): + console_write(exception, True) + for name, exception in provider.get_broken_packages(): + console_write(exception, True) + + cache_key = repo + '.packages' + set_cache(cache_key, repository_packages, cache_ttl) + packages.update(repository_packages) + + renamed_packages = provider.get_renamed_packages() + set_cache_under_settings(self, 'renamed_packages', repo, renamed_packages, cache_ttl) + + unavailable_packages = provider.get_unavailable_packages() + set_cache_under_settings(self, 'unavailable_packages', repo, unavailable_packages, cache_ttl, list_=True) + + return packages + + def list_packages(self, unpacked_only=False): + """ + :param unpacked_only: + Only list packages that are not inside of .sublime-package files + + :return: A list of all installed, non-default, package names + """ + + package_names = os.listdir(sublime.packages_path()) + package_names = [path for path in package_names if + os.path.isdir(os.path.join(sublime.packages_path(), path))] + + if int(sublime.version()) > 3000 and unpacked_only == False: + package_files = os.listdir(sublime.installed_packages_path()) + package_names += [f.replace('.sublime-package', '') for f in package_files if re.search('\.sublime-package$', f) != None] + + # Ignore things to be deleted + ignored = ['User'] + for package in package_names: + cleanup_file = os.path.join(sublime.packages_path(), package, + 'package-control.cleanup') + if os.path.exists(cleanup_file): + ignored.append(package) + + packages = list(set(package_names) - set(ignored) - + set(self.list_default_packages())) + packages = sorted(packages, key=lambda s: s.lower()) + + return packages + + def list_all_packages(self): + """ :return: A list of all installed package names, including default packages""" + + packages = self.list_default_packages() + self.list_packages() + packages = sorted(packages, key=lambda s: s.lower()) + return packages + + def list_default_packages(self): + """ :return: A list of all default package names""" + + if int(sublime.version()) > 3000: + bundled_packages_path = os.path.join(os.path.dirname(sublime.executable_path()), + 'Packages') + files = os.listdir(bundled_packages_path) + + else: + files = os.listdir(os.path.join(os.path.dirname( + sublime.packages_path()), 'Pristine Packages')) + files = list(set(files) - set(os.listdir( + sublime.installed_packages_path()))) + packages = [file.replace('.sublime-package', '') for file in files] + packages = sorted(packages, key=lambda s: s.lower()) + return packages + + def get_package_dir(self, package): + """:return: The full filesystem path to the package directory""" + + return os.path.join(sublime.packages_path(), package) + + def get_mapped_name(self, package): + """:return: The name of the package after passing through mapping rules""" + + return self.settings.get('package_name_map', {}).get(package, package) + + def create_package(self, package_name, package_destination, + binary_package=False): + """ + Creates a .sublime-package file from the running Packages directory + + :param package_name: + The package to create a .sublime-package file for + + :param package_destination: + The full filesystem path of the directory to save the new + .sublime-package file in. + + :param binary_package: + If the created package should follow the binary package include/ + exclude patterns from the settings. These normally include a setup + to exclude .py files and include .pyc files, but that can be + changed via settings. + + :return: bool if the package file was successfully created + """ + + package_dir = self.get_package_dir(package_name) + + if not os.path.exists(package_dir): + show_error(u'The folder for the package name specified, %s, does not exist in %s' % ( + package_name, sublime.packages_path())) + return False + + package_filename = package_name + '.sublime-package' + package_path = os.path.join(package_destination, + package_filename) + + if not os.path.exists(sublime.installed_packages_path()): + os.mkdir(sublime.installed_packages_path()) + + if os.path.exists(package_path): + os.remove(package_path) + + try: + package_file = zipfile.ZipFile(package_path, "w", + compression=zipfile.ZIP_DEFLATED) + except (OSError, IOError) as e: + show_error(u'An error occurred creating the package file %s in %s.\n\n%s' % ( + package_filename, package_destination, unicode_from_os(e))) + return False + + if int(sublime.version()) >= 3000: + compileall.compile_dir(package_dir, quiet=True, legacy=True, optimize=2) + + dirs_to_ignore = self.settings.get('dirs_to_ignore', []) + if not binary_package: + files_to_ignore = self.settings.get('files_to_ignore', []) + files_to_include = self.settings.get('files_to_include', []) + else: + files_to_ignore = self.settings.get('files_to_ignore_binary', []) + files_to_include = self.settings.get('files_to_include_binary', []) + + slash = '\\' if os.name == 'nt' else '/' + trailing_package_dir = package_dir + slash if package_dir[-1] != slash else package_dir + package_dir_regex = re.compile('^' + re.escape(trailing_package_dir)) + for root, dirs, files in os.walk(package_dir): + [dirs.remove(dir_) for dir_ in dirs if dir_ in dirs_to_ignore] + paths = dirs + paths.extend(files) + for path in paths: + full_path = os.path.join(root, path) + relative_path = re.sub(package_dir_regex, '', full_path) + + ignore_matches = [fnmatch(relative_path, p) for p in files_to_ignore] + include_matches = [fnmatch(relative_path, p) for p in files_to_include] + if any(ignore_matches) and not any(include_matches): + continue + + if os.path.isdir(full_path): + continue + package_file.write(full_path, relative_path) + + package_file.close() + + return True + + def install_package(self, package_name): + """ + Downloads and installs (or upgrades) a package + + Uses the self.list_available_packages() method to determine where to + retrieve the package file from. + + The install process consists of: + + 1. Finding the package + 2. Downloading the .sublime-package/.zip file + 3. Extracting the package file + 4. Showing install/upgrade messaging + 5. Submitting usage info + 6. Recording that the package is installed + + :param package_name: + The package to download and install + + :return: bool if the package was successfully installed + """ + + packages = self.list_available_packages() + + is_available = package_name in list(packages.keys()) + is_unavailable = package_name in self.settings.get('unavailable_packages', []) + + if is_unavailable and not is_available: + console_write(u'The package "%s" is not available on this platform.' % package_name, True) + return False + + if not is_available: + show_error(u'The package specified, %s, is not available' % package_name) + return False + + url = packages[package_name]['download']['url'] + package_filename = package_name + '.sublime-package' + + tmp_dir = tempfile.mkdtemp() + + try: + # This is refers to the zipfile later on, so we define it here so we can + # close the zip file if set during the finally clause + package_zip = None + + tmp_package_path = os.path.join(tmp_dir, package_filename) + + unpacked_package_dir = self.get_package_dir(package_name) + package_path = os.path.join(sublime.installed_packages_path(), + package_filename) + pristine_package_path = os.path.join(os.path.dirname( + sublime.packages_path()), 'Pristine Packages', package_filename) + + if os.path.exists(os.path.join(unpacked_package_dir, '.git')): + if self.settings.get('ignore_vcs_packages'): + show_error(u'Skipping git package %s since the setting ignore_vcs_packages is set to true' % package_name) + return False + return GitUpgrader(self.settings['git_binary'], + self.settings['git_update_command'], unpacked_package_dir, + self.settings['cache_length'], self.settings['debug']).run() + elif os.path.exists(os.path.join(unpacked_package_dir, '.hg')): + if self.settings.get('ignore_vcs_packages'): + show_error(u'Skipping hg package %s since the setting ignore_vcs_packages is set to true' % package_name) + return False + return HgUpgrader(self.settings['hg_binary'], + self.settings['hg_update_command'], unpacked_package_dir, + self.settings['cache_length'], self.settings['debug']).run() + + old_version = self.get_metadata(package_name).get('version') + is_upgrade = old_version != None + + # Download the sublime-package or zip file + try: + with downloader(url, self.settings) as manager: + package_bytes = manager.fetch(url, 'Error downloading package.') + except (DownloaderException) as e: + console_write(e, True) + show_error(u'Unable to download %s. Please view the console for more details.' % package_name) + return False + + with open_compat(tmp_package_path, "wb") as package_file: + package_file.write(package_bytes) + + # Try to open it as a zip file + try: + package_zip = zipfile.ZipFile(tmp_package_path, 'r') + except (zipfile.BadZipfile): + show_error(u'An error occurred while trying to unzip the package file for %s. Please try installing the package again.' % package_name) + return False + + # Scan through the root level of the zip file to gather some info + root_level_paths = [] + last_path = None + for path in package_zip.namelist(): + try: + if not isinstance(path, str_cls): + path = path.decode('utf-8', 'strict') + except (UnicodeDecodeError): + console_write(u'One or more of the zip file entries in %s is not encoded using UTF-8, aborting' % package_name, True) + return False + + last_path = path + + if path.find('/') in [len(path) - 1, -1]: + root_level_paths.append(path) + # Make sure there are no paths that look like security vulnerabilities + if path[0] == '/' or path.find('../') != -1 or path.find('..\\') != -1: + show_error(u'The package specified, %s, contains files outside of the package dir and cannot be safely installed.' % package_name) + return False + + if last_path and len(root_level_paths) == 0: + root_level_paths.append(last_path[0:last_path.find('/') + 1]) + + # If there is only a single directory at the top leve, the file + # is most likely a zip from BitBucket or GitHub and we need + # to skip the top-level dir when extracting + skip_root_dir = len(root_level_paths) == 1 and \ + root_level_paths[0].endswith('/') + + no_package_file_zip_path = '.no-sublime-package' + if skip_root_dir: + no_package_file_zip_path = root_level_paths[0] + no_package_file_zip_path + + # If we should extract unpacked or as a .sublime-package file + unpack = True + + # By default, ST3 prefers .sublime-package files since this allows + # overriding files in the Packages/{package_name}/ folder + if int(sublime.version()) >= 3000: + unpack = False + + # If the package maintainer doesn't want a .sublime-package + try: + package_zip.getinfo(no_package_file_zip_path) + unpack = True + except (KeyError): + pass + + # If we already have a package-metadata.json file in + # Packages/{package_name}/, the only way to successfully upgrade + # will be to unpack + unpacked_metadata_file = os.path.join(unpacked_package_dir, + 'package-metadata.json') + if os.path.exists(unpacked_metadata_file): + unpack = True + + # If we determined it should be unpacked, we extract directly + # into the Packages/{package_name}/ folder + if unpack: + self.backup_package_dir(package_name) + package_dir = unpacked_package_dir + + # Otherwise we go into a temp dir since we will be creating a + # new .sublime-package file later + else: + tmp_working_dir = os.path.join(tmp_dir, 'working') + os.mkdir(tmp_working_dir) + package_dir = tmp_working_dir + + package_metadata_file = os.path.join(package_dir, + 'package-metadata.json') + + if not os.path.exists(package_dir): + os.mkdir(package_dir) + + os.chdir(package_dir) + + # Here we don't use .extractall() since it was having issues on OS X + overwrite_failed = False + extracted_paths = [] + for path in package_zip.namelist(): + dest = path + + try: + if not isinstance(dest, str_cls): + dest = dest.decode('utf-8', 'strict') + except (UnicodeDecodeError): + console_write(u'One or more of the zip file entries in %s is not encoded using UTF-8, aborting' % package_name, True) + return False + + if os.name == 'nt': + regex = ':|\*|\?|"|<|>|\|' + if re.search(regex, dest) != None: + console_write(u'Skipping file from package named %s due to an invalid filename' % package_name, True) + continue + + # If there was only a single directory in the package, we remove + # that folder name from the paths as we extract entries + if skip_root_dir: + dest = dest[len(root_level_paths[0]):] + + if os.name == 'nt': + dest = dest.replace('/', '\\') + else: + dest = dest.replace('\\', '/') + + dest = os.path.join(package_dir, dest) + + def add_extracted_dirs(dir_): + while dir_ not in extracted_paths: + extracted_paths.append(dir_) + dir_ = os.path.dirname(dir_) + if dir_ == package_dir: + break + + if path.endswith('/'): + if not os.path.exists(dest): + os.makedirs(dest) + add_extracted_dirs(dest) + else: + dest_dir = os.path.dirname(dest) + if not os.path.exists(dest_dir): + os.makedirs(dest_dir) + add_extracted_dirs(dest_dir) + extracted_paths.append(dest) + try: + open_compat(dest, 'wb').write(package_zip.read(path)) + except (IOError) as e: + message = unicode_from_os(e) + if re.search('[Ee]rrno 13', message): + overwrite_failed = True + break + console_write(u'Skipping file from package named %s due to an invalid filename' % package_name, True) + + except (UnicodeDecodeError): + console_write(u'Skipping file from package named %s due to an invalid filename' % package_name, True) + + package_zip.close() + package_zip = None + + # If upgrading failed, queue the package to upgrade upon next start + if overwrite_failed: + reinstall_file = os.path.join(package_dir, 'package-control.reinstall') + open_compat(reinstall_file, 'w').close() + + # Don't delete the metadata file, that way we have it + # when the reinstall happens, and the appropriate + # usage info can be sent back to the server + clear_directory(package_dir, [reinstall_file, package_metadata_file]) + + show_error(u'An error occurred while trying to upgrade %s. Please restart Sublime Text to finish the upgrade.' % package_name) + return False + + # Here we clean out any files that were not just overwritten. It is ok + # if there is an error removing a file. The next time there is an + # upgrade, it should be cleaned out successfully then. + clear_directory(package_dir, extracted_paths) + + self.print_messages(package_name, package_dir, is_upgrade, old_version) + + with open_compat(package_metadata_file, 'w') as f: + metadata = { + "version": packages[package_name]['download']['version'], + "url": packages[package_name]['homepage'], + "description": packages[package_name]['description'] + } + json.dump(metadata, f) + + # Submit install and upgrade info + if is_upgrade: + params = { + 'package': package_name, + 'operation': 'upgrade', + 'version': packages[package_name]['download']['version'], + 'old_version': old_version + } + else: + params = { + 'package': package_name, + 'operation': 'install', + 'version': packages[package_name]['download']['version'] + } + self.record_usage(params) + + # Record the install in the settings file so that you can move + # settings across computers and have the same packages installed + def save_package(): + settings = sublime.load_settings('Package Control.sublime-settings') + installed_packages = settings.get('installed_packages', []) + if not installed_packages: + installed_packages = [] + installed_packages.append(package_name) + installed_packages = list(set(installed_packages)) + installed_packages = sorted(installed_packages, + key=lambda s: s.lower()) + settings.set('installed_packages', installed_packages) + sublime.save_settings('Package Control.sublime-settings') + sublime.set_timeout(save_package, 1) + + # If we didn't extract directly into the Packages/{package_name}/ + # folder, we need to create a .sublime-package file and install it + if not unpack: + try: + # Remove the downloaded file since we are going to overwrite it + os.remove(tmp_package_path) + package_zip = zipfile.ZipFile(tmp_package_path, "w", + compression=zipfile.ZIP_DEFLATED) + except (OSError, IOError) as e: + show_error(u'An error occurred creating the package file %s in %s.\n\n%s' % ( + package_filename, tmp_dir, unicode_from_os(e))) + return False + + package_dir_regex = re.compile('^' + re.escape(package_dir)) + for root, dirs, files in os.walk(package_dir): + paths = dirs + paths.extend(files) + for path in paths: + full_path = os.path.join(root, path) + relative_path = re.sub(package_dir_regex, '', full_path) + if os.path.isdir(full_path): + continue + package_zip.write(full_path, relative_path) + + package_zip.close() + package_zip = None + + if os.path.exists(package_path): + os.remove(package_path) + shutil.move(tmp_package_path, package_path) + + # We have to remove the pristine package too or else Sublime Text 2 + # will silently delete the package + if os.path.exists(pristine_package_path): + os.remove(pristine_package_path) + + os.chdir(sublime.packages_path()) + return True + + finally: + # We need to make sure the zipfile is closed to + # help prevent permissions errors on Windows + if package_zip: + package_zip.close() + + # Try to remove the tmp dir after a second to make sure + # a virus scanner is holding a reference to the zipfile + # after we close it. + def remove_tmp_dir(): + try: + shutil.rmtree(tmp_dir) + except (PermissionError): + # If we can't remove the tmp dir, don't let an uncaught exception + # fall through and break the install process + pass + sublime.set_timeout(remove_tmp_dir, 1000) + + def backup_package_dir(self, package_name): + """ + Does a full backup of the Packages/{package}/ dir to Backup/ + + :param package_name: + The name of the package to back up + + :return: + If the backup succeeded + """ + + package_dir = os.path.join(sublime.packages_path(), package_name) + if not os.path.exists(package_dir): + return True + + try: + backup_dir = os.path.join(os.path.dirname( + sublime.packages_path()), 'Backup', + datetime.datetime.now().strftime('%Y%m%d%H%M%S')) + if not os.path.exists(backup_dir): + os.makedirs(backup_dir) + package_backup_dir = os.path.join(backup_dir, package_name) + if os.path.exists(package_backup_dir): + console_write(u"FOLDER %s ALREADY EXISTS!" % package_backup_dir) + shutil.copytree(package_dir, package_backup_dir) + return True + + except (OSError, IOError) as e: + show_error(u'An error occurred while trying to backup the package directory for %s.\n\n%s' % ( + package_name, unicode_from_os(e))) + if os.path.exists(package_backup_dir): + shutil.rmtree(package_backup_dir) + return False + + def print_messages(self, package, package_dir, is_upgrade, old_version): + """ + Prints out package install and upgrade messages + + The functionality provided by this allows package maintainers to + show messages to the user when a package is installed, or when + certain version upgrade occur. + + :param package: + The name of the package the message is for + + :param package_dir: + The full filesystem path to the package directory + + :param is_upgrade: + If the install was actually an upgrade + + :param old_version: + The string version of the package before the upgrade occurred + """ + + messages_file = os.path.join(package_dir, 'messages.json') + if not os.path.exists(messages_file): + return + + messages_fp = open_compat(messages_file, 'r') + try: + message_info = json.loads(read_compat(messages_fp)) + except (ValueError): + console_write(u'Error parsing messages.json for %s' % package, True) + return + messages_fp.close() + + output = '' + if not is_upgrade and message_info.get('install'): + install_messages = os.path.join(package_dir, + message_info.get('install')) + message = '\n\n%s:\n%s\n\n ' % (package, + ('-' * len(package))) + with open_compat(install_messages, 'r') as f: + message += read_compat(f).replace('\n', '\n ') + output += message + '\n' + + elif is_upgrade and old_version: + upgrade_messages = list(set(message_info.keys()) - + set(['install'])) + upgrade_messages = version_sort(upgrade_messages, reverse=True) + old_version_cmp = version_comparable(old_version) + + for version in upgrade_messages: + if version_comparable(version) <= old_version_cmp: + break + if not output: + message = '\n\n%s:\n%s\n' % (package, + ('-' * len(package))) + output += message + upgrade_message_path = os.path.join(package_dir, + message_info.get(version)) + message = '\n ' + with open_compat(upgrade_message_path, 'r') as f: + message += read_compat(f).replace('\n', '\n ') + output += message + '\n' + + if not output: + return + + def print_to_panel(): + window = sublime.active_window() + + views = window.views() + view = None + for _view in views: + if _view.name() == 'Package Control Messages': + view = _view + break + + if not view: + view = window.new_file() + view.set_name('Package Control Messages') + view.set_scratch(True) + + def write(string): + view.run_command('package_message', {'string': string}) + + if not view.size(): + view.settings().set("word_wrap", True) + write('Package Control Messages\n' + + '========================') + + write(output) + sublime.set_timeout(print_to_panel, 1) + + def remove_package(self, package_name): + """ + Deletes a package + + The deletion process consists of: + + 1. Deleting the directory (or marking it for deletion if deletion fails) + 2. Submitting usage info + 3. Removing the package from the list of installed packages + + :param package_name: + The package to delete + + :return: bool if the package was successfully deleted + """ + + installed_packages = self.list_packages() + + if package_name not in installed_packages: + show_error(u'The package specified, %s, is not installed' % package_name) + return False + + os.chdir(sublime.packages_path()) + + # Give Sublime Text some time to ignore the package + time.sleep(1) + + package_filename = package_name + '.sublime-package' + installed_package_path = os.path.join(sublime.installed_packages_path(), + package_filename) + pristine_package_path = os.path.join(os.path.dirname( + sublime.packages_path()), 'Pristine Packages', package_filename) + package_dir = self.get_package_dir(package_name) + + version = self.get_metadata(package_name).get('version') + + try: + if os.path.exists(installed_package_path): + os.remove(installed_package_path) + except (OSError, IOError) as e: + show_error(u'An error occurred while trying to remove the installed package file for %s.\n\n%s' % ( + package_name, unicode_from_os(e))) + return False + + try: + if os.path.exists(pristine_package_path): + os.remove(pristine_package_path) + except (OSError, IOError) as e: + show_error(u'An error occurred while trying to remove the pristine package file for %s.\n\n%s' % ( + package_name, unicode_from_os(e))) + return False + + # We don't delete the actual package dir immediately due to a bug + # in sublime_plugin.py + can_delete_dir = True + if not clear_directory(package_dir): + # If there is an error deleting now, we will mark it for + # cleanup the next time Sublime Text starts + open_compat(os.path.join(package_dir, 'package-control.cleanup'), + 'w').close() + can_delete_dir = False + + params = { + 'package': package_name, + 'operation': 'remove', + 'version': version + } + self.record_usage(params) + + # Remove the package from the installed packages list + def clear_package(): + settings = sublime.load_settings('Package Control.sublime-settings') + installed_packages = settings.get('installed_packages', []) + if not installed_packages: + installed_packages = [] + installed_packages.remove(package_name) + settings.set('installed_packages', installed_packages) + sublime.save_settings('Package Control.sublime-settings') + sublime.set_timeout(clear_package, 1) + + if can_delete_dir and os.path.exists(package_dir): + os.rmdir(package_dir) + + return True + + def record_usage(self, params): + """ + Submits install, upgrade and delete actions to a usage server + + The usage information is currently displayed on the Package Control + community package list at http://wbond.net/sublime_packages/community + + :param params: + A dict of the information to submit + """ + + if not self.settings.get('submit_usage'): + return + params['package_control_version'] = \ + self.get_metadata('Package Control').get('version') + params['sublime_platform'] = self.settings.get('platform') + params['sublime_version'] = self.settings.get('version') + + # For Python 2, we need to explicitly encoding the params + for param in params: + if isinstance(params[param], str_cls): + params[param] = params[param].encode('utf-8') + + url = self.settings.get('submit_url') + '?' + urlencode(params) + + try: + with downloader(url, self.settings) as manager: + result = manager.fetch(url, 'Error submitting usage information.') + except (DownloaderException) as e: + console_write(e, True) + return + + try: + result = json.loads(result.decode('utf-8')) + if result['result'] != 'success': + raise ValueError() + except (ValueError): + console_write(u'Error submitting usage information for %s' % params['package'], True) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_renamer.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_renamer.py new file mode 100644 index 0000000..73e83fd --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/package_renamer.py @@ -0,0 +1,117 @@ +import os + +import sublime + +from .console_write import console_write +from .package_io import package_file_exists + + +class PackageRenamer(): + """ + Class to handle renaming packages via the renamed_packages setting + gathered from channels and repositories. + """ + + def load_settings(self): + """ + Loads the list of installed packages from the + Package Control.sublime-settings file. + """ + + self.settings_file = 'Package Control.sublime-settings' + self.settings = sublime.load_settings(self.settings_file) + self.installed_packages = self.settings.get('installed_packages', []) + if not isinstance(self.installed_packages, list): + self.installed_packages = [] + + def rename_packages(self, installer): + """ + Renames any installed packages that the user has installed. + + :param installer: + An instance of :class:`PackageInstaller` + """ + + # Fetch the packages since that will pull in the renamed packages list + installer.manager.list_available_packages() + renamed_packages = installer.manager.settings.get('renamed_packages', {}) + if not renamed_packages: + renamed_packages = {} + + # These are packages that have been tracked as installed + installed_pkgs = self.installed_packages + # There are the packages actually present on the filesystem + present_packages = installer.manager.list_packages() + + # Rename directories for packages that have changed names + for package_name in renamed_packages: + package_dir = os.path.join(sublime.packages_path(), package_name) + if not package_file_exists(package_name, 'package-metadata.json'): + continue + + new_package_name = renamed_packages[package_name] + new_package_dir = os.path.join(sublime.packages_path(), + new_package_name) + + changing_case = package_name.lower() == new_package_name.lower() + case_insensitive_fs = sublime.platform() in ['windows', 'osx'] + + # Since Windows and OSX use case-insensitive filesystems, we have to + # scan through the list of installed packages if the rename of the + # package is just changing the case of it. If we don't find the old + # name for it, we continue the loop since os.path.exists() will return + # true due to the case-insensitive nature of the filesystems. + if case_insensitive_fs and changing_case: + has_old = False + for present_package_name in present_packages: + if present_package_name == package_name: + has_old = True + break + if not has_old: + continue + + if not os.path.exists(new_package_dir) or (case_insensitive_fs and changing_case): + + # Windows will not allow you to rename to the same name with + # a different case, so we work around that with a temporary name + if os.name == 'nt' and changing_case: + temp_package_name = '__' + new_package_name + temp_package_dir = os.path.join(sublime.packages_path(), + temp_package_name) + os.rename(package_dir, temp_package_dir) + package_dir = temp_package_dir + + os.rename(package_dir, new_package_dir) + installed_pkgs.append(new_package_name) + + console_write(u'Renamed %s to %s' % (package_name, new_package_name), True) + + else: + installer.manager.remove_package(package_name) + message_string = u'Removed %s since package with new name (%s) already exists' % ( + package_name, new_package_name) + console_write(message_string, True) + + try: + installed_pkgs.remove(package_name) + except (ValueError): + pass + + sublime.set_timeout(lambda: self.save_packages(installed_pkgs), 10) + + def save_packages(self, installed_packages): + """ + Saves the list of installed packages (after having been appropriately + renamed) + + :param installed_packages: + The new list of installed packages + """ + + installed_packages = list(set(installed_packages)) + installed_packages = sorted(installed_packages, + key=lambda s: s.lower()) + + if installed_packages != self.installed_packages: + self.settings.set('installed_packages', installed_packages) + sublime.save_settings(self.settings_file) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/preferences_filename.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/preferences_filename.py new file mode 100644 index 0000000..7091dd9 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/preferences_filename.py @@ -0,0 +1,11 @@ +import sublime + + +def preferences_filename(): + """ + :return: The appropriate settings filename based on the version of Sublime Text + """ + + if int(sublime.version()) >= 2174: + return 'Preferences.sublime-settings' + return 'Global.sublime-settings' diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/__init__.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/__init__.py new file mode 100644 index 0000000..cfea3bd --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/__init__.py @@ -0,0 +1,12 @@ +from .bitbucket_repository_provider import BitBucketRepositoryProvider +from .github_repository_provider import GitHubRepositoryProvider +from .github_user_provider import GitHubUserProvider +from .repository_provider import RepositoryProvider + +from .channel_provider import ChannelProvider + + +REPOSITORY_PROVIDERS = [BitBucketRepositoryProvider, GitHubRepositoryProvider, + GitHubUserProvider, RepositoryProvider] + +CHANNEL_PROVIDERS = [ChannelProvider] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/bitbucket_repository_provider.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/bitbucket_repository_provider.py new file mode 100644 index 0000000..b5d603f --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/bitbucket_repository_provider.py @@ -0,0 +1,163 @@ +import re + +from ..clients.bitbucket_client import BitBucketClient +from ..downloaders.downloader_exception import DownloaderException +from ..clients.client_exception import ClientException +from .provider_exception import ProviderException + + +class BitBucketRepositoryProvider(): + """ + Allows using a public BitBucket repository as the source for a single package. + For legacy purposes, this can also be treated as the source for a Package + Control "repository". + + :param repo: + The public web URL to the BitBucket repository. Should be in the format + `https://bitbucket.org/user/package`. + + :param settings: + A dict containing at least the following fields: + `cache_length`, + `debug`, + `timeout`, + `user_agent` + Optional fields: + `http_proxy`, + `https_proxy`, + `proxy_username`, + `proxy_password`, + `query_string_params` + `install_prereleases` + """ + + def __init__(self, repo, settings): + self.cache = {} + self.repo = repo + self.settings = settings + self.failed_sources = {} + + @classmethod + def match_url(cls, repo): + """Indicates if this provider can handle the provided repo""" + + return re.search('^https?://bitbucket.org/([^/]+/[^/]+)/?$', repo) != None + + def prefetch(self): + """ + Go out and perform HTTP operations, caching the result + + :raises: + DownloaderException: when there is an issue download package info + ClientException: when there is an issue parsing package info + """ + + [name for name, info in self.get_packages()] + + def get_failed_sources(self): + """ + List of any URLs that could not be accessed while accessing this repository + + :return: + A generator of ("https://bitbucket.org/user/repo", Exception()) tuples + """ + + return self.failed_sources.items() + + def get_broken_packages(self): + """ + For API-compatibility with RepositoryProvider + """ + + return {}.items() + + def get_packages(self, invalid_sources=None): + """ + Uses the BitBucket API to construct necessary info for a package + + :param invalid_sources: + A list of URLs that should be ignored + + :raises: + DownloaderException: when there is an issue download package info + ClientException: when there is an issue parsing package info + + :return: + A generator of + ( + 'Package Name', + { + 'name': name, + 'description': description, + 'author': author, + 'homepage': homepage, + 'last_modified': last modified date, + 'download': { + 'url': url, + 'date': date, + 'version': version + }, + 'previous_names': [], + 'labels': [], + 'sources': [the repo URL], + 'readme': url, + 'issues': url, + 'donate': url, + 'buy': None + } + ) + tuples + """ + + if 'get_packages' in self.cache: + for key, value in self.cache['get_packages'].items(): + yield (key, value) + return + + client = BitBucketClient(self.settings) + + if invalid_sources != None and self.repo in invalid_sources: + raise StopIteration() + + try: + repo_info = client.repo_info(self.repo) + download = client.download_info(self.repo) + + name = repo_info['name'] + details = { + 'name': name, + 'description': repo_info['description'], + 'homepage': repo_info['homepage'], + 'author': repo_info['author'], + 'last_modified': download.get('date'), + 'download': download, + 'previous_names': [], + 'labels': [], + 'sources': [self.repo], + 'readme': repo_info['readme'], + 'issues': repo_info['issues'], + 'donate': repo_info['donate'], + 'buy': None + } + self.cache['get_packages'] = {name: details} + yield (name, details) + + except (DownloaderException, ClientException, ProviderException) as e: + self.failed_sources[self.repo] = e + self.cache['get_packages'] = {} + raise StopIteration() + + def get_renamed_packages(self): + """For API-compatibility with RepositoryProvider""" + + return {} + + def get_unavailable_packages(self): + """ + Method for compatibility with RepositoryProvider class. These providers + are based on API calls, and thus do not support different platform + downloads, making it impossible for there to be unavailable packages. + + :return: An empty list + """ + return [] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/channel_provider.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/channel_provider.py new file mode 100644 index 0000000..5543bdc --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/channel_provider.py @@ -0,0 +1,312 @@ +import json +import os +import re + +try: + # Python 3 + from urllib.parse import urlparse +except (ImportError): + # Python 2 + from urlparse import urlparse + +from ..console_write import console_write +from .release_selector import ReleaseSelector +from .provider_exception import ProviderException +from ..downloaders.downloader_exception import DownloaderException +from ..clients.client_exception import ClientException +from ..download_manager import downloader + + +class ChannelProvider(ReleaseSelector): + """ + Retrieves a channel and provides an API into the information + + The current channel/repository infrastructure caches repository info into + the channel to improve the Package Control client performance. This also + has the side effect of lessening the load on the GitHub and BitBucket APIs + and getting around not-infrequent HTTP 503 errors from those APIs. + + :param channel: + The URL of the channel + + :param settings: + A dict containing at least the following fields: + `cache_length`, + `debug`, + `timeout`, + `user_agent` + Optional fields: + `http_proxy`, + `https_proxy`, + `proxy_username`, + `proxy_password`, + `query_string_params` + `install_prereleases` + """ + + def __init__(self, channel, settings): + self.channel_info = None + self.schema_version = 0.0 + self.channel = channel + self.settings = settings + self.unavailable_packages = [] + + @classmethod + def match_url(cls, channel): + """Indicates if this provider can handle the provided channel""" + + return True + + def prefetch(self): + """ + Go out and perform HTTP operations, caching the result + + :raises: + ProviderException: when an error occurs trying to open a file + DownloaderException: when an error occurs trying to open a URL + """ + + self.fetch() + + def fetch(self): + """ + Retrieves and loads the JSON for other methods to use + + :raises: + ProviderException: when an error occurs with the channel contents + DownloaderException: when an error occurs trying to open a URL + """ + + if self.channel_info != None: + return + + if re.match('https?://', self.channel, re.I): + with downloader(self.channel, self.settings) as manager: + channel_json = manager.fetch(self.channel, + 'Error downloading channel.') + + # All other channels are expected to be filesystem paths + else: + if not os.path.exists(self.channel): + raise ProviderException(u'Error, file %s does not exist' % self.channel) + + if self.settings.get('debug'): + console_write(u'Loading %s as a channel' % self.channel, True) + + # We open as binary so we get bytes like the DownloadManager + with open(self.channel, 'rb') as f: + channel_json = f.read() + + try: + channel_info = json.loads(channel_json.decode('utf-8')) + except (ValueError): + raise ProviderException(u'Error parsing JSON from channel %s.' % self.channel) + + schema_error = u'Channel %s does not appear to be a valid channel file because ' % self.channel + + if 'schema_version' not in channel_info: + raise ProviderException(u'%s the "schema_version" JSON key is missing.' % schema_error) + + try: + self.schema_version = float(channel_info.get('schema_version')) + except (ValueError): + raise ProviderException(u'%s the "schema_version" is not a valid number.' % schema_error) + + if self.schema_version not in [1.0, 1.1, 1.2, 2.0]: + raise ProviderException(u'%s the "schema_version" is not recognized. Must be one of: 1.0, 1.1, 1.2 or 2.0.' % schema_error) + + self.channel_info = channel_info + + def get_name_map(self): + """ + :raises: + ProviderException: when an error occurs with the channel contents + DownloaderException: when an error occurs trying to open a URL + + :return: + A dict of the mapping for URL slug -> package name + """ + + self.fetch() + + if self.schema_version >= 2.0: + return {} + + return self.channel_info.get('package_name_map', {}) + + def get_renamed_packages(self): + """ + :raises: + ProviderException: when an error occurs with the channel contents + DownloaderException: when an error occurs trying to open a URL + + :return: + A dict of the packages that have been renamed + """ + + self.fetch() + + if self.schema_version >= 2.0: + return {} + + return self.channel_info.get('renamed_packages', {}) + + def get_repositories(self): + """ + :raises: + ProviderException: when an error occurs with the channel contents + DownloaderException: when an error occurs trying to open a URL + + :return: + A list of the repository URLs + """ + + self.fetch() + + if 'repositories' not in self.channel_info: + raise ProviderException(u'Channel %s does not appear to be a valid channel file because the "repositories" JSON key is missing.' % self.channel) + + # Determine a relative root so repositories can be defined + # relative to the location of the channel file. + if re.match('https?://', self.channel, re.I): + url_pieces = urlparse(self.channel) + domain = url_pieces.scheme + '://' + url_pieces.netloc + path = '/' if url_pieces.path == '' else url_pieces.path + if path[-1] != '/': + path = os.path.dirname(path) + relative_base = domain + path + else: + relative_base = os.path.dirname(self.channel) + '/' + + output = [] + repositories = self.channel_info.get('repositories', []) + for repository in repositories: + if re.match('^\./|\.\./', repository): + repository = os.path.normpath(relative_base + repository) + output.append(repository) + + return output + + def get_certs(self): + """ + Provides a secure way for distribution of SSL CA certificates + + Unfortunately Python does not include a bundle of CA certs with urllib + to perform SSL certificate validation. To circumvent this issue, + Package Control acts as a distributor of the CA certs for all HTTPS + URLs of package downloads. + + The default channel scrapes and caches info about all packages + periodically, and in the process it checks the CA certs for all of + the HTTPS URLs listed in the repositories. The contents of the CA cert + files are then hashed, and the CA cert is stored in a filename with + that hash. This is a fingerprint to ensure that Package Control has + the appropriate CA cert for a domain name. + + Next, the default channel file serves up a JSON object of the domain + names and the hashes of their current CA cert files. If Package Control + does not have the appropriate hash for a domain, it may retrieve it + from the channel server. To ensure that Package Control is talking to + a trusted authority to get the CA certs from, the CA cert for + sublime.wbond.net is bundled with Package Control. Then when downloading + the channel file, Package Control can ensure that the channel file's + SSL certificate is valid, thus ensuring the resulting CA certs are + legitimate. + + As a matter of optimization, the distribution of Package Control also + includes the current CA certs for all known HTTPS domains that are + included in the channel, as of the time when Package Control was + last released. + + :raises: + ProviderException: when an error occurs with the channel contents + DownloaderException: when an error occurs trying to open a URL + + :return: + A dict of {'Domain Name': ['cert_file_hash', 'cert_file_download_url']} + """ + + self.fetch() + + return self.channel_info.get('certs', {}) + + def get_packages(self, repo): + """ + Provides access to the repository info that is cached in a channel + + :param repo: + The URL of the repository to get the cached info of + + :raises: + ProviderException: when an error occurs with the channel contents + DownloaderException: when an error occurs trying to open a URL + + :return: + A dict in the format: + { + 'Package Name': { + 'name': name, + 'description': description, + 'author': author, + 'homepage': homepage, + 'last_modified': last modified date, + 'download': { + 'url': url, + 'date': date, + 'version': version + }, + 'previous_names': [old_name, ...], + 'labels': [label, ...], + 'readme': url, + 'issues': url, + 'donate': url, + 'buy': url + }, + ... + } + """ + + self.fetch() + + # The 2.0 channel schema renamed the key cached package info was + # stored under in order to be more clear to new users. + packages_key = 'packages_cache' if self.schema_version >= 2.0 else 'packages' + + if self.channel_info.get(packages_key, False) == False: + return {} + + if self.channel_info[packages_key].get(repo, False) == False: + return {} + + output = {} + for package in self.channel_info[packages_key][repo]: + copy = package.copy() + + # In schema version 2.0, we store a list of dicts containing info + # about all available releases. These include "version" and + # "platforms" keys that are used to pick the download for the + # current machine. + if self.schema_version >= 2.0: + copy = self.select_release(copy) + else: + copy = self.select_platform(copy) + + if not copy: + self.unavailable_packages.append(package['name']) + continue + + output[copy['name']] = copy + + return output + + def get_unavailable_packages(self): + """ + Provides a list of packages that are unavailable for the current + platform/architecture that Sublime Text is running on. + + This list will be empty unless get_packages() is called first. + + :return: A list of package names + """ + + return self.unavailable_packages diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/github_repository_provider.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/github_repository_provider.py new file mode 100644 index 0000000..158c850 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/github_repository_provider.py @@ -0,0 +1,169 @@ +import re + +from ..clients.github_client import GitHubClient +from ..downloaders.downloader_exception import DownloaderException +from ..clients.client_exception import ClientException +from .provider_exception import ProviderException + + +class GitHubRepositoryProvider(): + """ + Allows using a public GitHub repository as the source for a single package. + For legacy purposes, this can also be treated as the source for a Package + Control "repository". + + :param repo: + The public web URL to the GitHub repository. Should be in the format + `https://github.com/user/package` for the master branch, or + `https://github.com/user/package/tree/{branch_name}` for any other + branch. + + :param settings: + A dict containing at least the following fields: + `cache_length`, + `debug`, + `timeout`, + `user_agent` + Optional fields: + `http_proxy`, + `https_proxy`, + `proxy_username`, + `proxy_password`, + `query_string_params` + `install_prereleases` + """ + + def __init__(self, repo, settings): + self.cache = {} + # Clean off the trailing .git to be more forgiving + self.repo = re.sub('\.git$', '', repo) + self.settings = settings + self.failed_sources = {} + + @classmethod + def match_url(cls, repo): + """Indicates if this provider can handle the provided repo""" + + master = re.search('^https?://github.com/[^/]+/[^/]+/?$', repo) + branch = re.search('^https?://github.com/[^/]+/[^/]+/tree/[^/]+/?$', + repo) + return master != None or branch != None + + def prefetch(self): + """ + Go out and perform HTTP operations, caching the result + + :raises: + DownloaderException: when there is an issue download package info + ClientException: when there is an issue parsing package info + """ + + [name for name, info in self.get_packages()] + + def get_failed_sources(self): + """ + List of any URLs that could not be accessed while accessing this repository + + :return: + A generator of ("https://github.com/user/repo", Exception()) tuples + """ + + return self.failed_sources.items() + + def get_broken_packages(self): + """ + For API-compatibility with RepositoryProvider + """ + + return {}.items() + + def get_packages(self, invalid_sources=None): + """ + Uses the GitHub API to construct necessary info for a package + + :param invalid_sources: + A list of URLs that should be ignored + + :raises: + DownloaderException: when there is an issue download package info + ClientException: when there is an issue parsing package info + + :return: + A generator of + ( + 'Package Name', + { + 'name': name, + 'description': description, + 'author': author, + 'homepage': homepage, + 'last_modified': last modified date, + 'download': { + 'url': url, + 'date': date, + 'version': version + }, + 'previous_names': [], + 'labels': [], + 'sources': [the repo URL], + 'readme': url, + 'issues': url, + 'donate': url, + 'buy': None + } + ) + tuples + """ + + if 'get_packages' in self.cache: + for key, value in self.cache['get_packages'].items(): + yield (key, value) + return + + client = GitHubClient(self.settings) + + if invalid_sources != None and self.repo in invalid_sources: + raise StopIteration() + + try: + repo_info = client.repo_info(self.repo) + download = client.download_info(self.repo) + + name = repo_info['name'] + details = { + 'name': name, + 'description': repo_info['description'], + 'homepage': repo_info['homepage'], + 'author': repo_info['author'], + 'last_modified': download.get('date'), + 'download': download, + 'previous_names': [], + 'labels': [], + 'sources': [self.repo], + 'readme': repo_info['readme'], + 'issues': repo_info['issues'], + 'donate': repo_info['donate'], + 'buy': None + } + self.cache['get_packages'] = {name: details} + yield (name, details) + + except (DownloaderException, ClientException, ProviderException) as e: + self.failed_sources[self.repo] = e + self.cache['get_packages'] = {} + raise StopIteration() + + def get_renamed_packages(self): + """For API-compatibility with RepositoryProvider""" + + return {} + + def get_unavailable_packages(self): + """ + Method for compatibility with RepositoryProvider class. These providers + are based on API calls, and thus do not support different platform + downloads, making it impossible for there to be unavailable packages. + + :return: An empty list + """ + return [] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/github_user_provider.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/github_user_provider.py new file mode 100644 index 0000000..6af60be --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/github_user_provider.py @@ -0,0 +1,172 @@ +import re + +from ..clients.github_client import GitHubClient +from ..downloaders.downloader_exception import DownloaderException +from ..clients.client_exception import ClientException +from .provider_exception import ProviderException + + +class GitHubUserProvider(): + """ + Allows using a GitHub user/organization as the source for multiple packages, + or in Package Control terminology, a "repository". + + :param repo: + The public web URL to the GitHub user/org. Should be in the format + `https://github.com/user`. + + :param settings: + A dict containing at least the following fields: + `cache_length`, + `debug`, + `timeout`, + `user_agent`, + Optional fields: + `http_proxy`, + `https_proxy`, + `proxy_username`, + `proxy_password`, + `query_string_params` + `install_prereleases` + """ + + def __init__(self, repo, settings): + self.cache = {} + self.repo = repo + self.settings = settings + self.failed_sources = {} + + @classmethod + def match_url(cls, repo): + """Indicates if this provider can handle the provided repo""" + + return re.search('^https?://github.com/[^/]+/?$', repo) != None + + def prefetch(self): + """ + Go out and perform HTTP operations, caching the result + """ + + [name for name, info in self.get_packages()] + + def get_failed_sources(self): + """ + List of any URLs that could not be accessed while accessing this repository + + :raises: + DownloaderException: when there is an issue download package info + ClientException: when there is an issue parsing package info + + :return: + A generator of ("https://github.com/user/repo", Exception()) tuples + """ + + return self.failed_sources.items() + + def get_broken_packages(self): + """ + For API-compatibility with RepositoryProvider + """ + + return {}.items() + + def get_packages(self, invalid_sources=None): + """ + Uses the GitHub API to construct necessary info for all packages + + :param invalid_sources: + A list of URLs that should be ignored + + :raises: + DownloaderException: when there is an issue download package info + ClientException: when there is an issue parsing package info + + :return: + A generator of + ( + 'Package Name', + { + 'name': name, + 'description': description, + 'author': author, + 'homepage': homepage, + 'last_modified': last modified date, + 'download': { + 'url': url, + 'date': date, + 'version': version + }, + 'previous_names': [], + 'labels': [], + 'sources': [the user URL], + 'readme': url, + 'issues': url, + 'donate': url, + 'buy': None + } + ) + tuples + """ + + if 'get_packages' in self.cache: + for key, value in self.cache['get_packages'].items(): + yield (key, value) + return + + client = GitHubClient(self.settings) + + if invalid_sources != None and self.repo in invalid_sources: + raise StopIteration() + + try: + user_repos = client.user_info(self.repo) + except (DownloaderException, ClientException, ProviderException) as e: + self.failed_sources = [self.repo] + self.cache['get_packages'] = e + raise e + + output = {} + for repo_info in user_repos: + try: + name = repo_info['name'] + repo_url = 'https://github.com/' + repo_info['user_repo'] + + download = client.download_info(repo_url) + + details = { + 'name': name, + 'description': repo_info['description'], + 'homepage': repo_info['homepage'], + 'author': repo_info['author'], + 'last_modified': download.get('date'), + 'download': download, + 'previous_names': [], + 'labels': [], + 'sources': [self.repo], + 'readme': repo_info['readme'], + 'issues': repo_info['issues'], + 'donate': repo_info['donate'], + 'buy': None + } + output[name] = details + yield (name, details) + + except (DownloaderException, ClientException, ProviderException) as e: + self.failed_sources[repo_url] = e + + self.cache['get_packages'] = output + + def get_renamed_packages(self): + """For API-compatibility with RepositoryProvider""" + + return {} + + def get_unavailable_packages(self): + """ + Method for compatibility with RepositoryProvider class. These providers + are based on API calls, and thus do not support different platform + downloads, making it impossible for there to be unavailable packages. + + :return: An empty list + """ + return [] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/provider_exception.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/provider_exception.py new file mode 100644 index 0000000..e98295f --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/provider_exception.py @@ -0,0 +1,5 @@ +class ProviderException(Exception): + """If a provider could not return information""" + + def __str__(self): + return self.args[0] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/release_selector.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/release_selector.py new file mode 100644 index 0000000..5305468 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/release_selector.py @@ -0,0 +1,125 @@ +import re +import sublime + +from ..versions import version_sort, version_exclude_prerelease + + +class ReleaseSelector(): + """ + A base class for finding the best version of a package for the current machine + """ + + def select_release(self, package_info): + """ + Returns a modified package info dict for package from package schema version 2.0 + + :param package_info: + A package info dict with a "releases" key + + :return: + The package info dict with the "releases" key deleted, and a + "download" key added that contains a dict with "version", "url" and + "date" keys. + None if no compatible relases are available. + """ + + releases = version_sort(package_info['releases']) + if not self.settings.get('install_prereleases'): + releases = version_exclude_prerelease(releases) + + for release in releases: + platforms = release.get('platforms', '*') + if not isinstance(platforms, list): + platforms = [platforms] + + best_platform = self.get_best_platform(platforms) + if not best_platform: + continue + + if not self.is_compatible_version(release.get('sublime_text', '<3000')): + continue + + package_info['download'] = release + package_info['last_modified'] = release.get('date') + del package_info['releases'] + + return package_info + + return None + + def select_platform(self, package_info): + """ + Returns a modified package info dict for package from package schema version <= 1.2 + + :param package_info: + A package info dict with a "platforms" key + + :return: + The package info dict with the "platforms" key deleted, and a + "download" key added that contains a dict with "version" and "url" + keys. + None if no compatible platforms. + """ + platforms = list(package_info['platforms'].keys()) + best_platform = self.get_best_platform(platforms) + if not best_platform: + return None + + package_info['download'] = package_info['platforms'][best_platform][0] + package_info['download']['date'] = package_info.get('last_modified') + del package_info['platforms'] + + return package_info + + def get_best_platform(self, platforms): + """ + Returns the most specific platform that matches the current machine + + :param platforms: + An array of platform names for a package. E.g. ['*', 'windows', 'linux-x64'] + + :return: A string reprenting the most specific matching platform + """ + + ids = [sublime.platform() + '-' + sublime.arch(), sublime.platform(), + '*'] + + for id in ids: + if id in platforms: + return id + + return None + + def is_compatible_version(self, version_range): + min_version = float("-inf") + max_version = float("inf") + + if version_range == '*': + return True + + gt_match = re.match('>(\d+)$', version_range) + ge_match = re.match('>=(\d+)$', version_range) + lt_match = re.match('<(\d+)$', version_range) + le_match = re.match('<=(\d+)$', version_range) + range_match = re.match('(\d+) - (\d+)$', version_range) + + if gt_match: + min_version = int(gt_match.group(1)) + 1 + elif ge_match: + min_version = int(ge_match.group(1)) + elif lt_match: + max_version = int(lt_match.group(1)) - 1 + elif le_match: + max_version = int(le_match.group(1)) + elif range_match: + min_version = int(range_match.group(1)) + max_version = int(range_match.group(2)) + else: + return None + + if min_version > int(sublime.version()): + return False + if max_version < int(sublime.version()): + return False + + return True diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/repository_provider.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/repository_provider.py new file mode 100644 index 0000000..b94a9dd --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/providers/repository_provider.py @@ -0,0 +1,454 @@ +import json +import re +import os +from itertools import chain + +try: + # Python 3 + from urllib.parse import urlparse +except (ImportError): + # Python 2 + from urlparse import urlparse + +from ..console_write import console_write +from .release_selector import ReleaseSelector +from .provider_exception import ProviderException +from ..downloaders.downloader_exception import DownloaderException +from ..clients.client_exception import ClientException +from ..clients.github_client import GitHubClient +from ..clients.bitbucket_client import BitBucketClient +from ..download_manager import downloader + + +class RepositoryProvider(ReleaseSelector): + """ + Generic repository downloader that fetches package info + + With the current channel/repository architecture where the channel file + caches info from all includes repositories, these package providers just + serve the purpose of downloading packages not in the default channel. + + The structure of the JSON a repository should contain is located in + example-packages.json. + + :param repo: + The URL of the package repository + + :param settings: + A dict containing at least the following fields: + `cache_length`, + `debug`, + `timeout`, + `user_agent` + Optional fields: + `http_proxy`, + `https_proxy`, + `proxy_username`, + `proxy_password`, + `query_string_params` + `install_prereleases` + """ + + def __init__(self, repo, settings): + self.cache = {} + self.repo_info = None + self.schema_version = 0.0 + self.repo = repo + self.settings = settings + self.unavailable_packages = [] + self.failed_sources = {} + self.broken_packages = {} + + @classmethod + def match_url(cls, repo): + """Indicates if this provider can handle the provided repo""" + + return True + + def prefetch(self): + """ + Go out and perform HTTP operations, caching the result + + :raises: + DownloaderException: when there is an issue download package info + ClientException: when there is an issue parsing package info + """ + + [name for name, info in self.get_packages()] + + def get_failed_sources(self): + """ + List of any URLs that could not be accessed while accessing this repository + + :return: + A generator of ("https://example.com", Exception()) tuples + """ + + return self.failed_sources.items() + + def get_broken_packages(self): + """ + List of package names for packages that are missing information + + :return: + A generator of ("Package Name", Exception()) tuples + """ + + return self.broken_packages.items() + + def fetch(self): + """ + Retrieves and loads the JSON for other methods to use + + :raises: + ProviderException: when an error occurs trying to open a file + DownloaderException: when an error occurs trying to open a URL + """ + + if self.repo_info != None: + return + + self.repo_info = self.fetch_location(self.repo) + + if 'includes' not in self.repo_info: + return + + # Allow repositories to include other repositories + if re.match('https?://', self.repo, re.I): + url_pieces = urlparse(self.repo) + domain = url_pieces.scheme + '://' + url_pieces.netloc + path = '/' if url_pieces.path == '' else url_pieces.path + if path[-1] != '/': + path = os.path.dirname(path) + relative_base = domain + path + else: + relative_base = os.path.dirname(self.repo) + '/' + + includes = self.repo_info.get('includes', []) + del self.repo_info['includes'] + for include in includes: + if re.match('^\./|\.\./', include): + include = os.path.normpath(relative_base + include) + include_info = self.fetch_location(include) + included_packages = include_info.get('packages', []) + self.repo_info['packages'].extend(included_packages) + + def fetch_location(self, location): + """ + Fetches the contents of a URL of file path + + :param location: + The URL or file path + + :raises: + ProviderException: when an error occurs trying to open a file + DownloaderException: when an error occurs trying to open a URL + + :return: + A dict of the parsed JSON + """ + + if re.match('https?://', self.repo, re.I): + with downloader(location, self.settings) as manager: + json_string = manager.fetch(location, 'Error downloading repository.') + + # Anything that is not a URL is expected to be a filesystem path + else: + if not os.path.exists(location): + raise ProviderException(u'Error, file %s does not exist' % location) + + if self.settings.get('debug'): + console_write(u'Loading %s as a repository' % location, True) + + # We open as binary so we get bytes like the DownloadManager + with open(location, 'rb') as f: + json_string = f.read() + + try: + return json.loads(json_string.decode('utf-8')) + except (ValueError): + raise ProviderException(u'Error parsing JSON from repository %s.' % location) + + def get_packages(self, invalid_sources=None): + """ + Provides access to the packages in this repository + + :param invalid_sources: + A list of URLs that are permissible to fetch data from + + :raises: + ProviderException: when an error occurs trying to open a file + DownloaderException: when there is an issue download package info + ClientException: when there is an issue parsing package info + + :return: + A generator of + ( + 'Package Name', + { + 'name': name, + 'description': description, + 'author': author, + 'homepage': homepage, + 'last_modified': last modified date, + 'download': { + 'url': url, + 'date': date, + 'version': version + }, + 'previous_names': [old_name, ...], + 'labels': [label, ...], + 'sources': [url, ...], + 'readme': url, + 'issues': url, + 'donate': url, + 'buy': url + } + ) + tuples + """ + + if 'get_packages' in self.cache: + for key, value in self.cache['get_packages'].items(): + yield (key, value) + return + + if invalid_sources != None and self.repo in invalid_sources: + raise StopIteration() + + try: + self.fetch() + except (DownloaderException, ProviderException) as e: + self.failed_sources[self.repo] = e + self.cache['get_packages'] = {} + return + + def fail(message): + exception = ProviderException(message) + self.failed_sources[self.repo] = exception + self.cache['get_packages'] = {} + return + schema_error = u'Repository %s does not appear to be a valid repository file because ' % self.repo + + if 'schema_version' not in self.repo_info: + error_string = u'%s the "schema_version" JSON key is missing.' % schema_error + fail(error_string) + return + + try: + self.schema_version = float(self.repo_info.get('schema_version')) + except (ValueError): + error_string = u'%s the "schema_version" is not a valid number.' % schema_error + fail(error_string) + return + + if self.schema_version not in [1.0, 1.1, 1.2, 2.0]: + error_string = u'%s the "schema_version" is not recognized. Must be one of: 1.0, 1.1, 1.2 or 2.0.' % schema_error + fail(error_string) + return + + if 'packages' not in self.repo_info: + error_string = u'%s the "packages" JSON key is missing.' % schema_error + fail(error_string) + return + + github_client = GitHubClient(self.settings) + bitbucket_client = BitBucketClient(self.settings) + + # Backfill the "previous_names" keys for old schemas + previous_names = {} + if self.schema_version < 2.0: + renamed = self.get_renamed_packages() + for old_name in renamed: + new_name = renamed[old_name] + if new_name not in previous_names: + previous_names[new_name] = [] + previous_names[new_name].append(old_name) + + output = {} + for package in self.repo_info['packages']: + info = { + 'sources': [self.repo] + } + + for field in ['name', 'description', 'author', 'last_modified', 'previous_names', + 'labels', 'homepage', 'readme', 'issues', 'donate', 'buy']: + if package.get(field): + info[field] = package.get(field) + + # Schema version 2.0 allows for grabbing details about a pacakge, or its + # download from "details" urls. See the GitHubClient and BitBucketClient + # classes for valid URLs. + if self.schema_version >= 2.0: + details = package.get('details') + releases = package.get('releases') + + # Try to grab package-level details from GitHub or BitBucket + if details: + if invalid_sources != None and details in invalid_sources: + continue + + info['sources'].append(details) + + try: + github_repo_info = github_client.repo_info(details) + bitbucket_repo_info = bitbucket_client.repo_info(details) + + # When grabbing details, prefer explicit field values over the values + # from the GitHub or BitBucket API + if github_repo_info: + info = dict(chain(github_repo_info.items(), info.items())) + elif bitbucket_repo_info: + info = dict(chain(bitbucket_repo_info.items(), info.items())) + else: + raise ProviderException(u'Invalid "details" value "%s" for one of the packages in the repository %s.' % (details, self.repo)) + + except (DownloaderException, ClientException, ProviderException) as e: + if 'name' in info: + self.broken_packages[info['name']] = e + self.failed_sources[details] = e + continue + + # If no releases info was specified, also grab the download info from GH or BB + if not releases and details: + releases = [{'details': details}] + + if not releases: + e = ProviderException(u'No "releases" value for one of the packages in the repository %s.' % self.repo) + if 'name' in info: + self.broken_packages[info['name']] = e + else: + self.failed_sources[self.repo] = e + continue + + # This allows developers to specify a GH or BB location to get releases from, + # especially tags URLs (https://github.com/user/repo/tags or + # https://bitbucket.org/user/repo#tags) + info['releases'] = [] + for release in releases: + download_details = None + download_info = {} + + # Make sure that explicit fields are copied over + for field in ['platforms', 'sublime_text', 'version', 'url', 'date']: + if field in release: + download_info[field] = release[field] + + if 'details' in release: + download_details = release['details'] + + try: + github_download = github_client.download_info(download_details) + bitbucket_download = bitbucket_client.download_info(download_details) + + # Overlay the explicit field values over values fetched from the APIs + if github_download: + download_info = dict(chain(github_download.items(), download_info.items())) + # No matching tags + elif github_download == False: + download_info = {} + elif bitbucket_download: + download_info = dict(chain(bitbucket_download.items(), download_info.items())) + # No matching tags + elif bitbucket_download == False: + download_info = {} + else: + raise ProviderException(u'Invalid "details" value "%s" under the "releases" key for the package "%s" in the repository %s.' % (download_details, info['name'], self.repo)) + + except (DownloaderException, ClientException, ProviderException) as e: + if 'name' in info: + self.broken_packages[info['name']] = e + self.failed_sources[download_details] = e + continue + + if download_info: + info['releases'].append(download_info) + + info = self.select_release(info) + + # Schema version 1.0, 1.1 and 1.2 just require that all values be + # explicitly specified in the package JSON + else: + info['platforms'] = package.get('platforms') + info = self.select_platform(info) + + if not info: + self.unavailable_packages.append(package['name']) + continue + + if 'download' not in info and 'releases' not in info: + self.broken_packages[info['name']] = ProviderException(u'No "releases" key for the package "%s" in the repository %s.' % (info['name'], self.repo)) + continue + + for field in ['previous_names', 'labels']: + if field not in info: + info[field] = [] + + for field in ['readme', 'issues', 'donate', 'buy']: + if field not in info: + info[field] = None + + if 'homepage' not in info: + info['homepage'] = self.repo + + if 'download' in info: + # Rewrites the legacy "zipball" URLs to the new "zip" format + info['download']['url'] = re.sub( + '^(https://nodeload.github.com/[^/]+/[^/]+/)zipball(/.*)$', + '\\1zip\\2', info['download']['url']) + + # Rewrites the legacy "nodeload" URLs to the new "codeload" subdomain + info['download']['url'] = info['download']['url'].replace( + 'nodeload.github.com', 'codeload.github.com') + + # Extract the date from the download + if 'last_modified' not in info: + info['last_modified'] = info['download']['date'] + + elif 'releases' in info and 'last_modified' not in info: + # Extract a date from the newest download + date = '1970-01-01 00:00:00' + for release in info['releases']: + if 'date' in release and release['date'] > date: + date = release['date'] + info['last_modified'] = date + + if info['name'] in previous_names: + info['previous_names'].extend(previous_names[info['name']]) + + output[info['name']] = info + yield (info['name'], info) + + self.cache['get_packages'] = output + + def get_renamed_packages(self): + """:return: A dict of the packages that have been renamed""" + + if self.schema_version < 2.0: + return self.repo_info.get('renamed_packages', {}) + + output = {} + for package in self.repo_info['packages']: + if 'previous_names' not in package: + continue + + previous_names = package['previous_names'] + if not isinstance(previous_names, list): + previous_names = [previous_names] + + for previous_name in previous_names: + output[previous_name] = package['name'] + + return output + + def get_unavailable_packages(self): + """ + Provides a list of packages that are unavailable for the current + platform/architecture that Sublime Text is running on. + + This list will be empty unless get_packages() is called first. + + :return: A list of package names + """ + + return self.unavailable_packages diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/reloader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/reloader.py new file mode 100644 index 0000000..0696022 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/reloader.py @@ -0,0 +1,130 @@ +import sys + +import sublime + + +st_version = 2 +# With the way ST3 works, the sublime module is not "available" at startup +# which results in an empty version number +if sublime.version() == '' or int(sublime.version()) > 3000: + st_version = 3 + from imp import reload + + +# Python allows reloading modules on the fly, which allows us to do live upgrades. +# The only caveat to this is that you have to reload in the dependency order. +# +# Thus is module A depends on B and we don't reload B before A, when A is reloaded +# it will still have a reference to the old B. Thus we hard-code the dependency +# order of the various Package Control modules so they get reloaded properly. +# +# There are solutions for doing this all programatically, but this is much easier +# to understand. + +reload_mods = [] +for mod in sys.modules: + if mod[0:15].lower().replace(' ', '_') == 'package_control' and sys.modules[mod] != None: + reload_mods.append(mod) + +mod_prefix = 'package_control' +if st_version == 3: + mod_prefix = 'Package Control.' + mod_prefix + +mods_load_order = [ + '', + + '.sys_path', + '.cache', + '.http_cache', + '.ca_certs', + '.clear_directory', + '.cmd', + '.console_write', + '.preferences_filename', + '.show_error', + '.unicode', + '.thread_progress', + '.package_io', + '.semver', + '.versions', + + '.http', + '.http.invalid_certificate_exception', + '.http.debuggable_http_response', + '.http.debuggable_https_response', + '.http.debuggable_http_connection', + '.http.persistent_handler', + '.http.debuggable_http_handler', + '.http.validating_https_connection', + '.http.validating_https_handler', + + '.clients', + '.clients.client_exception', + '.clients.bitbucket_client', + '.clients.github_client', + '.clients.readme_client', + '.clients.json_api_client', + + '.providers', + '.providers.provider_exception', + '.providers.bitbucket_repository_provider', + '.providers.channel_provider', + '.providers.github_repository_provider', + '.providers.github_user_provider', + '.providers.repository_provider', + '.providers.release_selector', + + '.download_manager', + + '.downloaders', + '.downloaders.downloader_exception', + '.downloaders.rate_limit_exception', + '.downloaders.binary_not_found_error', + '.downloaders.non_clean_exit_error', + '.downloaders.non_http_error', + '.downloaders.caching_downloader', + '.downloaders.decoding_downloader', + '.downloaders.limiting_downloader', + '.downloaders.cert_provider', + '.downloaders.urllib_downloader', + '.downloaders.cli_downloader', + '.downloaders.curl_downloader', + '.downloaders.wget_downloader', + '.downloaders.wininet_downloader', + '.downloaders.background_downloader', + + '.upgraders', + '.upgraders.vcs_upgrader', + '.upgraders.git_upgrader', + '.upgraders.hg_upgrader', + + '.package_manager', + '.package_creator', + '.package_installer', + '.package_renamer', + + '.commands', + '.commands.add_channel_command', + '.commands.add_repository_command', + '.commands.create_binary_package_command', + '.commands.create_package_command', + '.commands.disable_package_command', + '.commands.discover_packages_command', + '.commands.enable_package_command', + '.commands.existing_packages_command', + '.commands.grab_certs_command', + '.commands.install_package_command', + '.commands.list_packages_command', + '.commands.package_message_command', + '.commands.remove_package_command', + '.commands.upgrade_all_packages_command', + '.commands.upgrade_package_command', + + '.package_cleanup', + '.automatic_upgrader' +] + +for suffix in mods_load_order: + mod = mod_prefix + suffix + if mod in reload_mods: + reload(sys.modules[mod]) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/semver.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/semver.py new file mode 100644 index 0000000..917fa77 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/semver.py @@ -0,0 +1,833 @@ +"""pysemver: Semantic Version comparing for Python. + +Provides comparing of semantic versions by using SemVer objects using rich comperations plus the +possibility to match a selector string against versions. Interesting for version dependencies. +Versions look like: "1.7.12+b.133" +Selectors look like: ">1.7.0 || 1.6.9+b.111 - 1.6.9+b.113" + +Example usages: + >>> SemVer(1, 2, 3, build=13) + SemVer("1.2.3+13") + >>> SemVer.valid("1.2.3.4") + False + >>> SemVer.clean("this is unimportant text 1.2.3-2 and will be stripped") + "1.2.3-2" + >>> SemVer("1.7.12+b.133").satisfies(">1.7.0 || 1.6.9+b.111 - 1.6.9+b.113") + True + >>> SemSel(">1.7.0 || 1.6.9+b.111 - 1.6.9+b.113").matches(SemVer("1.7.12+b.133"), + ... SemVer("1.6.9+b.112"), SemVer("1.6.10")) + [SemVer("1.7.12+b.133"), SemVer("1.6.9+b.112")] + >>> min(_) + SemVer("1.6.9+b.112") + >>> _.patch + 9 + +Exported classes: + * SemVer(collections.namedtuple()) + Parses semantic versions and defines methods for them. Supports rich comparisons. + * SemSel(tuple) + Parses semantic version selector strings and defines methods for them. + * SelParseError(Exception) + An error among others raised when parsing a semantic version selector failed. + +Other classes: + * SemComparator(object) + * SemSelAndChunk(list) + * SemSelOrChunk(list) + +Functions/Variables/Constants: + none + + +Copyright (c) 2013 Zachary King, FichteFoll + +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. +""" + +import re +import sys +from collections import namedtuple # Python >=2.6 + + +__all__ = ('SemVer', 'SemSel', 'SelParseError') + + +if sys.version_info[0] == 3: + basestring = str + cmp = lambda a, b: (a > b) - (a < b) + + +# @functools.total_ordering would be nice here but was added in 2.7, __cmp__ is not Py3 +class SemVer(namedtuple("_SemVer", 'major, minor, patch, prerelease, build')): + """Semantic Version, consists of 3 to 5 components defining the version's adicity. + + See http://semver.org/ (2.0.0-rc.1) for the standard mainly used for this implementation, few + changes have been made. + + Information on this particular class and their instances: + - Immutable and hashable. + - Subclasses `collections.namedtuple`. + - Always `True` in boolean context. + - len() returns an int between 3 and 5; 4 when a pre-release is set and 5 when a build is + set. Note: Still returns 5 when build is set but not pre-release. + - Parts of the semantic version can be accessed by integer indexing, key (string) indexing, + slicing and getting an attribute. Returned slices are tuple. Leading '-' and '+' of + optional components are not stripped. Supported keys/attributes: + major, minor, patch, prerelease, build. + + Examples: + s = SemVer("1.2.3-4.5+6") + s[2] == 3 + s[:3] == (1, 2, 3) + s['build'] == '-4.5' + s.major == 1 + + Short information on semantic version structure: + + Semantic versions consist of: + * a major component (numeric) + * a minor component (numeric) + * a patch component (numeric) + * a pre-release component [optional] + * a build component [optional] + + The pre-release component is indicated by a hyphen '-' and followed by alphanumeric[1] sequences + separated by dots '.'. Sequences are compared numerically if applicable (both sequences of two + versions are numeric) or lexicographically. May also include hyphens. The existence of a + pre-release component lowers the actual version; the shorter pre-release component is considered + lower. An 'empty' pre-release component is considered to be the least version for this + major-minor-patch combination (e.g. "1.0.0-"). + + The build component may follow the optional pre-release component and is indicated by a plus '+' + followed by sequences, just as the pre-release component. Comparing works similarly. However the + existence of a build component raises the actual version and may also raise a pre-release. An + 'empty' build component is considered to be the highest version for this + major-minor-patch-prerelease combination (e.g. "1.2.3+"). + + + [1]: Regexp for a sequence: r'[0-9A-Za-z-]+'. + """ + + # Static class variables + _base_regex = r'''(?x) + (?P[0-9]+) + \.(?P[0-9]+) + \.(?P[0-9]+) + (?:\-(?P(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?))? + (?:\+(?P(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?))?''' + _search_regex = re.compile(_base_regex) + _match_regex = re.compile('^%s$' % _base_regex) # required because of $ anchor + + # "Constructor" + def __new__(cls, *args, **kwargs): + """There are two different constructor styles that are allowed: + - Option 1 allows specification of a semantic version as a string and the option to "clean" + the string before parsing it. + - Option 2 allows specification of each component separately as one parameter. + + Note that all the parameters specified in the following sections can be passed either as + positional or as named parameters while considering the usual Python rules for this. As + such, `SemVer(1, 2, minor=1)` will result in an exception and not in `SemVer("1.1.2")`. + + Option 1: + Constructor examples: + SemVer("1.0.1") + SemVer("this version 1.0.1-pre.1 here", True) + SemVer(ver="0.0.9-pre-alpha+34", clean=False) + + Parameters: + * ver (str) + The string containing the version. + * clean = `False` (bool; optional) + If this is true in boolean context, `SemVer.clean(ver)` is called before + parsing. + + Option 2: + Constructor examples: + SemVer(1, 0, 1) + SemVer(1, '0', prerelease='pre-alpha', patch=1, build=34) + SemVer(**dict(minor=2, major=1, patch=3)) + + Parameters: + * major (int, str, float ...) + * minor (...) + * patch (...) + Major to patch components must be an integer or convertable to an int (e.g. a + string or another number type). + + * prerelease = `None` (str, int, float ...; optional) + * build = `None` (...; optional) + Pre-release and build components should be a string (or number) type. + Will be passed to `str()` if not already a string but the final string must + match '^[0-9A-Za-z.-]*$' + + Raises: + * TypeError + Invalid parameter type(s) or combination (e.g. option 1 and 2). + * ValueError + Invalid semantic version or option 2 parameters unconvertable. + """ + ver, clean, comps = None, False, None + kw, l = kwargs.copy(), len(args) + len(kwargs) + + def inv(): + raise TypeError("Invalid parameter combination: args=%s; kwargs=%s" % (args, kwargs)) + + # Do validation and parse the parameters + if l == 0 or l > 5: + raise TypeError("SemVer accepts at least 1 and at most 5 arguments (%d given)" % l) + + elif l < 3: + if len(args) == 2: + ver, clean = args + else: + ver = args[0] if args else kw.pop('ver', None) + clean = kw.pop('clean', clean) + if kw: + inv() + + else: + comps = list(args) + [kw.pop(cls._fields[k], None) for k in range(len(args), 5)] + if kw or any(comps[i] is None for i in range(3)): + inv() + + typecheck = (int,) * 3 + (basestring,) * 2 + for i, (v, t) in enumerate(zip(comps, typecheck)): + if v is None: + continue + elif not isinstance(v, t): + try: + if i < 3: + v = typecheck[i](v) + else: # The real `basestring` can not be instatiated (Py2) + v = str(v) + except ValueError as e: + # Modify the exception message. I can't believe this actually works + e.args = ("Parameter #%d must be of type %s or convertable" + % (i, t.__name__),) + raise + else: + comps[i] = v + if t is basestring and not re.match(r"^[0-9A-Za-z.-]*$", v): + raise ValueError("Build and pre-release strings must match '^[0-9A-Za-z.-]*$'") + + # Final adjustments + if not comps: + if ver is None or clean is None: + inv() + ver = clean and cls.clean(ver) or ver + comps = cls._parse(ver) + + # Create the obj + return super(SemVer, cls).__new__(cls, *comps) + + # Magic methods + def __str__(self): + return ('.'.join(map(str, self[:3])) + + ('-' + self.prerelease if self.prerelease is not None else '') + + ('+' + self.build if self.build is not None else '')) + + def __repr__(self): + # Use the shortest representation - what would you prefer? + return 'SemVer("%s")' % str(self) + # return 'SemVer(%s)' % ', '.join('%s=%r' % (k, getattr(self, k)) for k in self._fields) + + def __len__(self): + return 3 + (self.build is not None and 2 or self.prerelease is not None) + + # Magic rich comparing methods + def __gt__(self, other): + return self._compare(other) == 1 if isinstance(other, SemVer) else NotImplemented + + def __eq__(self, other): + return self._compare(other) == 0 if isinstance(other, SemVer) else NotImplemented + + def __lt__(self, other): + return not (self > other or self == other) + + def __ge__(self, other): + return not (self < other) + + def __le__(self, other): + return not (self > other) + + def __ne__(self, other): + return not (self == other) + + # Utility (class-)methods + def satisfies(self, sel): + """Alias for `bool(sel.matches(self))` or `bool(SemSel(sel).matches(self))`. + + See `SemSel.__init__()` and `SemSel.matches(*vers)` for possible exceptions. + + Returns: + * bool: `True` if the version matches the passed selector, `False` otherwise. + """ + if not isinstance(sel, SemSel): + sel = SemSel(sel) # just "re-raise" exceptions + + return bool(sel.matches(self)) + + @classmethod + def valid(cls, ver): + """Check if `ver` is a valid semantic version. Classmethod. + + Parameters: + * ver (str) + The string that should be stripped. + + Raises: + * TypeError + Invalid parameter type. + + Returns: + * bool: `True` if it is valid, `False` otherwise. + """ + if not isinstance(ver, basestring): + raise TypeError("%r is not a string" % ver) + + if cls._match_regex.match(ver): + return True + else: + return False + + @classmethod + def clean(cls, vers): + """Remove everything before and after a valid version string. Classmethod. + + Parameters: + * vers (str) + The string that should be stripped. + + Raises: + * TypeError + Invalid parameter type. + + Returns: + * str: The stripped version string. Only the first version is matched. + * None: No version found in the string. + """ + if not isinstance(vers, basestring): + raise TypeError("%r is not a string" % vers) + m = cls._search_regex.search(vers) + if m: + return vers[m.start():m.end()] + else: + return None + + # Private (class-)methods + @classmethod + def _parse(cls, ver): + """Private. Do not touch. Classmethod. + """ + if not isinstance(ver, basestring): + raise TypeError("%r is not a string" % ver) + + match = cls._match_regex.match(ver) + + if match is None: + raise ValueError("'%s' is not a valid SemVer string" % ver) + + g = list(match.groups()) + for i in range(3): + g[i] = int(g[i]) + + return g # Will be passed as namedtuple(...)(*g) + + def _compare(self, other): + """Private. Do not touch. + self > other: 1 + self = other: 0 + self < other: -1 + """ + # Shorthand lambdas + cp_len = lambda t, i=0: cmp(len(t[i]), len(t[not i])) + + for i, (x1, x2) in enumerate(zip(self, other)): + if i > 2: + if x1 is None and x2 is None: + continue + + # self is greater when other has a prerelease but self doesn't + # self is less when other has a build but self doesn't + if x1 is None or x2 is None: + return int(2 * (i - 3.5)) * (1 - 2 * (x1 is None)) + + # self is less when other's build is empty + if i == 4 and (not x1 or not x2) and x1 != x2: + return 1 - 2 * bool(x1) + + # Split by '.' and use numeric comp or lexicographical order + t2 = [x1.split('.'), x2.split('.')] + for y1, y2 in zip(*t2): + if y1.isdigit() and y2.isdigit(): + y1 = int(y1) + y2 = int(y2) + if y1 > y2: + return 1 + elif y1 < y2: + return -1 + + # The "longer" sub-version is greater + d = cp_len(t2) + if d: + return d + else: + if x1 > x2: + return 1 + elif x1 < x2: + return -1 + + # The versions equal + return 0 + + +class SemComparator(object): + """Holds a SemVer object and a comparing operator and can match these against a given version. + + Constructor: SemComparator('<=', SemVer("1.2.3")) + + Methods: + * matches(ver) + """ + # Private properties + _ops = { + '>=': '__ge__', + '<=': '__le__', + '>': '__gt__', + '<': '__lt__', + '=': '__eq__', + '!=': '__ne__' + } + _ops_satisfy = ('~', '!') + + # Constructor + def __init__(self, op, ver): + """Constructor examples: + SemComparator('<=', SemVer("1.2.3")) + SemComparator('!=', SemVer("2.3.4")) + + Parameters: + * op (str, False, None) + One of [>=, <=, >, <, =, !=, !, ~] or evaluates to `False` which defaults to '~'. + '~' means a "satisfy" operation where pre-releases and builds are ignored. + '!' is a negative "~". + * ver (SemVer) + Holds the version to compare with. + + Raises: + * ValueError + Invalid `op` parameter. + * TypeError + Invalid `ver` parameter. + """ + super(SemComparator, self).__init__() + + if op and op not in self._ops_satisfy and op not in self._ops: + raise ValueError("Invalid value for `op` parameter.") + if not isinstance(ver, SemVer): + raise TypeError("`ver` parameter is not instance of SemVer.") + + # Default to '~' for versions with no build or pre-release + op = op or '~' + # Fallback to '=' and '!=' if len > 3 + if len(ver) != 3: + if op == '~': + op = '=' + if op == '!': + op = '!=' + + self.op = op + self.ver = ver + + # Magic methods + def __str__(self): + return (self.op or "") + str(self.ver) + + # Utility methods + def matches(self, ver): + """Match the internal version (constructor) against `ver`. + + Parameters: + * ver (SemVer) + + Raises: + * TypeError + Could not compare `ver` against the version passed in the constructor with the + passed operator. + + Returns: + * bool + `True` if the version matched the specified operator and internal version, `False` + otherwise. + """ + if self.op in self._ops_satisfy: + # Compare only the first three parts (which are tuples) and directly + return bool((self.ver[:3] == ver[:3]) + (self.op == '!') * -1) + ret = getattr(ver, self._ops[self.op])(self.ver) + if ret == NotImplemented: + raise TypeError("Unable to compare %r with operator '%s'" % (ver, self.op)) + return ret + + +class SemSelAndChunk(list): + """Extends list and defines a few methods used for matching versions. + + New elements should be added by calling `.add_child(op, ver)` which creates a SemComparator + instance and adds that to itself. + + Methods: + * matches(ver) + * add_child(op, ver) + """ + # Magic methods + def __str__(self): + return ' '.join(map(str, self)) + + # Utitlity methods + def matches(self, ver): + """Match all of the added children against `ver`. + + Parameters: + * ver (SemVer) + + Raises: + * TypeError + Invalid `ver` parameter. + + Returns: + * bool: + `True` if *all* of the SemComparator children match `ver`, `False` otherwise. + """ + if not isinstance(ver, SemVer): + raise TypeError("`ver` parameter is not instance of SemVer.") + return all(cp.matches(ver) for cp in self) + + def add_child(self, op, ver): + """Create a SemComparator instance with the given parameters and appends that to self. + + Parameters: + * op (str) + * ver (SemVer) + Both parameters are forwarded to `SemComparator.__init__`, see there for a more detailed + description. + + Raises: + Exceptions raised by `SemComparator.__init__`. + """ + self.append(SemComparator(op, SemVer(ver))) + + +class SemSelOrChunk(list): + """Extends list and defines a few methods used for matching versions. + + New elements should be added by calling `.new_child()` which returns a SemSelAndChunk + instance. + + Methods: + * matches(ver) + * new_child() + """ + # Magic methods + def __str__(self): + return ' || '.join(map(str, self)) + + # Utility methods + def matches(self, ver): + """Match all of the added children against `ver`. + + Parameters: + * ver (SemVer) + + Raises: + * TypeError + Invalid `ver` parameter. + + Returns: + * bool + `True` if *any* of the SemSelAndChunk children matches `ver`. + `False` otherwise. + """ + if not isinstance(ver, SemVer): + raise TypeError("`ver` parameter is not instance of SemVer.") + return any(ch.matches(ver) for ch in self) + + def new_child(self): + """Creates a new SemSelAndChunk instance, appends it to self and returns it. + + Returns: + * SemSelAndChunk: An empty instance. + """ + ch = SemSelAndChunk() + self.append(ch) + return ch + + +class SelParseError(Exception): + """An Exception raised when parsing a semantic selector failed. + """ + pass + + +# Subclass `tuple` because this is a somewhat simple method to make this immutable +class SemSel(tuple): + """A Semantic Version Selector, holds a selector and can match it against semantic versions. + + Information on this particular class and their instances: + - Immutable but not hashable because the content within might have changed. + - Subclasses `tuple` but does not behave like one. + - Always `True` in boolean context. + - len() returns the number of containing *and chunks* (see below). + - Iterable, iterates over containing *and chunks*. + + When talking about "versions" it refers to a semantic version (SemVer). For information on how + versions compare to one another, see SemVer's doc string. + + List for **comparators**: + "1.0.0" matches the version 1.0.0 and all its pre-release and build variants + "!1.0.0" matches any version that is not 1.0.0 or any of its variants + "=1.0.0" matches only the version 1.0.0 + "!=1.0.0" matches any version that is not 1.0.0 + ">=1.0.0" matches versions greater than or equal 1.0.0 + "<1.0.0" matches versions smaller than 1.0.0 + "1.0.0 - 1.0.3" matches versions greater than or equal 1.0.0 thru 1.0.3 + "~1.0" matches versions greater than or equal 1.0.0 thru 1.0.9999 (and more) + "~1", "1.x", "1.*" match versions greater than or equal 1.0.0 thru 1.9999.9999 (and more) + "~1.1.2" matches versions greater than or equal 1.1.2 thru 1.1.9999 (and more) + "~1.1.2+any" matches versions greater than or equal 1.1.2+any thru 1.1.9999 (and more) + "*", "~", "~x" match any version + + Multiple comparators can be combined by using ' ' spaces and every comparator must match to make + the **and chunk** match a version. + Multiple and chunks can be combined to **or chunks** using ' || ' and match if any of the and + chunks split by these matches. + + A complete example would look like: + ~1 || 0.0.3 || <0.0.2 >0.0.1+b.1337 || 2.0.x || 2.1.0 - 2.1.0+b.12 !=2.1.0+b.9 + + Methods: + * matches(*vers) + """ + # Private properties + _fuzzy_regex = re.compile(r'''(?x)^ + (?P[<>]=?|~>?=?)? + (?:(?P\d+) + (?:\.(?P\d+) + (?:\.(?P\d+) + (?P[-+][a-zA-Z0-9-+.]*)? + )? + )? + )?$''') + _xrange_regex = re.compile(r'''(?x)^ + (?P[<>]=?|~>?=?)? + (?:(?P\d+|[xX*]) + (?:\.(?P\d+|[xX*]) + (?:\.(?P\d+|[xX*]))? + )? + ) + (?P.*)$''') + _split_op_regex = re.compile(r'^(?P=|[<>!]=?)?(?P.*)$') + + # "Constructor" + def __new__(cls, sel): + """Constructor examples: + SemSel(">1.0.0") + SemSel("~1.2.9 !=1.2.12") + + Parameters: + * sel (str) + A version selector string. + + Raises: + * TypeError + `sel` parameter is not a string. + * ValueError + A version in the selector could not be matched as a SemVer. + * SemParseError + The version selector's syntax is unparsable; invalid ranges (fuzzy, xrange or + explicit range) or invalid '||' + """ + chunk = cls._parse(sel) + return super(SemSel, cls).__new__(cls, (chunk,)) + + # Magic methods + def __str__(self): + return str(self._chunk) + + def __repr__(self): + return 'SemSel("%s")' % self._chunk + + def __len__(self): + # What would you expect? + return len(self._chunk) + + def __iter__(self): + return iter(self._chunk) + + # Read-only (private) attributes + @property + def _chunk(self): + return self[0] + + # Utility methods + def matches(self, *vers): + """Match the selector against a selection of versions. + + Parameters: + * *vers (str, SemVer) + Versions can be passed as strings and SemVer objects will be created with them. + May also be a mixed list. + + Raises: + * TypeError + A version is not an instance of str (basestring) or SemVer. + * ValueError + A string version could not be parsed as a SemVer. + + Returns: + * list + A list with all the versions that matched, may be empty. Use `max()` to determine + the highest matching version, or `min()` for the lowest. + """ + ret = [] + for v in vers: + if isinstance(v, str): + t = self._chunk.matches(SemVer(v)) + elif isinstance(v, SemVer): + t = self._chunk.matches(v) + else: + raise TypeError("Invalid parameter type '%s': %s" % (v, type(v))) + if t: + ret.append(v) + + return ret + + # Private methods + @classmethod + def _parse(cls, sel): + """Private. Do not touch. + + 1. split by whitespace into tokens + a. start new and_chunk on ' || ' + b. parse " - " ranges + c. replace "xX*" ranges with "~" equivalent + d. parse "~" ranges + e. parse unmatched token as comparator + ~. append to current and_chunk + 2. return SemSelOrChunk + + Raises TypeError, ValueError or SelParseError. + """ + if not isinstance(sel, basestring): + raise TypeError("Selector must be a string") + if not sel: + raise ValueError("String must not be empty") + + # Split selector by spaces and crawl the tokens + tokens = sel.split() + i = -1 + or_chunk = SemSelOrChunk() + and_chunk = or_chunk.new_child() + + while i + 1 < len(tokens): + i += 1 + t = tokens[i] + + # Replace x ranges with ~ selector + m = cls._xrange_regex.match(t) + m = m and m.groups('') + if m and any(not x.isdigit() for x in m[1:4]) and not m[0].startswith('>'): + # (do not match '>1.0' or '>*') + if m[4]: + raise SelParseError("XRanges do not allow pre-release or build components") + + # Only use digit parts and fail if digit found after non-digit + mm, xran = [], False + for x in m[1:4]: + if x.isdigit(): + if xran: + raise SelParseError("Invalid fuzzy range or XRange '%s'" % tokens[i]) + mm.append(x) + else: + xran = True + t = m[0] + '.'.join(mm) # x for x in m[1:4] if x.isdigit()) + # Append "~" if not already present + if not t.startswith('~'): + t = '~' + t + + # switch t: + if t == '||': + if i == 0 or tokens[i - 1] == '||' or i + 1 == len(tokens): + raise SelParseError("OR range must not be empty") + # Start a new and_chunk + and_chunk = or_chunk.new_child() + + elif t == '-': + # ' - ' range + i += 1 + invalid = False + try: + # If these result in exceptions, you know you're doing it wrong + t = tokens[i] + c = and_chunk[-1] + except: + raise SelParseError("Invalid ' - ' range position") + + # If there is an op in front of one of the bound versions + invalid = (c.op not in ('=', '~') + or cls._split_op_regex.match(t).group(1) not in (None, '=')) + if invalid: + raise SelParseError("Invalid ' - ' range '%s - %s'" + % (tokens[i - 2], tokens[i])) + + c.op = ">=" + and_chunk.add_child('<=', t) + + elif t == '': + # Multiple spaces + pass + + elif t.startswith('~'): + m = cls._fuzzy_regex.match(t) + if not m: + raise SelParseError("Invalid fuzzy range or XRange '%s'" % tokens[i]) + + mm, m = m.groups('')[1:4], m.groupdict('') # mm: major to patch + + # Minimum requirement + min_ver = ('.'.join(x or '0' for x in mm) + '-' + if not m['other'] + else cls._split_op_regex(t[1:]).group('ver')) + and_chunk.add_child('>=', min_ver) + + if m['major']: + # Increase version before none (or second to last if '~1.2.3') + e = [0, 0, 0] + for j, d in enumerate(mm): + if not d or j == len(mm) - 1: + e[j - 1] = e[j - 1] + 1 + break + e[j] = int(d) + + and_chunk.add_child('<', '.'.join(str(x) for x in e) + '-') + + # else: just plain '~' or '*', or '~>X' which are already handled + + else: + # A normal comparator + m = cls._split_op_regex.match(t).groupdict() # this regex can't fail + and_chunk.add_child(**m) + + # Finally return the or_chunk + return or_chunk \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/show_error.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/show_error.py new file mode 100644 index 0000000..b8169c9 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/show_error.py @@ -0,0 +1,12 @@ +import sublime + + +def show_error(string): + """ + Displays an error message with a standard "Package Control" header + + :param string: + The error to display + """ + + sublime.error_message(u'Package Control\n\n%s' % string) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/sys_path.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/sys_path.py new file mode 100644 index 0000000..10daa3d --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/sys_path.py @@ -0,0 +1,27 @@ +import sys +import os + +if os.name == 'nt': + from ctypes import windll, create_unicode_buffer + +import sublime + + +def add_to_path(path): + # Python 2.x on Windows can't properly import from non-ASCII paths, so + # this code added the DOC 8.3 version of the lib folder to the path in + # case the user's username includes non-ASCII characters + if os.name == 'nt': + buf = create_unicode_buffer(512) + if windll.kernel32.GetShortPathNameW(path, buf, len(buf)): + path = buf.value + + if path not in sys.path: + sys.path.append(path) + + +lib_folder = os.path.join(sublime.packages_path(), 'Package Control', 'lib') +add_to_path(os.path.join(lib_folder, 'all')) + +if os.name == 'nt': + add_to_path(os.path.join(lib_folder, 'windows')) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/thread_progress.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/thread_progress.py new file mode 100644 index 0000000..b40c564 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/thread_progress.py @@ -0,0 +1,46 @@ +import sublime + + +class ThreadProgress(): + """ + Animates an indicator, [= ], in the status area while a thread runs + + :param thread: + The thread to track for activity + + :param message: + The message to display next to the activity indicator + + :param success_message: + The message to display once the thread is complete + """ + + def __init__(self, thread, message, success_message): + self.thread = thread + self.message = message + self.success_message = success_message + 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]' % \ + (self.message, ' ' * before, ' ' * after)) + + if not after: + self.addend = -1 + if not before: + self.addend = 1 + i += self.addend + + sublime.set_timeout(lambda: self.run(i), 100) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/unicode.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/unicode.py new file mode 100644 index 0000000..f0464a2 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/unicode.py @@ -0,0 +1,49 @@ +import os +import locale +import sys + + +# Sublime Text on OS X does not seem to report the correct encoding +# so we hard-code that to UTF-8 +_encoding = 'utf-8' if sys.platform == 'darwin' else locale.getpreferredencoding() + +_fallback_encodings = ['utf-8', 'cp1252'] + + +def unicode_from_os(e): + """ + This is needed as some exceptions coming from the OS are + already encoded and so just calling unicode(e) will result + in an UnicodeDecodeError as the string isn't in ascii form. + + :param e: + The exception to get the value of + + :return: + The unicode version of the exception message + """ + + if sys.version_info >= (3,): + return str(e) + + try: + if isinstance(e, Exception): + e = e.message + + if isinstance(e, unicode): + return e + + if isinstance(e, int): + e = str(e) + + return unicode(e, _encoding) + + # If the "correct" encoding did not work, try some defaults, and then just + # obliterate characters that we can't seen to decode properly + except UnicodeDecodeError: + for encoding in _fallback_encodings: + try: + return unicode(e, encoding, errors='strict') + except: + pass + return unicode(e, errors='replace') diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/__init__.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/git_upgrader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/git_upgrader.py new file mode 100644 index 0000000..878b1fd --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/git_upgrader.py @@ -0,0 +1,106 @@ +import os + +from ..cache import set_cache, get_cache +from ..show_error import show_error +from .vcs_upgrader import VcsUpgrader + + +class GitUpgrader(VcsUpgrader): + """ + Allows upgrading a local git-repository-based package + """ + + cli_name = 'git' + + def retrieve_binary(self): + """ + Returns the path to the git executable + + :return: The string path to the executable or False on error + """ + + name = 'git' + if os.name == 'nt': + name += '.exe' + binary = self.find_binary(name) + if binary and os.path.isdir(binary): + full_path = os.path.join(binary, name) + if os.path.exists(full_path): + binary = full_path + if not binary: + show_error((u'Unable to find %s. Please set the git_binary setting by accessing the ' + + u'Preferences > Package Settings > Package Control > Settings \u2013 User menu entry. ' + + u'The Settings \u2013 Default entry can be used for reference, but changes to that will be ' + + u'overwritten upon next upgrade.') % name) + return False + + if os.name == 'nt': + tortoise_plink = self.find_binary('TortoisePlink.exe') + if tortoise_plink: + os.environ.setdefault('GIT_SSH', tortoise_plink) + return binary + + def get_working_copy_info(self): + binary = self.retrieve_binary() + if not binary: + return False + + # Get the current branch name + res = self.execute([binary, 'symbolic-ref', '-q', 'HEAD'], self.working_copy) + branch = res.replace('refs/heads/', '') + + # Figure out the remote and the branch name on the remote + remote = self.execute([binary, 'config', '--get', 'branch.%s.remote' % branch], self.working_copy) + res = self.execute([binary, 'config', '--get', 'branch.%s.merge' % branch], self.working_copy) + remote_branch = res.replace('refs/heads/', '') + + return { + 'branch': branch, + 'remote': remote, + 'remote_branch': remote_branch + } + + def run(self): + """ + Updates the repository with remote changes + + :return: False or error, or True on success + """ + + binary = self.retrieve_binary() + if not binary: + return False + + info = self.get_working_copy_info() + + args = [binary] + args.extend(self.update_command) + args.extend([info['remote'], info['remote_branch']]) + self.execute(args, self.working_copy) + return True + + def incoming(self): + """:return: bool if remote revisions are available""" + + cache_key = self.working_copy + '.incoming' + incoming = get_cache(cache_key) + if incoming != None: + return incoming + + binary = self.retrieve_binary() + if not binary: + return False + + info = self.get_working_copy_info() + + res = self.execute([binary, 'fetch', info['remote']], self.working_copy) + if res == False: + return False + + args = [binary, 'log'] + args.append('..%s/%s' % (info['remote'], info['remote_branch'])) + output = self.execute(args, self.working_copy) + incoming = len(output) > 0 + + set_cache(cache_key, incoming, self.cache_length) + return incoming diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/hg_upgrader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/hg_upgrader.py new file mode 100644 index 0000000..36dfb48 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/hg_upgrader.py @@ -0,0 +1,74 @@ +import os + +from ..cache import set_cache, get_cache +from ..show_error import show_error +from .vcs_upgrader import VcsUpgrader + + +class HgUpgrader(VcsUpgrader): + """ + Allows upgrading a local mercurial-repository-based package + """ + + cli_name = 'hg' + + def retrieve_binary(self): + """ + Returns the path to the hg executable + + :return: The string path to the executable or False on error + """ + + name = 'hg' + if os.name == 'nt': + name += '.exe' + binary = self.find_binary(name) + if binary and os.path.isdir(binary): + full_path = os.path.join(binary, name) + if os.path.exists(full_path): + binary = full_path + if not binary: + show_error((u'Unable to find %s. Please set the hg_binary setting by accessing the ' + + u'Preferences > Package Settings > Package Control > Settings \u2013 User menu entry. ' + + u'The Settings \u2013 Default entry can be used for reference, but changes to that will be ' + + u'overwritten upon next upgrade.') % name) + return False + return binary + + def run(self): + """ + Updates the repository with remote changes + + :return: False or error, or True on success + """ + + binary = self.retrieve_binary() + if not binary: + return False + args = [binary] + args.extend(self.update_command) + args.append('default') + self.execute(args, self.working_copy) + return True + + def incoming(self): + """:return: bool if remote revisions are available""" + + cache_key = self.working_copy + '.incoming' + incoming = get_cache(cache_key) + if incoming != None: + return incoming + + binary = self.retrieve_binary() + if not binary: + return False + + args = [binary, 'in', '-q', 'default'] + output = self.execute(args, self.working_copy) + if output == False: + return False + + incoming = len(output) > 0 + + set_cache(cache_key, incoming, self.cache_length) + return incoming diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/vcs_upgrader.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/vcs_upgrader.py new file mode 100644 index 0000000..d82abe7 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/upgraders/vcs_upgrader.py @@ -0,0 +1,27 @@ +from ..cmd import create_cmd, Cli + + +class VcsUpgrader(Cli): + """ + Base class for updating packages that are a version control repository on local disk + + :param vcs_binary: + The full filesystem path to the executable for the version control + system. May be set to None to allow the code to try and find it. + + :param update_command: + The command to pass to the version control executable to update the + repository. + + :param working_copy: + The local path to the working copy/package directory + + :param cache_length: + The lenth of time to cache if incoming changesets are available + """ + + def __init__(self, vcs_binary, update_command, working_copy, cache_length, debug): + self.update_command = update_command + self.working_copy = working_copy + self.cache_length = cache_length + super(VcsUpgrader, self).__init__(vcs_binary, debug) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/versions.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/versions.py new file mode 100644 index 0000000..90a5ef6 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/package_control/versions.py @@ -0,0 +1,81 @@ +import re + +from .semver import SemVer +from .console_write import console_write + + +def semver_compat(v): + if isinstance(v, SemVer): + return str(v) + + # Allowing passing in a dict containing info about a package + if isinstance(v, dict): + if 'version' not in v: + return '0' + v = v['version'] + + # Trim v off of the front + v = re.sub('^v', '', v) + + # We prepend 0 to all date-based version numbers so that developers + # may switch to explicit versioning from GitHub/BitBucket + # versioning based on commit dates. + # + # When translating dates into semver, the way to get each date + # segment into the version is to treat the year and month as + # minor and patch, and then the rest as a numeric build version + # with four different parts. The result looks like: + # 0.2012.11+10.31.23.59 + date_match = re.match('(\d{4})\.(\d{2})\.(\d{2})\.(\d{2})\.(\d{2})\.(\d{2})$', v) + if date_match: + v = '0.%s.%s+%s.%s.%s.%s' % date_match.groups() + + # This handles version that were valid pre-semver with 4+ dotted + # groups, such as 1.6.9.0 + four_plus_match = re.match('(\d+\.\d+\.\d+)[T\.](\d+(\.\d+)*)$', v) + if four_plus_match: + v = '%s+%s' % (four_plus_match.group(1), four_plus_match.group(2)) + + # Semver must have major, minor, patch + elif re.match('^\d+$', v): + v += '.0.0' + elif re.match('^\d+\.\d+$', v): + v += '.0' + return v + + +def version_comparable(string): + return SemVer(semver_compat(string)) + + +def version_exclude_prerelease(versions): + output = [] + for version in versions: + if SemVer(semver_compat(version)).prerelease != None: + continue + output.append(version) + return output + + +def version_filter(versions, allow_prerelease=False): + output = [] + for version in versions: + no_v_version = re.sub('^v', '', version) + if not SemVer.valid(no_v_version): + continue + if not allow_prerelease and SemVer(no_v_version).prerelease != None: + continue + output.append(version) + return output + + +def _version_sort_key(item): + return SemVer(semver_compat(item)) + + +def version_sort(sortable, **kwargs): + try: + return sorted(sortable, key=_version_sort_key, **kwargs) + except (ValueError) as e: + console_write(u"Error sorting versions - %s" % e, True) + return [] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/readme.creole b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/readme.creole index eb37565..50b3d69 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/readme.creole +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Package Control/readme.creole @@ -1,7 +1,7 @@ = Sublime Package Control -A Sublime Text 2 (http://www.sublimetext.com/2) package manager for easily -discovering, install, upgrading and removing packages. Also includes an +A Sublime Text 2/3 (http://www.sublimetext.com) package manager for easily +discovering, installing, upgrading and removing packages. Also includes an automatic updater and package creation tool. Packages can be installed from GitHub, BitBucket or custom package repositories. @@ -14,10 +14,12 @@ instructions, screenshots and documentation. == License -Sublime Package Control (except for the ntlm library) is licensed under the MIT -license. +Sublime Package Control is licensed under the MIT license. - Copyright (c) 2011-2012 Will Bond +All of the source code (except for package_control/semver.py), is under the +license: + + Copyright (c) 2011-2013 Will Bond Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -37,7 +39,24 @@ license. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -The ntlm library (for Windows) is from the python-ntlm project -(http://code.google.com/p/python-ntlm/) and is licensed under the GNU Lesser -General Public License (LGPL). Details can be found in the source files -located in lib/windows/ntlm/. \ No newline at end of file +package_control/semver.py is under the license: + + Copyright (c) 2013 Zachary King, FichteFoll + + 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.UtilPackages/tools/PackageCache/RecentActiveFiles/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/RecentActiveFiles/package-metadata.json index 5d0ccb2..7b01737 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/RecentActiveFiles/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/RecentActiveFiles/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/jugyo/SublimeRecentActiveFiles", "version": "2013.01.17.03.59.02", "description": "Sublime Text 2 plugin that shows and opens recent activated files."} \ No newline at end of file +{"url": "https://github.com/jugyo/SublimeRecentActiveFiles", "version": "2013.01.17.08.59.02", "description": "Sublime Text 2 plugin that shows and opens recent activated files."} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Search Anywhere/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Search Anywhere/package-metadata.json index 0de4b03..9063051 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Search Anywhere/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Search Anywhere/package-metadata.json @@ -1 +1 @@ -{"url": "http://www.ericmartel.com/sublime-text-2-search-anywhere/", "version": "2012.03.20.16.35.46", "description": "Utility to quickly search on multiple search engines from the current selection or custom input. Default search engine configurable per file type. Search Engines defined through JSON including Google, Yahoo, Bing, Stack Overflow, PHP.net, sitepoint and caniuse.com "} \ No newline at end of file +{"url": "http://www.ericmartel.com/sublime-text-2-search-anywhere/", "version": "2012.03.20.20.35.46", "description": "Utility to quickly search on multiple search engines from the current selection or custom input. Default search engine configurable per file type. Search Engines defined through JSON including Google, Yahoo, Bing, Stack Overflow, PHP.net, sitepoint and caniuse.com "} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/Commands.sublime-commands b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/Commands.sublime-commands index dd290f6..9d0c087 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/Commands.sublime-commands +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/Commands.sublime-commands @@ -80,5 +80,9 @@ "caption": "File: Open In Browser - Production Server", "command": "side_bar_open_in_browser", "args":{"paths":[], "type":"production"} + }, + { + "caption": "Side Bar: Refresh", + "command": "refresh_folder_list" } ] \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/SideBar.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/SideBar.py index c0400dd..d3b783b 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/SideBar.py +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/SideBar.py @@ -14,22 +14,6 @@ from send2trash import send2trash if sublime.platform() == 'windows': import _winreg -def disable_default(): - default = sublime.packages_path()+'/Default/Side Bar.sublime-menu' - desired = sublime.packages_path()+'/SideBarEnhancements/disable_default/Side Bar.sublime-menu.txt' - if file(default, 'r').read() == file(desired, 'r').read(): - file(default, 'w+').write('[/*'+file(desired, 'r').read()+'*/]') - - default = sublime.packages_path()+'/Default/Side Bar Mount Point.sublime-menu' - desired = sublime.packages_path()+'/SideBarEnhancements/disable_default/Side Bar Mount Point.sublime-menu.txt' - if file(default, 'r').read() == file(desired, 'r').read(): - file(default, 'w+').write('[/*'+file(desired, 'r').read()+'*/]') - -try: - disable_default(); -except: - pass - def expand_vars(path): for k, v in os.environ.iteritems(): # dirty hack, this should be autofixed in python3 @@ -1240,9 +1224,7 @@ class SideBarProjectItemExcludeCommand(sublime_plugin.WindowCommand): class SideBarOpenInBrowserCommand(sublime_plugin.WindowCommand): def run(self, paths = [], type = False): - browser = s.get("default_browser") - if browser == '': - browser = 'firefox' + browser = s.get("default_browser", "") if type == False or type == 'testing': type = 'url_testing' @@ -1365,6 +1347,8 @@ class SideBarOpenInBrowserCommand(sublime_plugin.WindowCommand): items.extend([ '/usr/bin/chromium' ,'chromium' + ,'/usr/bin/chromium-browser' + ,'chromium-browser' ]) commands = ['-new-tab', url] elif browser == 'firefox': @@ -1430,8 +1414,11 @@ class SideBarOpenInBrowserCommand(sublime_plugin.WindowCommand): ]) commands = ['-new-tab', '-url', url] else: - sublime.error_message('Browser "'+browser+'" not found!\nUse any of the following: firefox, chrome, chromium, opera, safari') - return + if s.get('portable_browser') != '': + items.extend([s.get('portable_browser')]) + commands = ['-new-tab', url] + #sublime.error_message('Browser "'+browser+'" not found!\nUse any of the following: firefox, chrome, chromium, opera, safari') + #return for item in items: try: diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/package-metadata.json index 28bde61..7677438 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/titoBouzout/SideBarEnhancements", "version": "2013.03.24.15.45.28", "description": "Enhancements to Sublime Text sidebar. Files and folders."} \ No newline at end of file +{"url": "https://github.com/titoBouzout/SideBarEnhancements", "version": "2013.08.28.13.28.32", "description": "Enhancements to Sublime Text sidebar. Files and folders."} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/readme.md b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/readme.md index 90e7f3d..85e1e0b 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/readme.md +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/SideBarEnhancements/readme.md @@ -1,193 +1,4 @@ -# Sublime Text 3 Compatibilty +# This branch "master" is deprecated. -Please see: https://github.com/titoBouzout/SideBarEnhancements/tree/st3atyourownrisk +For the faster and better, Sublime Text 3, please see: https://github.com/titoBouzout/SideBarEnhancements/tree/st3 -# Description - - -Provides enhancements to the operations on Side Bar of Files and Folders for Sublime Text 2. See: http://www.sublimetext.com/ - -Notably provides delete as "move to trash", open with.. a and clipboard. Close, move, open and restore buffers affected by a rename/move command. - -Provides the basics: new file/folder, edit, open/run, reveal, find in selected/parent/project, cut, copy, paste, paste in parent, rename, move, delete, refresh.... - -The not so basic: copy paths as URIs, URLs, content as UTF8, content as data:uri base64 ( nice for embedding into CSS! ), copy as tags img/a/script/style, duplicate - - -Preference to control if a buffer should be closed when affected by a deletion operation. - -Allows to display "file modified date" and "file size" on statusbar. - -All commands available for files and folders(when applicable) . - -[img]http://dl.dropbox.com/u/43596449/tito/sublime/SideBar/screenshot.png[/img] - - - -# F12 key - -F12 key allows you to open the current file in browser. -If you want to add a url to that feature: - - * Right click any file on sidebar and select: "Project -> Edit Projects Preview URLs" - * Edit this file, and add your paths and URLs with the following structure: - -``` -{ - "S:/www/domain.tld":{ - "url_testing":"http://testing", - "url_production":"http://domain.tld" - }, - "C:/Users/luna/some/domain2.tld":{ - "url_testing":"http://testing1", - "url_production":"http://productiontld2" - } -} -``` - -```url_testing``` allows you to set the url of your local server, opened via F12 - -```url_production``` allows you to set the url of your production server, opened via ALT+F12 - -# Notes on configuring the `Open With` menu: - -Definitions file: `User/SidebarEnhancements/Open With/Side Bar.sublime-menu` (note the extra subfolder levels). -To open it, right-click on any file in an open project and select `Open With > Edit Applications...` - -- On OSX, the 'application' property simply takes the *name* of an application, to which the file at hand's full path will be passed as if with `open ...`, e.g.: "application": "Google Chrome" -- On OSX, invoking *shell* commands is NOT supported. - -# Todo - - * Use a real clipboard integrated with the OS - -# Installation - -Install this repository via "Package Control" http://wbond.net/sublime_packages/package_control - -# FAQ - -Q: Uninstall? - - * Follow the instructions here: https://github.com/titoBouzout/SideBarEnhancements/issues/18 - -Q: Why the menu is not shown on `Open Files`? - -- It should be mentioned that the package's context menu is only available for files and folders **in a project (section `Folders` in the side bar)**, and _not_ on the open files listed at the top of the side bar, due to a limitation of ST2. - -# Using the External Libraries - - - * "getImageInfo" to get width and height for images from "bfg-pages". See: http://code.google.com/p/bfg-pages/ - * "desktop" to be able to open files with system handlers. See: http://pypi.python.org/pypi/desktop - * "send2trash" to be able to send to the trash instead of deleting for ever!. See: http://pypi.python.org/pypi/Send2Trash - * "hurry.filesize" to be able to format file sizes. See: http://pypi.python.org/pypi/hurry.filesize/ - -# Source-code - - -https://github.com/titoBouzout/SideBarEnhancements - -# Forum Thread - - -http://www.sublimetext.com/forum/viewtopic.php?f=5&t=3331 - -# Contributors: - - - Leif Ringstad - - Sven Axelsson - - Dalibor Simacek - - Stephen Horne - -# Update v1.2: - -* Improved: Feature "find advanced -> in paths containing" or CTRL+ALT+F now provides instant search, contribution by @ryecroft, thanks a lot! -* Fix: When only 1 tab is open and setting "close_windows_when_empty" is true. If the user renames or delete the current file will cause the application to close by itself (it will be perceived as a crash but is not). -* New: Add to the command palette useful commands as duplicate, reveal, move, open project file, open in browser, refresh, rename -* New: added keybindings F12 to open in local server, ALT+F12 to open in production server. -* New: Allows to copy the URL of the selected items. -* Improved: When renaming/moving remember the tab position and syntax. -* small fixes: -- Correct display of commands that are available only for projects -- Be sure to return None if there is no open project -- only display a message when using the clipboard if something was copied. - -# Update v1.1: - -* New: Add boolean preference "confirm_before_deleting" which controls if a the package should ask the user to delete files and folders -* New: When using copy, cut or paste the editor will ask for "replace items" when these items exists. Note: When a folder exists the package will merge the two as in the OS. - -# Update v1.0: - -* New: Add boolean preference "close_affected_buffers_when_deleting_even_if_dirty" which controls if a buffer should be closed when affected by a deletion operation- - -# Update v0.9: - - -* Minor tweaks and fixes. -* Fix: Re-enable move to trash for OSX -* New: Allow to display "file modified time" and "file size" on statusbar via preferences. -* Fix: Disable of built-in function is now automatic. -* On the way: exclude from project, promote as project folder. ( requires restart to apply changes, looks like there is no way to reload project files.) -* Fix: Many appends of same directory to "sys.path" - -# Update v0.8: - - -* Full review for when the user has selection of multiples items. -* New: Added support for bookmarks and marks for when a view is moved. - -# Update v0.7: - - -* New: After a rename of a file or folder, the affected views will update(reload) to reflect the new location keeping intact content, selections, folded regions and scroll position. -* New: File path search - -# Update v0.6: - - -* Fix: Paste was pasting on parent folder (Misinterpretation of boolean) -* Fix: "Open with" works on Linux -* Improved: Allow case change on Windows when renaming a file or folder -* Improved: Update to "find commands" for version 2134 - -# Update v0.5: - - -* Change: Removed "files" prefix from commands. -* New: Ability to copy a path relative to the current view -* New: Ability to "paste in parent" -* New: Ctrl+T will ask for a new file on same folder as current view -* Improved: Context menu open faster - -# Update v0.4: - - -* Fix: "Open / Run" fixed on Linux thanks to project [desktop](http://pypi.python.org/pypi/desktop ) -* Improved: "Paste" command copy permission bits, last access time, last modification time, and flags -* Improved: "Delete" command send files to trash thanks to [Send2Trash](http://pypi.python.org/pypi/Send2Trash ) . NOTE: If "Delete" fails to send to trash it will ask for "Permanently Delete" On confirmation it delete the item forever. - -# Update v0.3: - - -* Fixed: Open should run correctly with some strange characters on paths -* New: "Open with.." is enabled and allows to set custom applications for different file extensions. -* New: "Copy content as Data URI" ( handy for embedding images on CSS files ) -* Improved: Copy img tags now add attributes width and height thanks to project [bfg-pages](http://code.google.com/p/bfg-pages/ ) and suggestion from nobleach. - -# Update v0.2: - - - * Copy paths and names in various formats. - * Removed license to not conflict with sublime - -# Update v0.1: - - - * Tweaks here, tweaks there. - * Renamed repository - * New: "edit" will open the file with sublime text. - * New: "open" will call to the command line with the file path - * New: a disabled "open with" for future use - * Tweaks: ids to all context elements diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/README.md b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/README.md index bff337f..fe3f14e 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/README.md +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/README.md @@ -64,3 +64,9 @@ For example, Gnome Terminal and iTerm2 users respectively will want to change te - "term_command": "gnome-terminal --working-directory=" - "term_command" : "open -a iTerm\ 2 " + +---------- + +__Sublime Text 3__ + +Sublime Text 3 is not officially supported by Sublime Files at the moment. However, there is an experimental branch "py3" that has a python3 version of Sublime Files that should work with Sublime Text 3. diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/package-metadata.json index 2edf95f..9abe24b 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/al63/SublimeFiles", "version": "2013.03.29.16.32.31", "description": "Sublime Text 2 plugin for keyboard driven file navigation"} \ No newline at end of file +{"url": "https://github.com/al63/SublimeFiles", "version": "2013.07.16.01.35.12", "description": "Sublime Text 2 plugin for keyboard driven file navigation"} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/sublime_files.py b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/sublime_files.py index 3136215..5411933 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/sublime_files.py +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Sublime Files/sublime_files.py @@ -11,6 +11,7 @@ class SublimeFilesCommand(sublime_plugin.WindowCommand): try: self.home except: + self.current_dir = "" # first time starting up. ugly, but works settings = sublime.load_settings('SublimeFiles.sublime-settings') if os.name == 'nt': @@ -18,9 +19,13 @@ class SublimeFilesCommand(sublime_plugin.WindowCommand): else: self.home = 'HOME' try: - os.chdir(os.path.dirname(sublime.active_window().active_view().file_name())) + self.current_dir = os.path.dirname(sublime.active_window().active_view().file_name()) + os.chdir(self.current_dir) except: - os.chdir(os.getenv(self.home)) + self.current_dir = os.getenv(self.home) + os.chdir(self.current_dir) + + self.project_root = None self.bookmark = None self.term_command = settings.get('term_command') self.ignore_list = settings.get('ignore_list') @@ -31,6 +36,8 @@ class SublimeFilesCommand(sublime_plugin.WindowCommand): # function for showing panel for changing directories / opening files def open_navigator(self): + self.check_project_root() + self.current_dir = os.getcwdu() self.dir_files = ['[' + os.getcwdu() + ']', bullet + ' Directory actions', '..' + os.sep, '~' + os.sep] @@ -57,14 +64,29 @@ class SublimeFilesCommand(sublime_plugin.WindowCommand): self.dir_files.append(element) self.dir_files = self.dir_files[:4] + sorted(self.dir_files[4:], key=sort_files) + if self.bookmark: self.dir_files.insert(2, bullet + ' To bookmark (' + self.bookmark + ')') if self.window.active_view() and self.window.active_view().file_name(): self.dir_files.insert(2, bullet + ' To current view') + self.window.show_quick_panel(self.dir_files, self.handle_navigator_option, sublime.MONOSPACE_FONT) + # checks if the user has opened up a folder, and if so automatically navigate to the root + def check_project_root(self): + folders = self.window.folders() + if len(folders) > 0: + # If not one yet present or if has changed + if not self.project_root or self.project_root != folders[0]: + self.project_root = folders[0] + os.chdir(self.project_root) + elif self.project_root: + # if folders is empty now and we had a root, let's clear it out + self.project_root = None + # handles user's selection from open_navigator def handle_navigator_option(self, call_value): + os.chdir(self.current_dir) if call_value != -1: option = self.dir_files[call_value] if call_value == 0: diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/README.md b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/README.md index 14f3ae4..709271e 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/README.md +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/README.md @@ -1,6 +1,6 @@ # Soda Theme -Dark and light custom UI themes for Sublime Text 2. +Dark and light custom UI themes for Sublime Text 2 and Sublime Text 3. Project site: [http://buymeasoda.github.com/soda-theme/](http://buymeasoda.github.com/soda-theme/) @@ -12,7 +12,7 @@ Project site: [http://buymeasoda.github.com/soda-theme/](http://buymeasoda.githu ## Installation -Soda theme is designed to work with the latest [development build](http://www.sublimetext.com/dev) of Sublime Text 2. +Soda theme is designed to work with the latest development builds of Sublime Text, including [Sublime Text 2](http://www.sublimetext.com/dev) and [Sublime Text 3](http://www.sublimetext.com/3dev). ### Using Sublime Package Control @@ -20,9 +20,9 @@ If you are using Will Bond's excellent [Sublime Package Control](http://wbond.ne ### Using Git -Alternatively, if you are a git user, you can install the theme and keep up to date by cloning the repo directly into your `Packages` directory in the Sublime Text 2 application settings area. +Alternatively, if you are a git user, you can install the theme and keep up to date by cloning the repo directly into your `Packages` directory in the Sublime Text application settings area. -You can locate your Sublime Text 2 `Packages` directory by using the menu item `Preferences -> Browse Packages...`. +You can locate your Sublime Text `Packages` directory by using the menu item `Preferences -> Browse Packages...`. While inside the `Packages` directory, clone the theme repository using the command below: @@ -32,21 +32,35 @@ While inside the `Packages` directory, clone the theme repository using the comm * Download the files using the GitHub .zip download option * Unzip the files and rename the folder to `Theme - Soda` -* Copy the folder to your Sublime Text 2 `Packages` directory +* Find your `Packages` directory using the menu item `Preferences -> Browse Packages...` +* Copy the folder into your Sublime Text `Packages` directory ## Activating the theme -To configure Sublime Text 2 to use the theme: +To configure Sublime Text to use the theme, follow the instructions below for your specific version. + +### Sublime Text 2 * Open your User Settings Preferences file `Sublime Text 2 -> Preferences -> Settings - User` * Add (or update) your theme entry to be `"theme": "Soda Light.sublime-theme"` or `"theme": "Soda Dark.sublime-theme"` -### Example User Settings +**Example Sublime Text 2 User Settings** { "theme": "Soda Light.sublime-theme" } +### Sublime Text 3 + +* Open your User Settings Preferences file `Sublime Text -> Preferences -> Settings - User` +* Add (or update) your theme entry to be `"theme": "Soda Light 3.sublime-theme"` or `"theme": "Soda Dark 3.sublime-theme"` + +**Example Sublime Text 3 User Settings** + + { + "theme": "Soda Light 3.sublime-theme" + } + ## Additional Features ### Alternate Tab Styles @@ -59,6 +73,16 @@ By default, a square tab style is used. If you'd prefer to use the original curv ![Soda Tab Styles](http://buymeasoda.github.com/soda-theme/images/features/multiple-tab-styles.png) +### Sidebar Folder Icons + +Soda Theme has the ability to use folder icons in the sidebar. + +If you'd like to use folder icons in the sidebar instead of the regular arrows, add the following custom setting to your `Settings - User` file: + + "soda_folder_icons": true + +![Soda Folder Icons](http://buymeasoda.github.com/soda-theme/images/features/sidebar-folder-icons.png) + ### Retina Resolution UI Soda Theme has been designed to take advantage of retina resolution (high-dpi) displays. Both Soda Light and Soda Dark support retina displays. @@ -67,7 +91,7 @@ Soda Theme has been designed to take advantage of retina resolution (high-dpi) d ### Theme Customisation -Sublime Text 2 provides an elegant way to tweak existing themes without having to duplicate or maintain a separate copy of the original theme. If there are aspects of Soda Theme that you would like to adjust, take a look at the [theme customisation](https://github.com/buymeasoda/soda-theme/wiki/Theme-customisation) wiki page. +Sublime Text provides an elegant way to tweak existing themes without having to duplicate or maintain a separate copy of the original theme. If there are aspects of Soda Theme that you would like to adjust, take a look at the [theme customisation](https://github.com/buymeasoda/soda-theme/wiki/Theme-customisation) wiki page. ## Bonus Options @@ -78,7 +102,7 @@ The Soda Light screenshot uses a modified version of Espresso Tutti Colori and t If you'd like to use the syntax highlighting schemes shown in the screenshots: * Download [colour-schemes.zip](http://buymeasoda.github.com/soda-theme/extras/colour-schemes.zip) -* Unzip and place the extracted `tmtheme` files in the Sublime Text 2 `Packages/User` folder +* Unzip and place the extracted `tmtheme` files in the Sublime Text `Packages/User` folder * Enable the colour scheme via `Preferences -> Color Scheme -> User` ### Code Font diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark 3.sublime-theme b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark 3.sublime-theme new file mode 100644 index 0000000..e090ed4 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark 3.sublime-theme @@ -0,0 +1,1154 @@ +[ + +// +// TABS (REGULAR) +// + + // Tab set + { + "class": "tabset_control", + "layer0.texture": "Theme - Soda/Soda Dark/tabset-background.png", + "layer0.inner_margin": [1, 7], + "layer0.opacity": 1.0, + "content_margin": [-4, 0, -4, 3], + "tab_overlap": 5, + "tab_width": 180, + "tab_min_width": 45, + "tab_height": 25, + "mouse_wheel_switch": false + }, + { + "class": "tabset_control", + "settings": ["enable_tab_scrolling"], + "content_margin": [3, 0, 3, 3] + }, + { + "class": "tabset_control", + "settings": ["mouse_wheel_switches_tabs"], + "mouse_wheel_switch": true + }, + // Tab element + { + "class": "tab_control", + "content_margin": [12, 3, 12, 3], + "max_margin_trim": 0, + "hit_test_level": 0.0, + "layer0.texture": "Theme - Soda/Soda Dark/tab-inactive.png", + "layer0.inner_margin": [5, 5], + "layer0.opacity": 1.0 + }, + // Tab close state + { + "class": "tab_control", + "settings": ["show_tab_close_buttons"], + "content_margin": [12, 3, 7, 3] + }, + // Tab hover state + { + "class": "tab_control", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/tab-hover.png" + }, + // Tab active state + { + "class": "tab_control", + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Dark/tab-active.png" + }, + // Tab dirty state (close button hidden) + { + "class": "tab_control", + "settings": ["!show_tab_close_buttons"], + "attributes": ["dirty"], + "content_margin": [12, 3, 7, 3] + }, + +// +// TABS (CLASSIC) +// + + // Tab set + { + "class": "tabset_control", + "settings": ["soda_classic_tabs"], + "content_margin": [3, 4, 3, 3], + "tab_overlap": 24, + "tab_height": 28 + }, + { + "class": "tabset_control", + "settings": ["soda_classic_tabs", "enable_tab_scrolling"], + "content_margin": [6, 4, 6, 3] + }, + // Tab element + { + "class": "tab_control", + "settings": ["soda_classic_tabs"], + "content_margin": [22, 6, 22, 4], + "hit_test_level": 0.5, + "layer0.texture": "Theme - Soda/Soda Dark/classic/tab-inactive.png", + "layer0.inner_margin": [18, 4] + }, + // Tab close state + { + "class": "tab_control", + "settings": ["soda_classic_tabs", "show_tab_close_buttons"], + "content_margin": [22, 6, 15, 4] + }, + // Tab hover state + { + "class": "tab_control", + "settings": ["soda_classic_tabs"], + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/classic/tab-hover.png" + }, + // Tab active state + { + "class": "tab_control", + "settings": ["soda_classic_tabs"], + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Dark/classic/tab-active.png" + }, + // Tab dirty state (close button hidden) + { + "class": "tab_control", + "settings": ["soda_classic_tabs", "!show_tab_close_buttons"], + "attributes": ["dirty"], + "content_margin": [22, 6, 15, 4] + }, + +// +// TAB BUTTONS +// + + // Tab close button + { + "class": "tab_close_button", + "content_margin": [0, 0], + "layer0.texture": "Theme - Soda/Soda Dark/tab-close-inactive.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "tab_close_button", + "settings": ["show_tab_close_buttons"], + "content_margin": [8, 8] + }, + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Dark/tab-close.png", + "layer0.opacity": 1.0 + }, + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/tab-close.png", + "layer0.opacity": 1.0 + }, + // Tab dirty button + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["dirty"]}], + "layer0.texture": "Theme - Soda/Soda Dark/tab-dirty-inactive.png" + }, + { + "class": "tab_close_button", + "settings": ["!show_tab_close_buttons"], + "parents": [{"class": "tab_control", "attributes": ["dirty"]}], + "content_margin": [8, 8] + }, + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["dirty", "hover"]}], + "layer0.opacity": 1.0 + }, + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["dirty", "selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/tab-dirty.png" + }, + // Tab highlight button + { + "class": "tab_close_button", + "settings": ["highlight_modified_tabs"], + "parents": [{"class": "tab_control", "attributes": ["dirty"]}], + "layer0.texture": "Theme - Soda/Soda Dark/tab-highlight-inactive.png" + }, + { + "class": "tab_close_button", + "settings": ["highlight_modified_tabs"], + "parents": [{"class": "tab_control", "attributes": ["dirty", "selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/tab-highlight.png" + }, + // Tab close button hover + { + "class": "tab_close_button", + "settings": ["show_tab_close_buttons"], + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/tab-close-hover.png" + }, + // Tab close button pressed + { + "class": "tab_close_button", + "settings": ["show_tab_close_buttons"], + "attributes": ["pressed"], + "layer0.texture": "Theme - Soda/Soda Dark/tab-close-pressed.png" + }, + +// +// TAB LABELS +// + + { + "class": "tab_label", + "fade": true, + "fg": [170, 170, 170], + "shadow_color": [25, 25, 25], + "shadow_offset": [0, -1], + "font.italic": false + }, + { + "class": "tab_label", + "parents": [{"class": "tab_control", "attributes": ["hover"]}], + "fg": [200, 200, 200], + "shadow_color": [30, 30, 30] + }, + { + "class": "tab_label", + "parents": [{"class": "tab_control", "attributes": ["selected"]}], + "fg": [230, 230, 230], + "shadow_color": [35, 35, 35] + }, + { + "class": "tab_label", + "attributes": ["transient"], + "font.italic": true + }, + +// +// TAB SCROLLING +// + + // Tab dropdown + { + "class": "show_tabs_dropdown_button", + "content_margin": [9, 7, 8, 6], + "layer0.texture": "Theme - Soda/Soda Dark/tabset-list.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "show_tabs_dropdown_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/tabset-list-hover.png" + }, + // Tab scroll left + { + "class": "scroll_tabs_left_button", + "content_margin": [9, 7, 8, 6], + "layer0.texture": "Theme - Soda/Soda Dark/tabset-left.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "scroll_tabs_left_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/tabset-left-hover.png" + }, + // Tab scroll right + { + "class": "scroll_tabs_right_button", + "content_margin": [9, 7, 8, 6], + "layer0.texture": "Theme - Soda/Soda Dark/tabset-right.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "scroll_tabs_right_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/tabset-right-hover.png" + }, + +// +// FOLD BUTTONS +// + + { + "class": "fold_button_control", + "layer0.texture": "Theme - Soda/Soda Dark/fold-closed.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0, + "content_margin": [8, 8] + }, + { + "class": "fold_button_control", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/fold-closed-hover.png" + }, + { + "class": "fold_button_control", + "attributes": ["expanded"], + "layer0.texture": "Theme - Soda/Soda Dark/fold-open.png" + }, + { + "class": "fold_button_control", + "attributes": ["expanded", "hover"], + "layer0.texture": "Theme - Soda/Soda Dark/fold-open-hover.png" + }, + +// +// STANDARD SCROLLBARS +// + + // Standard vertical scroll bar + { + "class": "scroll_bar_control", + "layer0.texture": "Theme - Soda/Soda Dark/standard-scrollbar-vertical.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [2, 6], + "blur": false + }, + // Standard horizontal scroll bar + { + "class": "scroll_bar_control", + "attributes": ["horizontal"], + "layer0.texture": "Theme - Soda/Soda Dark/standard-scrollbar-horizontal.png", + "layer0.inner_margin": [6, 2], + "blur": false + }, + // Standard scroll bar corner + { + "class": "scroll_corner_control", + "layer0.texture": "Theme - Soda/Soda Dark/standard-scrollbar-corner.png", + "layer0.inner_margin": [2, 2], + "layer0.opacity": 1.0 + }, + // Standard vertical scroll puck + { + "class": "puck_control", + "layer0.texture": "Theme - Soda/Soda Dark/standard-puck-vertical.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [0, 10], + "content_margin": [8, 12], + "blur": false + }, + // Standard horizontal scroll puck + { + "class": "puck_control", + "attributes": ["horizontal"], + "layer0.texture": "Theme - Soda/Soda Dark/standard-puck-horizontal.png", + "layer0.inner_margin": [10, 0], + "content_margin": [12, 8], + "blur": false + }, + +// +// OVERLAY SCROLLBARS +// + + // Overlay toggle scroll bar + { + "class": "scroll_area_control", + "settings": ["overlay_scroll_bars"], + "overlay": true + }, + { + "class": "scroll_area_control", + "settings": ["!overlay_scroll_bars"], + "overlay": false + }, + // Overlay vertical scroll bar + { + "class": "scroll_bar_control", + "settings": ["overlay_scroll_bars"], + "layer0.texture": "Theme - Soda/Soda Dark/overlay-scrollbar-vertical.png", + "layer0.inner_margin": [0, 5], + "blur": true + }, + // Overlay horizontal scroll bar + { + "class": "scroll_bar_control", + "settings": ["overlay_scroll_bars"], + "attributes": ["horizontal"], + "layer0.texture": "Theme - Soda/Soda Dark/overlay-scrollbar-horizontal.png", + "layer0.inner_margin": [5, 0], + "blur": true + }, + // Overlay vertical puck + { + "class": "puck_control", + "settings": ["overlay_scroll_bars"], + "layer0.texture": "Theme - Soda/Soda Dark/overlay-puck-vertical.png", + "layer0.inner_margin": [0, 5], + "content_margin": [5, 20], + "blur": true + }, + // Overlay horizontal puck + { + "class": "puck_control", + "settings": ["overlay_scroll_bars"], + "attributes": ["horizontal"], + "layer0.texture": "Theme - Soda/Soda Dark/overlay-puck-horizontal.png", + "layer0.inner_margin": [5, 0], + "content_margin": [20, 5], + "blur": true + }, + // Overlay light puck (for dark content) + { + "class": "puck_control", + "settings": ["overlay_scroll_bars"], + "attributes": ["dark"], + "layer0.texture": "Theme - Soda/Soda Dark/overlay-dark-puck-vertical.png" + }, + // Overlay light horizontal puck (for dark content) + { + "class": "puck_control", + "settings": ["overlay_scroll_bars"], + "attributes": ["horizontal", "dark"], + "layer0.texture": "Theme - Soda/Soda Dark/overlay-dark-puck-horizontal.png" + }, + +// +// EMPTY WINDOW BACKGROUND +// + + { + "class": "sheet_container_control", + "layer0.tint": [25, 25, 25], + "layer0.opacity": 1.0 + }, + +// +// GRID LAYOUT +// + + { + "class": "grid_layout_control", + "border_size": 1, + "border_color": [70, 70, 70] + }, + +// +// MINI MAP +// + + { + "class": "minimap_control", + "settings": ["always_show_minimap_viewport"], + "viewport_color": [255, 255, 255, 35], + "viewport_opacity": 1.0 + }, + { + "class": "minimap_control", + "settings": ["!always_show_minimap_viewport"], + "viewport_color": [255, 255, 255, 35], + "viewport_opacity": { "target": 0.0, "speed": 10.0, "interpolation": "smoothstep" } + }, + { + "class": "minimap_control", + "attributes": ["hover"], + "settings": ["!always_show_minimap_viewport"], + "viewport_opacity": 1.0 + }, + +// +// DIALOG +// + + { + "class": "dialog", + "layer0.tint": [67, 67, 67], + "layer0.opacity": 1.0 + }, + +// +// PROGRESS BAR +// + + { + "class": "progress_bar_control", + "layer0.tint": [30, 30, 30], + "layer0.opacity": 1.0 + }, + { + "class": "progress_gauge_control", + "layer0.tint": [144, 144, 144], + "layer0.opacity": 1.0, + "content_margin": [0, 6] + }, + +// +// LABELS +// + + // General labels + { + "class": "label_control", + "color": [204, 204, 204] + }, + // Text field labels + { + "class": "label_control", + "parents": [{"class": "panel_control"}], + "shadow_color": [50, 50, 50], + "shadow_offset": [0, -1] + }, + // Button labels + { + "class": "label_control", + "parents": [{"class": "button_control"}], + "shadow_color": [55, 55, 55], + "shadow_offset": [0, -1] + }, + +// +// TOOLTIP +// + + // Tooltip container + { + "class": "tool_tip_control", + "layer0.texture": "Theme - Soda/Soda Dark/tooltip.png", + "layer0.inner_margin": [1, 1], + "layer0.opacity": 0.95, + "content_margin": [3, 3] + }, + // Tooltip content + { + "class": "tool_tip_label_control", + "color": [0, 0, 0] + }, + +// +// STATUS BAR +// + + // Status bar container + { + "class": "status_bar", + "layer0.texture": "Theme - Soda/Soda Dark/status-bar-background.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [2, 2], + "content_margin": [8, 4, 8, 4] + }, + // Status bar button + { + "class": "status_button", + "min_size": [100, 0] + }, + // Status bar label + { + "class": "label_control", + "parents": [{"class": "status_bar"}], + "color": [150, 150, 150], + "shadow_color": [25, 25, 25], + "shadow_offset": [0, -1] + }, + +// +// SIDEBAR +// + + // Sidebar container + { + "class": "sidebar_container", + "layer0.texture": "Theme - Soda/Soda Dark/sidebar-bg.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [1, 1, 2, 1], + "content_margin": [0, 0, 1, 0] + }, + // Sidebar tree + { + "class": "sidebar_tree", + "row_padding": [8, 3], + "indent": 15, + "indent_offset": 15, + "indent_top_level": false, + "dark_content": true + }, + // Sidebar rows + { + "class": "tree_row", + "layer0.texture": "Theme - Soda/Soda Dark/sidebar-row-selected.png", + "layer0.opacity": 0.0, + "layer0.inner_margin": [1, 1] + }, + // Sidebar row selected + { + "class": "tree_row", + "attributes": ["selected"], + "layer0.opacity": 1.0 + }, + // Sidebar heading + { + "class": "sidebar_heading", + "color": [210, 210, 210], + "font.bold": true, + "shadow_color": [0, 0, 0], + "shadow_offset": [0, -1] + }, + // Sidebar heading selected + { + "class": "sidebar_heading", + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "shadow_offset": [0, 1] + }, + // Sidebar entry + { + "class": "sidebar_label", + "color": [125, 125, 125], + "shadow_color": [0, 0, 0], + "shadow_offset": [0, -1], + "font.bold": false, + "font.italic": false + }, + // Sidebar folder entry + { + "class": "sidebar_label", + "parents": [{"class": "tree_row", "attributes": ["expandable"]}], + "color": [190, 190, 190] + }, + { + "class": "sidebar_label", + "parents": [{"class": "tree_row", "attributes": ["expandable", "hover"]}], + "color": [235, 235, 235] + }, + { + "class": "sidebar_label", + "parents": [{"class": "tree_row", "attributes": ["expandable"]}], + "settings": ["bold_folder_labels"], + "font.bold": true + }, + // Sidebar entry selected + { + "class": "sidebar_label", + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "color": [210, 210, 210], + "shadow_color": [0, 0, 0], + "shadow_offset": [0, 1] + }, + // Sidebar entry transient + { + "class": "sidebar_label", + "attributes": ["transient"], + "font.italic": true + }, + +// +// SIDEBAR - OPEN FILE ICONS +// + + // Sidebar file close + { + "class": "close_button", + "layer0.texture": "Theme - Soda/Soda Dark/file-close.png", + "layer0.opacity": 0.0, + "layer0.inner_margin": 0, + "content_margin": [8, 8] + }, + { + "class": "close_button", + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.opacity": 1.0 + }, + // Sidebar file dirty + { + "class": "close_button", + "attributes": ["dirty"], + "layer0.texture": "Theme - Soda/Soda Dark/file-dirty.png", + "layer0.opacity": 1.0 + }, + { + "class": "close_button", + "attributes": ["dirty"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/file-dirty-selected.png" + }, + { + "class": "close_button", + "attributes": ["dirty"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Dark/file-close.png" + }, + // Sidebar file close hover + { + "class": "close_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/file-close-hover.png" + }, + { + "class": "close_button", + "parents": [{"class": "tree_row", "attributes": ["hover", "selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/file-close-selected.png" + }, + +// +// SIDEBAR - GENERAL FILE ICONS +// + + // Sidebar group closed + { + "class": "disclosure_button_control", + "content_margin": [8, 8], + "layer0.texture": "Theme - Soda/Soda Dark/group-closed.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "disclosure_button_control", + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Dark/group-closed-hover.png" + }, + { + "class": "disclosure_button_control", + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/group-closed-selected.png" + }, + // Sidebar folder closed + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "layer0.texture": "Theme - Soda/Soda Dark/folder-closed.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Dark/folder-closed-hover.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/folder-closed-selected.png" + }, + // Sidebar group open + { + "class": "disclosure_button_control", + "attributes": ["expanded"], + "layer0.texture": "Theme - Soda/Soda Dark/group-open.png" + }, + { + "class": "disclosure_button_control", + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Dark/group-open-hover.png" + }, + { + "class": "disclosure_button_control", + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/group-open-selected.png" + }, + // Sidebar folder open + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "layer0.texture": "Theme - Soda/Soda Dark/folder-open.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Dark/folder-open-hover.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/folder-open-selected.png" + }, + +// +// STANDARD TEXT BUTTONS +// + + // Default button state + { + "class": "button_control", + "content_margin": [6, 5, 6, 6], + "min_size": [75, 0], + "layer0.texture": "Theme - Soda/Soda Dark/btn-large.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [6, 6] + }, + // Pressed button state + { + "class": "button_control", + "attributes": ["pressed"], + "layer0.texture": "Theme - Soda/Soda Dark/btn-large-on.png" + }, + +// +// TEXT INPUT FIELD +// + + // Text input field item + { + "class": "text_line_control", + "layer0.texture": "Theme - Soda/Soda Dark/text-field.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [4, 5, 4, 3], + "content_margin": [3, 3, 6, 3] + }, + + // Text input dropdown + { + "class": "dropdown_button_control", + "content_margin": [8, 8], + "layer0.texture": "Theme - Soda/Soda Dark/text-field-list.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [4, 4] + }, + { + "class": "dropdown_button_control", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/text-field-list-hover.png" + }, + +// +// PANEL BACKGROUNDS +// + + // Bottom panel background + { + "class": "panel_control", + "layer0.texture": "Theme - Soda/Soda Dark/panel-background.png", + "layer0.inner_margin": [2, 2, 2, 2], + "layer0.opacity": 1.0, + "content_margin": [2, 3, 2, 1] + }, + // Quick panel background + { + "class": "overlay_control", + "settings": ["!soda_retina_fix"], + "layer0.texture": "Theme - Soda/Soda Dark/quick-panel-background.png", + "layer0.inner_margin": [12, 6, 12, 15], + "layer0.opacity": 1.0, + "layer1.texture": "Theme - Soda/Soda Dark/quick-panel-sections.png", + "layer1.inner_margin": [12, 40, 12, 19], + "layer1.opacity": 1.0, + "content_margin": [11, 8, 11, 17] + }, + // Quick panel background (Retina fix) + { + "class": "overlay_control", + "settings": ["soda_retina_fix"], + "layer0.tint": [67, 67, 67], + "layer0.opacity": 1.0, + "content_margin": [6, 8, 6, 6] + }, + +// +// QUICK PANEL +// + + { + "class": "quick_panel", + "row_padding": [5, 2], + "layer0.tint": [33, 33, 33], + "layer0.opacity": 1.0, + "dark_content": true + }, + { + "class": "quick_panel_row", + "layer0.texture": "Theme - Soda/Soda Dark/quick-panel-row.png", + "layer0.inner_margin": [2, 2, 2, 2], + "layer0.opacity": 1.0 + }, + { + "class": "quick_panel_row", + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Dark/quick-panel-row-selected.png" + }, + { + "class": "quick_panel_label", + "fg": [210, 210, 210, 255], + "match_fg": [126, 199, 239, 255], + "selected_fg": [255, 255, 255, 255], + "selected_match_fg": [166, 229, 255, 255] + }, + { + "class": "quick_panel_path_label", + "fg": [130, 130, 130, 255], + "match_fg": [220, 220, 220, 255], + "selected_fg": [175, 175, 175, 255], + "selected_match_fg": [220, 220, 220, 255] + }, + { + "class": "quick_panel_score_label", + "fg": [126, 199, 239, 255], + "selected_fg": [166, 229, 255, 255] + }, + +// +// MINI QUICK PANEL +// + + { + "class": "mini_quick_panel_row", + "layer0.texture": "Theme - Soda/Soda Dark/quick-panel-row.png", + "layer0.inner_margin": [2, 2, 2, 2], + "layer0.opacity": 1.0 + }, + { + "class": "mini_quick_panel_row", + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Dark/quick-panel-row-selected.png" + }, + +// +// CODE COMPLETION DROPDOWN +// + + { + "class": "popup_control", + "content_margin": [2, 2], + "layer0.tint": [30, 30, 30], + "layer0.opacity": 1.0 + }, + { + "class": "auto_complete", + "row_padding": [4, 2] + }, + { + "class": "auto_complete_label", + "fg": [140, 140, 140], + "match_fg": [220, 220, 220], + "selected_fg": [180, 180, 180], + "selected_match_fg": [245, 245, 245] + }, + { + "class": "table_row", + "layer0.texture": "Theme - Soda/Soda Dark/autocomplete-row-selected.png", + "layer0.opacity": 0.0, + "layer0.inner_margin": [3, 1] + }, + { + "class": "table_row", + "attributes": ["selected"], + "layer0.opacity": 1.0 + }, + +// +// BOTTOM PANEL BUTTONS +// + + // Button group middle + { + "class": "icon_button_control", + "layer0.texture": "Theme - Soda/Soda Dark/btn-group-middle.png", + "layer0.inner_margin": [6, 6], + "layer0.opacity": 1.0, + "content_margin": [3, 3] + }, + { + "class": "icon_button_control", + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Dark/btn-group-middle-on.png" + }, + // Button group left + { + "class": "icon_button_control", + "attributes": ["left"], + "layer0.texture": "Theme - Soda/Soda Dark/btn-group-left.png", + "content_margin": [4, 3, 3, 3] + }, + { + "class": "icon_button_control", + "attributes": ["left", "selected"], + "layer0.texture": "Theme - Soda/Soda Dark/btn-group-left-on.png" + }, + // Button group right + { + "class": "icon_button_control", + "attributes": ["right"], + "layer0.texture": "Theme - Soda/Soda Dark/btn-group-right.png", + "content_margin": [3, 3, 4, 3] + }, + { + "class": "icon_button_control", + "attributes": ["right", "selected"], + "layer0.texture": "Theme - Soda/Soda Dark/btn-group-right-on.png" + }, + // Button single + { + "class": "icon_button_control", + "attributes": ["left", "right"], + "layer0.texture": "Theme - Soda/Soda Dark/btn-small.png", + "content_margin": [4, 3] + }, + { + "class": "icon_button_control", + "attributes": ["left", "right", "selected"], + "layer0.texture": "Theme - Soda/Soda Dark/btn-small-on.png" + }, + // Panel close button + { + "class": "panel_close_button", + "layer0.texture": "Theme - Soda/Soda Dark/panel-close.png", + "layer0.opacity": 0.85, + "content_margin": [8, 12] + }, + { + "class": "panel_close_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Dark/panel-close.png", + "layer0.opacity": 1.0 + }, + { + "class": "panel_close_button", + "attributes": ["pressed"], + "layer0.texture": "Theme - Soda/Soda Dark/panel-close-pressed.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 1 +// + + // Regex search button + { + "class": "icon_regex", + "layer0.texture": "Theme - Soda/Soda Dark/icon-regex-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_regex", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-regex-on.png" + }, + // Case sensitive search button + { + "class": "icon_case", + "layer0.texture": "Theme - Soda/Soda Dark/icon-case-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_case", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-case-on.png" + }, + // Match whole word search button + { + "class": "icon_whole_word", + "layer0.texture": "Theme - Soda/Soda Dark/icon-word-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_whole_word", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-word-on.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 1 (EXTENDED: FIND IN FILES) +// + + // Show search context button + { + "class": "icon_context", + "layer0.texture": "Theme - Soda/Soda Dark/icon-context-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_context", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-context-on.png" + }, + // Use search buffer + { + "class": "icon_use_buffer", + "layer0.texture": "Theme - Soda/Soda Dark/icon-buffer-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_use_buffer", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-buffer-on.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 2 +// + + // Reverse search direction button + { + "class": "icon_reverse", + "layer0.texture": "Theme - Soda/Soda Dark/icon-reverse-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_reverse", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-reverse-on.png" + }, + // Search wrap button + { + "class": "icon_wrap", + "layer0.texture": "Theme - Soda/Soda Dark/icon-wrap-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_wrap", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-wrap-on.png" + }, + // Search in selection button + { + "class": "icon_in_selection", + "layer0.texture": "Theme - Soda/Soda Dark/icon-selection-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_in_selection", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-selection-on.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 3 +// + + // Preserve case button + { + "class": "icon_preserve_case", + "layer0.texture": "Theme - Soda/Soda Dark/icon-preserve-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_preserve_case", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-preserve-on.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 4 +// + + // Highlight results button + { + "class": "icon_highlight", + "layer0.texture": "Theme - Soda/Soda Dark/icon-highlight-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_highlight", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/icon-highlight-on.png" + } + +] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark.sublime-theme b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark.sublime-theme index 6c4a634..5b47e34 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark.sublime-theme +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark.sublime-theme @@ -488,6 +488,12 @@ "shadow_color": [0, 0, 0], "shadow_offset": [0, -1] }, + // Sidebar heading selected + { + "class": "sidebar_heading", + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "shadow_offset": [0, 1] + }, // Sidebar entry { "class": "sidebar_label", @@ -591,6 +597,24 @@ "parents": [{"class": "tree_row", "attributes": ["selected"]}], "layer0.texture": "Theme - Soda/Soda Dark/group-closed-selected.png" }, + // Sidebar folder closed + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "layer0.texture": "Theme - Soda/Soda Dark/folder-closed.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Dark/folder-closed-hover.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/folder-closed-selected.png" + }, // Sidebar group open { "class": "disclosure_button_control", @@ -609,6 +633,27 @@ "parents": [{"class": "tree_row", "attributes": ["selected"]}], "layer0.texture": "Theme - Soda/Soda Dark/group-open-selected.png" }, + // Sidebar folder open + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "layer0.texture": "Theme - Soda/Soda Dark/folder-open.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Dark/folder-open-hover.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Dark/folder-open-selected.png" + }, // // STANDARD TEXT BUTTONS diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/Widget - Soda Dark 3.sublime-settings b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/Widget - Soda Dark 3.sublime-settings new file mode 100644 index 0000000..d5787af --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/Widget - Soda Dark 3.sublime-settings @@ -0,0 +1,4 @@ +{ + "color_scheme": "Packages/Theme - Soda/Soda Dark/Widget - Soda Dark.stTheme", + "draw_shadows": false +} diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/Widget - Soda Dark.stTheme b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/Widget - Soda Dark.stTheme index 42cbb94..e106e4e 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/Widget - Soda Dark.stTheme +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/Widget - Soda Dark.stTheme @@ -21,8 +21,65 @@ #F8F8F8 invisibles #3B3B3B + lineHighlight + #333333 selection - #222222 + #666666 + selectionBorder + #66666600 + inactiveSelection + #4a4a4a + + + + name + Comment + scope + comment + settings + + fontStyle + italic + foreground + #505C63 + + + + name + Keyword + scope + keyword, storage + settings + + foreground + #A7DBD8 + + + + scope + constant + settings + + foreground + #E0E4CC + + + + scope + string + settings + + foreground + #E0E4CC + + + + scope + constant.character.escape + settings + + foreground + #e9903c diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left-on.png index 3a25d0c556c75392b9b7f61b672cdf89ad4edfe7..dd340bb9b608f3e29374b1ac8a060251ec4e1d0e 100644 GIT binary patch delta 283 zcmV+$0p$Lw0=5E>B!B%$L_t(Y4egdq4uUWch5JiEHzpi`#2c8fHr#~+cn)`ZD(}E4 zNL(P)c{Md*r^$>fCz(JSm~Y;6Xwfl7rL{InDMxO}H94yxpsDM6A40g$eM`P&B~UPA z@5-{gdhd@#Q9uNF3!#*<%3bHk5w_ z!34LhAQ*^4pg75D_+?JKhbYR_ZOv17%&MQ8~N7FQ?VHnQiI38$R0mf+>zeIiC`>yMD h*4i-7GyC!Q1Qa@uBs|i=67&E7002ovPDHLkV1j~PfkglS delta 271 zcmV+q0r38|0;&R#B!BTqL_t(Y4c(T#4TCTch5d^mX(F)!MK*|(I${?F$QrG zpcL3R_lz8g{wN$v4{VD2?w#?Ej4=u+r4m9Ik|s$KYePWQw(Z$k`y|+vbYvw^Fl29> zbFZqZZX~#3Wz6%umIw~&x)w?)cF3|U2|^5cFvcVZs|^Z<#D7t)lsyIzcSHF%5PaZn z8wduwLUNgQg=81SLVkZ2I@wM66JNBGKb(_1AH}W{B%Ze2yiT~+2;u>PT{u$bL+@s< za=}TU^Zo6;r|wDyj8kypyRN&BD%PDHLkV1nECcjy2B diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left-on@2x.png index d9ed06074399d88628362950297e9df8d7983b21..b6b8a5aa82727c5bf33df67f58f22ad0b8fb6b28 100644 GIT binary patch delta 703 zcmV;w0zm!I1=j_TB!8YsL_t(&1>KrGZW}QWh4)9hkn>YX5Y#BQaA&|5P@NMbCs30n zM@W++q);z+>C>lDqtdk+A&>?Atul|^H>SBU;BqDes2L7v;C=hZwGAr2TrL?~F0IWA ziOf-o^LB}?(4(9d0sv*y3BbtZ=3)S8AplT~Mx&cvulGrux__4XNNR7*ss4u`JZW?| zQIE&tFGW#&ZEF(zG=jj1+p4O5iu7C@$t2I7j*Jxb_}GFwolZdzwpKV7BK|WV08rQU zGvQVtKtOE(fh(mHa3%soS>m`nEeZppA2CR)IUMW^5OPkG)Tn-ym4ZO{8_WU%9gT~^ zz~~3Ha%*9Gg?~mjJU~!0V)#9Rgu)ToQMcZxTR|w85M02z)kvKR!U9383V%tLf}{%w zme#F}sZ&8%uqO1idK82|;B9Ns00m(o24e0Zc0k-al0Nm+p>*Eb`2TYxbs!yEbAWoN z^YO1kRwZJBlTq1x+SyuE>;;B8J0R{Ip%aWG<=ASZPJj1~ya>W4&jlP?kLn180>Bdl zwJHb;F%Wahk-9q|nBb&kfK;1;q!V6Xs8c~$KxCf6Ssp7$D$)xKbt(u80E+Vz(CWAY zLI_5y!l{co6@(LroQ!K$b81x(7Sd=HAl0QH>4cXtAH0IF0Dw660t67;PwO2uAsD@Y z=)byhoqzD)O*|elG@}#bB7-$1@C5>oK-^kqxdi=vI2=A4kH>KR<^_g2;YcI>JRA;x zzz~4!_xr4An&6JqTr|G5&eQ4i*L*(zw%u-L0+R#CQ!C_5;EmO+R;xd=+3fpdGWjU? z?00hb@5Q#A7nuB!Bb@$xu~@tt3vbu;fH*Vq%>unBwG5>o5GfAi%jL4__xo3y l&8C(YfEZKcyWP$_%wNTeX#1mcqQL+F002ovPDHLkV1j}JMos_# delta 697 zcmV;q0!IDU11VT z#7FQAFp+cTj<|pWA`)a|g!ps4+4*`$GdQq=rCMU7rn;+pwePFfJ!sXNoX_VLPhwBm zPc70{s@gA?_zG?0ln?^QDh&W4%iYBSq=XPaJ{%5Tr)m1ma(}OEJhb@Co$3pMRB1Ii z(HV_K9}VJDnX?ej1Bje>({8uFne@bBtJCSU%#A=&?T4g;&f#zZLIf08AS!!i37v_SAhJxyDy5?3~~TiP#*WZVeD70AuVmqOb6v<8s+1PjP}j{zh$;S?6#7eSb0aSTx)x}Oer)IbnpfRM)Ug=oTm$0l9? z(yRu^*?;auR+`YQRg6Gvt)pFr{@CyLFRc~VZ?V9jQ;wXh9S4KK7a5}K-ra83I-O31 zJC=*$;jML?OeWvw^ZDIov-x1IByuLEC$%%7H`ZynT>hF)r=Q2;@mu?6fvoAQEYnPZo&Kp66yv fnrydQ_b`6|v)yT-u89|w00000NkvXXu0mjf@1jY! diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left.png index 17bd507f948228cc47530457cee467ce6e7acf98..67b3ccffa272f7b945be065e405b04611ab19a64 100644 GIT binary patch delta 262 zcmV+h0r~#L0?GoABmohTB`kj%+r<67#g{aNko;f&|51wGwoPfRjZ!M7k<{9 M07*qoM6N<$f*j3wxBvhE delta 257 zcmV+c0sj8V0>uK5BmoSOB`kldG;x2=!Iw0&4FA{ve?Za8vM8;!QA(vW5*nHYYeCS| zb-mB?{A!G8Xl+>uB!=Cm1h|!Dc`AxRB?-*%)i4b2h$ybcITw_@_k09WLOb9Dk?pXU z=b04%F=zr3Xc7Quqap-OFt}@tE9go=ARe6{E_wjCjl{-BCsKWhPNa3>iao(Z%Eebt z#GQclA5MPK6T~InlNErgM8)?+s`1!(fJnv#Z9tIGgAS`q^4sO4=c(m3kngh;O zYh2H&s?L4iU#4j~(9MjE@Db7=bzQe<+t$%f#Fk>?wL+j5YbCZ=yR_HS00000NkvXX Hu0mjfe+qjm diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-left@2x.png index 34efcdfd90539b2dc0513b924a2af26767e684a1..0e077961d1f47b29a14a85b369171da8c4cbd116 100644 GIT binary patch delta 690 zcmV;j0!{tV19p?GcD@geBi%PhejF z%aRjc!U;zp#+gsyh=d3^CP5aA_;aCvc8R>ofs+yoTI-k#uEs|D_ zV~HGNDjb)u=n6H;DIoxm1f2kkd|h2EKuQPzq=UiWUA-#8jT+!J!-XDuG8r_fpX%EYMRAjQRqWz6?Gx$^ijqI zM7P^@to!gVD85kkhxz~lSCTl6Q4f8{aMD_8QMdWy7aNc69SKt^9Ftgk^dJ;!5dzvPL4PbAzcj#u&HtvAIE z(OlFFaIMWd!Uc;E(C$G9fM~Hs%%>JM9^E_gKef06e>s{@E$*NB)Z!sk31a%xV)er+ zQqndHvC!ml-=*l|DY@2KepABBJzPLW~ENJ;RHF0@TFYvJRJ@Pm!_%K zjlZi4Ab&`!quk*B-0gNx>-E|-o6Wl)XkFmlbJXwmbAfmkN225L=&z-bVw&4_yDjAL z+ASWB$L+~v@_Dn_e3BL2>MM3OmSRC_o#ylT@6l-Vt=H>4XIb`LocJa4Ig4L=j9xgz z0%_Cf^mV)4ezROI6Y24OzmJe=6&s*#%7Z`|U@K;`St|bp@oKeNWpaASwR;@pd9EJj Y55x$;o@>?ht^fc407*qoM6N<$g7&*lPXGV_ delta 697 zcmV;q0!IDH1U)=urwP8WiA!;2`s#?%%9^=h`!pPg>76NiYb&$mQx{0a8K$ARUcHADhkQ=YJ$g?j(OBxt2QBHG;6y z7;xfhJRW~-G#cMZu$tw1Qv`t%pIWWfPm$_6oley2^&)|a#2LNnMcHz>EcBtYioTF? z`XciJqTlaF;!3d~JPeA2vLDn35Ga<7Pwt=*Zr~*sAgB4EPM%s$5;t%mj^pTjK5N$g zMB8%^l+ra<0Dpv~#hlg{f)owU1fy2rFX>`w`S$>U8`gkuVpSeX5YUR@9ie!}3Z3#q z#SyC@-jQ&_Ey7el0AZ=wx#_;NkO)WUkcD~05r`(G1psQSfLMe8j0YhAqUj!?MTmq7 zbK2*%j%XKMr_Y1%rM1$cZh!nT0T4^2MZMmUP<%Kipnvx0K`7KB1hm(JSUP@bfCr%y zh@5kg=18rTW`}6L)C}-kn|FjSEJ8rL2O$8W#U3$VTG)6D@5ozeaR**GnlCNxpZU__ zA>~1sKzuv3=<_Q??+Cw}&hU3D5zUVKY0>}x&p{Ax4ahqpeX$qu*95ZCC){v?z?HR( z+tcY3rGIG(Q%pSo0Yol0xWA9brdZ1%p}?S5FV*NKc+dd8vDlrn06BFlpy3z)@Xk;;ETyxVLxSK^Eq f6T2+S)WiG(3P4uybm>kC00000NkvXXu0mjfeNI*9 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-middle-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-middle-on@2x.png index 268e9f5f58af7e9377e323f1d4466d9933bfa440..f8f23c17bafdadc2822a933ee0ff5ded3631006a 100644 GIT binary patch delta 320 zcmV-G0l)s)0^0(RB!41FL_t(&1?`wk3WG2Zg^i|*F5SB9Ej*6*2iFCaRwG{y;E3_b z2=byZ=KW+6FjQ6bT5s7auFT)E>{sHU>$;z^fLfXS#yz%en>0+*B&B7Z=fpSG^&SGK zq9TN_3uu}q4VGn5;huPA0XPq;sK^2ss0n~??g50TBmmFtAb(4jngIAl1|Wj&j_}n# z$WQ{_-Ff)I_c;lmOa)0mb{E3vEPxj(m=j*)dCg^oICt@8w|4qEFy0jq=7dJ@n-2XF#KJgZ>-Gp9lr zo*3>28IF`f`8g)`B~)tZJg14raV%xxDf8<0z%UG-Q>-ZTk=XbBx6s_DO#27FT@jbh Sz1*V!0000P?f%YGvsx~^N51=PyqckZ!m+oWNdCMhlRJSV=fu8$Bv z6%`?bLqO9sX|ODd3ire_3&43$MMW0CKurLA^BF*hN&@iQ8-H1P)C9mcG5`^DcZ9Eg zBSQ&zcjw^;-{&NNG8H5N*;@#svj9G*U`~Jms3HKKo&ZjqjqI4`T%#yD+sV@P;OhyL zI%usU0l$T;PFm|d0jqaGI3thE!o^y!DalFdJzpN|U2T&h}VfZjn#Mt-!SCKfJf5s*enfj^J Q>Hq)$07*qoM6N<$f{N;o-T(jq diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-middle.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-middle.png index 046049cdfbaf183d2394585c97f67c8d095c5403..dfca99b9346a392010a423eec11d30ccaf825be1 100644 GIT binary patch delta 169 zcmV;a09OCb0nP!CEq}2Mz#t3@Ql=i!CpdtAISO$G3#2$hM;2tHN{y5*NOtYS5Jcpf zO{))=F-)+ijcR?*(Opc!RmqGJp}GYYs078U`1+7*C=QEm}FECN@+BzP5k ze+IvT)rx!2SZnHA1plcsIFbXFMV@4)MLq+jMQ83JSVVo%N+Kz%oNS+kob$bnvH*Ah Xts9>s`=#Qu00000NkvXXu0mjfLPtb= delta 169 zcmV;a09OCb0nP!CEq_4@z#t5((9<5VNB9EY>z@Rl;YB;s!;W4g!eE8&A|y-NXb_QW zHmyQR$!O>kSYwRRTBC5(T19*Bf@YMNiHicX^*s$KeY5Kc-?i~Vm5Fy!*A@VX`0=`G)*cm<2dfTX<6PC zgj!s{o921mnbBe(Qv@Odyg@Uqe|Z^rwLL%twg=gM;E1tBBYy)HU^oeQ7+``6Eb4y} z6M57Uc{sIAOu(ZDaAeakfFqp&fVBnyM=|v43U8FquLFPL)9ykTiVTnvPFOSySb*Uq z;9-CXF0g3nfp2F@TSAn4`8r)n=wbw77rOX4*>9%@v`tLJ^ajcf& zT(4x{7?E7#XhLEZWP-6p^$)W6kBhxSS<`9n*;g4bQwEoK8ZGyKM$nx@%3Ow*+DGLGZUo0jEW zL8!$AylI~2of$0#GDRRVz!Nmn`j?l1SK9+bU^~e63rCDC8h;tE0K-YZ!vGW9U{U`` zOyp5Z!VE0FHD90M;4+9L3PDE4)!czYhF~PrD0YC^A4wc)_A!zyb^> z0S^OAaDzol4}3dQ+7hDV%h%~rLKhaoUD00000NkvXXu0mjfIqH=w diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right-on.png index 968db9bd3055e2848ba085aa5369964bcbb85bab..12340b721e4ad58b7c4b6aa3c9a57b29de600b1b 100644 GIT binary patch delta 291 zcmV+;0o?w`0=@!}B!2}-L_t(Y4egan3WG2ZhV#-zp-^19>CQX!1l{)<-50OXOBHJI z484H}rBE=B{u2~jMo3(g1_CO{_a&2z!x>{6^w`Y-XwbED&Z@F3-?0tgy#T)%W6qrO zrz(nq4Z{%412{w9f_X&00(??v^f8)*K*W<$GOhIiuhL7bwSQ411RI$_+^Fn@mEBPA;LtPc9+BDw63!B|DSVcJdEbaL)ednatBaI2Hi|=iw65 zH4B)xXviq>g(_JxQawS&_gI-rBB7rnsv^s>>;Z{UUDquNL*U>yO76X?s!P)}TGe&E p51bf04E5dSc^v*}+qTE9>=!FdR#*-*lR*Fg002ovPDHLkV1i?wgSh|z delta 301 zcmV+|0n+}y0>}c8B!3S{L_t(Y4egap3WG2Zg%gb~3Weg*O?Td*C+NP{=)QQ3UaC-w zXXp(?C_>Tv^i9y>GD6~_G!QT{dEd+=ncq#Fz}Mq+&r2BE{Bjv*L?QWZR1t3iGzUH?d&3&U!+>-$dT z&)?Zi2g7+J(L{6Dx5#9K*hUqstWZC}6>i~~2?C+wh_1+zBzXX$MAI~j(%@o@RCQ>% z_o65+RaHq*mL}!iW#bYX^mm=6>ASA$7E$RB9ZyzR6-P7p00000NkvXXu0mjfs#J>u diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right-on@2x.png index 7c56ad0584f085a060b92200d8b3e599dfd48e92..57595b4a4b217fd32cacfdcff75c0b46ccc1ebfb 100644 GIT binary patch delta 691 zcmV;k0!;nR1I{JdqSVR}l#~#u&4U7dS9SjC?lTn6B)(+862!H<`Lin=TY<^&VL=S}V z1X2+p$f5n?YPI@|_%Gs+4u`|&$ni)xdkrEhQAJTCd7j6P>p7fxzh1ASGg+h2h&Reu zy~|J_Fx1#=Hls2CWr#mu@`N5I@dePu(efVxvA5gpgnCi%_s~lxQWuWM;)*je2gqAO zL=a5kb<3$G!hZ#XBXy&{Ie{dVBLXCEvN%#BAucUCKM-T{0HU>0uhyK&L`J#U@_0$> z|Gn3;>MPm^qk&vGqKf-Or1UX7QG#gUt-5iXvFaa$wxVs~95I;Gt@Qh!e^*N)swjp)>J>qtGd+}4Or zD?(LY8nsQ9W7&4kK;(jqmLaH3KG_*-&2DQ%LlDYsjogGIbqKePFsbv2L|m4>)%6bvm6sE*1+O>tJdn>1;N;V_?0Z z9!fxzy~IxL+M$2UqG6bQTBj%Q7tN23wXqg3ZR Z{sF#I^M}hsb_@Uj002ovPDHLkV1fmXM~?si delta 694 zcmV;n0!jVL1KrYj@mF3#uEr2x?qKh%C2j+NL`gJcVIb(=mB~K zHto7S0ViPD1v8_$ML{E^QN#>?;-v5Mp6}ieZPFqj=)ZnBPP9_uY$mIgK_=<>ZM2YwWI*Xz}UvWGhzwY;Y?@ZQe zwfsQF=BbDpgn`C(yPcd)C%|OAUhht1K1nSy+(2;-b0_WsAk=QR>s=ABFQOPuq$V8U z>!mZi7LbpC@P8n(68Qd@Q;Wh0f>`VRP=h3mBLw0@F-B+v1Yf$=j1QtOx|4meMraN7 zX6#^$RlL?kt&F($zV_--M%o$zl1(|nNe&2BzIRiOgf0-`KSy+D%xyZsNUi2pEq+Tv z5L2mzw;uvDj^G9durua(bB=`0m|A|3DRd;v8nbicW`7(BK}%Xw#ZoAz5tVwH_^8w=u@-@*5g(Gn3t%BTThAwcg~~Z!($#0RaDi zkZr@~8h@lRnOfM@1(1N;3kVJnRL$}$7f~0ufOja}R-lYQ!_0j`M? zH{4(__&%G>9#^ZCKl#w4*0l#B>pRHa;Wo>%dpUOUYq3~7uGi~aG$OT=SV$w2{^=Q+-ryiTXn cf)J4U4@+zLhAZF(8UO$Q07*qoM6N<$f}BW3Pyhe` diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right.png index 60a7aa0e8bb0cdcffff81634a6d355b66ac3477e..289702524657e725de0f87e31d8d465c3f830d9a 100644 GIT binary patch delta 304 zcmV-00nh%<0?PuBB!3b~L_t(Y4b_!RYJ@-#g{x!hA`qM#WZ@B9W#uyaTqAq$kxPw) z;1$dPL>veWF8U|0WO@*0TNOw_(XEU3^{XmvLqv~A=|~U^NS{)Qw(B}4O@Q7{jPbm# z>$@@L+qP{>+qQ{vLI_XGvb;j{1Nmn0f;gq4u4|g-`31U#g?}yL)ClzB1YKjXQGr|! z{=kRdoiIo`STpPHPV$2!=%}d%;c{h=T2B~ogB*#f<&;S)QR=S$~v8oEm|CJVMu4EL0%3 zgWvJtk4hLM9juvkk4o}J5>#rcLD;SgQtL?uuseU(6O9r6Wmmb8ZhW>(ttX{BYSYD4 z{8Ylkk}73c*#^~0@_|($|H`i8=2fwaZSvj|UdA)}l8$Rd`eo$Fa?VjzRbPMvJI`|- z0i`M`E diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-group-right@2x.png index 8ad4f407bd1a599398a8351b0e69b89f27872441..43a6c1476448f7aa70def9607e3887d2d0daa82d 100644 GIT binary patch delta 722 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 A)c^nh delta 1051 zcmV+$1mydi2(So{B!BlwL_t(&1=X6pPZdEF#dqHqUs4c=4G9See*(q=i6ym-e}K`1 zipIp)*x1`xX=7pREC@El-oi$S1uX#zV(}XC;PLhLt>+AT#&Py7361Z*NiKKx&Mf@S zoSCKAP4hfY)KavfUqe=(rLbR28A=i?vkUO6=73UN7h))+9)E;{D7%}6_(Sl0Ffg(r z`V!^@6=Eo)xwyFapkA*(t<`GxaD5jsg|O5*fd7s7etCKM{_ybdC*ZCS8AWmB8FZ$# zw6ydRLS7X$B-;%}^scV1UhM7dy#whb^kfo5XCAg%t*;nJk(05pv4ls8VW|N+>G%7| z#l=MeF+}U3zkj;FzwexCpy19;v)O!xz7sPwb-l~1i6K23MKv0Yq}%N#*mmePo}l~- z(W4+*PzOBEOfoq+nPA&_cPxpb5H@DU`1m*lB{=io_v zhRt1uyp;}xldLJ=86uw|C{}hhoTDa)`xv5^L5P8O5ng#!836 zN!G-WM}Pl|V$PC7@dS%vZepr*=%$-2jnRP6gpxyq4JTNF4_RU%8Y?*@G0K`~Er(|) zWF6>`HTc``lpNyCq2k;8vi~`kjA7_+dqFBFTNb75Ce|?fEjLJQIMGr+Tx@ZR&kIsv z)U)4mv1;qWA9aSxZ|k9JB&|8xHT#C<-C7LZV9!abOh0RWVjNRCR+^V%S+{ zDCVR??)A6;;|Z!;&LOkkawv8QE#;O4#B5+cBRcC6l;hPzn@agYHdGwnZ0TC8;BAA; z?SC|FxePB}cL;laer}1ml7)-TH{on};r#=NA7bPb>0tKC%oH(a&?H`N$ z1r~x#S(Z6QvVtj_ot^!}G9uSgE?Zq){kFZm{pR%a^f@M`qu><9Oixe0URhb$+1%W8 zUy{`D5a8bH9~~WaHa0fi&d<+3hJ{~nrhg7m=V7zfPQkW^k3&8$EG#@)Utj-laB$E8 zBzIc!&&Nw!TU&Qv-*mg(t|MyrQX}p4dj5s71!XwvITk#ho15z+GVG}1wY9ZwVa#7a zP8~el-Q8{CgNBLY{xDiNDpz&7M^>d!*w_s#+;eU_E-G8yN)M=O6W0umv^pN>PH7c}!=YNOR_D0%2x3o;*(+FWACWsimku0)^wYNZOM?iZO)Nt*FLIORCSH=rLOLLw1M(5n0W{>O5;+>r4J%^MUD T2ycUH00000NkvXXu0mjfkpa%D delta 432 zcmV;h0Z;zJ1Ih!CB!7`fL_t(Y4c(T#YQr!Tg(cg#q^8&=;5~!8Wh|sihc4Yq-yuUs zL*Jr9p?#%>5;F7&`~bl?P@8t}PtOsNaOh}x>4l3eOFo~@m9QoIz86wTM+o5|U8F?X zh#n#>vH_#>2w5(dmvJ1wI*zkKM_`50_g`Jty_aSA2u4m99)BQjNs`>o=X2q@G&Mfj zw!J8d;tr4(sNJh_p|OghNU3AMc*;>B3`4Qs@2}Bx3F4ZZbFEr8r z6X!-#J57NIW7>?NrgB8_;X_-n;lxi$hfQINp>$X}4LfmUdoySVsm9S%rPDA2vh9X0 z{*+F;(R2>75r0~jT4s%=a-=70N;jnI&au~_rU6amNH2#e-H<-kiGE*0*fx%)0%@25 z{fE-2FEt(U<{Lk}naytm=NgcdXa0000KrIOI$$|#ozu_cOxpwf(RCV215!HQrbu$O+G+K zIx7PqO`7y+QYB4_5U@xgrc0l~!YCf({T?fB>4k)MRRDTSG6ud4WvTo53{|LSZ z2KG4ZJqdGyQZW=#T3%khKR!PGtWYT2#d8h4if*Yj0P`n~IiQGf}70iGNN{PouN5GqQCXjmCpLG>kh%{BipRW%e9nCuV+rKGOFAs^PZN^7(u; zHZ~TKk!#VZ)`1KWJ``w8 z6d6ODnIKKlj>1QwpI0wDSaJ$hkX0*AS#o9>@}d0z-*0J5#t>&FNRzUb;6p6pr1k%+ z$pmSU0)MnXev4L&vSb|CLv;E9e74xg7~;$_q)AzeB77!s(*74EMjM2n z!yZdWza`f|$(+6e3Z+Kg*DuQXC3aj0Nip@>w%Ma{7T5z;9z9 zuU~Ql9|it^#NGB|!<@hF#b+53+$e}4t1rlB&VK~SNy`-KSwbyZGKyDxBVBS5;oB@j za*je~$g1pUEdET8geZl2R-iR2LE;tPNS549FFVVS6y6l97zSqZG8kMh{#P~_b zO3qj82WXKsE8c$AV1}&0hj=8Ye~uY)$K#riVp;hu*_+t>47C7Vk#g(%h@9TRTpR4} z?0-~(5(qd!z+$)CjY_4GrGx}oD2NBhv}8{OB@{$uF;2Yqudc2Zo6Tl4H8mCd5lc=A zX|-5XxLi>x{ea;)suSdHWZr53LAbR~uw{sN$q|4)kKP3Hgr002ovPDHLk FV1gms50wA_ delta 1095 zcmV-N1i1Uu2;2yeB!44GL_t(&1?^hDPZU8I-MwGHofm%Q5)uOc1jGW3baW;*{sBfi zDol)xjlGSPHWtRtf*_T>Bo+v-}`U`v_5u!5pDwWD-OoWq@lflf)OaO8L zl=6yI6h#3@1b@fJ$HB?T39)q=jmF(wG>qE?JWSmn&7NRv$jr{p22csFsJuUG&iK-N zk}zH-q1ePWDX=dj^;cp{!f6%Kq-^SluK^&Ov~Il*D5St^{+<`p z!YWHpRv{Om8{qnV8WUp@&H+LU%EG3QM1~7)Q%;O!eNtl>mL#kL3i0D%?1%l~H2f|` zcRWDIoqtRN=uQ78aVBB4gfvN;g&HDpk-Bo*R^UxnDy=w?>BpO>P+B1W=KuC*l)klu zj>qk;!GNS-_ro;DF1CJRwuVqQElidb;Cmer zr+*ISfc2ajaFIobFLm_!PNx$Ti$%_P-AITB$Z;cflW8QxL4*_UJ)ADjH=E61e0)5P z^Vg72tJN|9A;32bc#hg5A#)>RYHI5J(b3U;TxKvwdpP1Z=dC)&G+%n~V46`GF8syC z1rbHHTI~ZZx*=qoD=RBswzjrjA@C`fhks(vNnaakC#qJfFL7aHZ)0P_+$70CI;LFr z@bIv)zP|o?Zf@=&B>V(re#XV-6(3+~0^5&Lsq}GSVd25r+SFMcey-B!72FL_t(Y4eeG-O2se~P5OU25jyO!&>aZRsY?*|(1AmXyKv@K z#2pCs(SaMNqs|{#QRvTeg16L}&^!e3!Xe2eIVblv&5fR>NolPerIZJ_fCg;X1d;*h zp)q)$Z<^-9 zqzmNRD2kNhI97zXQwq7RtNOk_!_cJ=bo7zuc}9qnY}q)Dq&fjKg-jHThOlkmG^F2z zYPMyVm3vk)EPTuxrw7tUFTgyLLFF6((9gmV_6L!2^kZM)<&LXyiv2z{LebM2T& zmg;LGk@ZW?|9{j;R({m}Ug|`dtxXeNa{j&JZEn_OhzCd=OS(zjgunM4{f+H!#Cino zJ-8=EG@$71SaxX=-P)F1sICf6gdMsA4?~}(>2+P#w{6>=V!Q!t*c$?P%ChXHEX&@* zRX*l<{#+ErC2D+#SBsT03$785osX)j_SEPMol4{Q;ckaU5@e`OfIWBuSpaFn_#+QQcx5W{KAKz395` zv1ytslP)TWqDXk2=Y)unlF0XcF$}{6K7A5_M;WFuk)cq@(TmFZ; zBU|sgs5*zfGpt(#@Qpaqx-#%F3Jz^p_v|5k#!vr>SVfyzq)n zXnwbe!o0dm_5>p)vB|+Z8qzeqt?T-}ZQC=vxGOp>T?n#8*M7;e?5-@!K>>Km^ZczS tifi=vBTOw$+AOdp3_D*{RjtYK4fwojUEM-V82|tP00>D%PDHLkV1h}KzzzTa diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-small-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/btn-small-on@2x.png index 978b14099abe7a3993d7feed60b4d30d8c31a70d..2b8fca27de81b907ee1bb168475d162cb5e24d2d 100644 GIT binary patch delta 1033 zcmV+k1or!u2%ZR#B!A^eL_t(&1?`%>PZU8I#`iwq{0If)puvRy0b`?B($LuW2N><> zE-^MX_BK}9SQtZN#dY=;HcBjLC`_yQM74615UqeqBb?HB;erUNV&+v5c-mzj97cN#h)bAb*vh3q;Y~G{qkhKZXYO zH1A`9IiMVdAhl+*`LJ58K1acQDDtycSri4q)2aGjhVDJ1>znhqqy`d>UA zkCV}8T2M>dSod1tAq+`-TrJ+Q)*s*R8B)xHg^44Og zGX(ToE=*EOaA^(odZ1WzSQO7$7|Wqpiw6>bsN+IwW9d|s zz5b%N9im|CFn^)OSgUtP6LGZSNUVbnQLFT7zVLS_ zq*syk82$(KS~?WEb0vqYI}$^Lv_SSQvY zCM{~NxsN&b5>`MNCOW)92X4~MmU0Ab%OYyRJXD}Gt8xDu=wWM{o2*Wqw z)PIQ=-ai1u4>2-2&*oDT<;%t%R10%ZM5?<&Kk*LW0CH#vS^Viv^W*oxl*J;(v63D8>&J_(S4H(7+xh zeI&3LD1jkJt=VkeD3{Ckkh_NKCgLSTGY!JK(DZFI8oft~yBS zyuADfFprZQg6#(VLqI(|JUo03rQdNK3W(0!s#dFSaifG7)a&&CGm#Rx<23mf2N0Wn z8X$tfU=R$4Lx1LmXJ=aQA`vwsAAv%VcnVB&KBN9Pj6$O<_B{)AnCsY-B z@1o{2;Rr+{{Bl+lXf5u^1cjf&Kp0x8L;v2`Ho^R;Gx1mPDdZz1YspviZ&{(PNTQ27TMTmb*Ej69fb_oH~axK;y_K%~lcjq*GBEQXnN1 zS!lUw4}bX@Qy>BO#QpYp$@%NDNa5o?`@%h>2L+?3ynnSQhkRZCtV05kQ@T!pl#nxu%3F$b z#|3e@epEiQmNW^L+_d&3>){Xz>lYIM6a5hy+$S3)p~%3F)$UJCA#JGBmx zH>ru{KJLYbumXy3*3#w!+HiRfTgnkcF5%RX+T)d`B^Tn*_!CZ+ukv)6$CB3FA`H)0 zsecoCKLChd#E9rn{Edvo#l=@lu!yJ2JUZ75AaABo6SXBL;M5f^EiJufM33~A3pX}4 z-tF%0K0P@(c>tG6bjo^zJK_BN{FC+d^-rx{xDiNDpz&7M^>d!*w_s#+;eU_E-G8yN)M=O6W0umv^pN>PH7c}!=YNOR_D0%2x3o;*(+FWACWsimku0)^wYNZOM?iZO)Nt*FLIORCSH=rLOLLw1M(5n0W{>O5;+>r4J%^MUD T2ycUH00000NkvXXu0mjfkpa%D delta 432 zcmV;h0Z;zJ1Ih!CB!7`fL_t(Y4c(T#YQr!Tg(cg#q^8&=;5~!8Wh|sihc4Yq-yuUs zL*Jr9p?#%>5;F7&`~bl?P@8t}PtOsNaOh}x>4l3eOFo~@m9QoIz86wTM+o5|U8F?X zh#n#>vH_#>2w5(dmvJ1wI*zkKM_`50_g`Jty_aSA2u4m99)BQjNs`>o=X2q@G&Mfj zw!J8d;tr4(sNJh_p|OghNU3AMc*;>B3`4Qs@2}Bx3F4ZZbFEr8r z6X!-#J57NIW7>?NrgB8_;X_-n;lxi$hfQINp>$X}4LfmUdoySVsm9S%rPDA2vh9X0 z{*+F;(R2>75r0~jT4s%=a-=70N;jnI&au~_rU6amNH2#e-H<-kiGE*0*fx%)0%@25 z{fE-2FEt(U<{Lk}naytm=NgcdXa0000KrIOI$$|#ozu_cOxpwf(RCV215!HQrbu$O+G+K zIx7PqO`7y+QYB4_5U@xgrc0l~!YCf({T?fB>4k)MRRDTSG6ud4WvTo53{|LSZ z2KG4ZJqdGyQZW=#T3%khKR!PGtWYT2#d8h4if*Yj0P`n~IiQGf}70iGNN{PouN5GqQCXjmCpLG>kh%{BipRW%e9nCuV+rKGOFAs^PZN^7(u; zHZ~TKk!#VZ)`1KWJ``w8 z6d6ODnIKKlj>1QwpI0wDSaJ$hkX0*AS#o9>@}d0z-*0J5#t>&FNRzUb;6p6pr1k%+ z$pmSU0)MnXev4L&vSb|CLv;E9e74xg7~;$_q)AzeB77!s(*74EMjM2n z!yZdWza`f|$(+6e3Z+Kg*DuQXC3aj0Nip@>w%Ma{7T5z;9z9 zuU~Ql9|it^#NGB|!<@hF#b+53+$e}4t1rlB&VK~SNy`-KSwbyZGKyDxBVBS5;oB@j za*je~$g1pUEdET8geZl2R-iR2LE;tPNS549FFVVS6y6l97zSqZG8kMh{#P~_b zO3qj82WXKsE8c$AV1}&0hj=8Ye~uY)$K#riVp;hu*_+t>47C7Vk#g(%h@9TRTpR4} z?0-~(5(qd!z+$)CjY_4GrGx}oD2NBhv}8{OB@{$uF;2Yqudc2Zo6Tl4H8mCd5lc=A zX|-5XxLi>x{ea;)suSdHWZr53LAbR~uw{sN$q|4)kKP3Hgr002ovPDHLk FV1gms50wA_ delta 1095 zcmV-N1i1Uu2;2yeB!44GL_t(&1?^hDPZU8I-MwGHofm%Q5)uOc1jGW3baW;*{sBfi zDol)xjlGSPHWtRtf*_T>Bo+v-}`U`v_5u!5pDwWD-OoWq@lflf)OaO8L zl=6yI6h#3@1b@fJ$HB?T39)q=jmF(wG>qE?JWSmn&7NRv$jr{p22csFsJuUG&iK-N zk}zH-q1ePWDX=dj^;cp{!f6%Kq-^SluK^&Ov~Il*D5St^{+<`p z!YWHpRv{Om8{qnV8WUp@&H+LU%EG3QM1~7)Q%;O!eNtl>mL#kL3i0D%?1%l~H2f|` zcRWDIoqtRN=uQ78aVBB4gfvN;g&HDpk-Bo*R^UxnDy=w?>BpO>P+B1W=KuC*l)klu zj>qk;!GNS-_ro;DF1CJRwuVqQElidb;Cmer zr+*ISfc2ajaFIobFLm_!PNx$Ti$%_P-AITB$Z;cflW8QxL4*_UJ)ADjH=E61e0)5P z^Vg72tJN|9A;32bc#hg5A#)>RYHI5J(b3U;TxKvwdpP1Z=dC)&G+%n~V46`GF8syC z1rbHHTI~ZZx*=qoD=RBswzjrjA@C`fhks(vNnaakC#qJfFL7aHZ)0P_+$70CI;LFr z@bIv)zP|o?Zf@=&B>V(re#XV-6(3+~0^5&Lsq}GSVd25r+SFMcey^%l9db$-r;{_2q|kxGnaP~< ze$V;7?_4I1@pvpQ!{xjTkYv0v4`?ozyNQT=ayT5jCR#(h`+te@x>Bipw`-7=t?^mnUZ-xzL{Addlp4^7+kDZ+zzt`&(?RMML zjNk7U-ELRZYBe@2z_Y(iI~csQoTKm@UH4^kb5jYDHaQRuh<(_1qQO_nmu1jQCUX!9 zh4vV30x+~Hg@0|?Cmas%A#Ls{6HXdh28B1?CK3tJ>-981Rf8N1`>-!G-l+DI?JR*J zH}`|V;I7O$6;DpLVV?SYKC!yG3aQ+_gixt1fkNeW^h$6+qcW z8MII+TtlZj>+9><43+{`YkHL$)S752NhpI3hr_4IWPj4t@At*&>8WjxvM)8LMNK72 zG*Sk|1b7mOM9wCW45gi#lZsl@L^0kebR$p##gbH7Sy{Q^a=A2b*nykwLyuC6n(1`< zmX68rQ3;eI$NHyDB107$KXOu06TpWm-DiT9N+lmaA7PPSNZ6%k8GdTw`5|r0f}II^ za&mGPf`4nH(MTZqZBHTzC~Im_liIW~Hz))S86xJDG_t6gn$)I^xj>QV*Ae#HNP2DU zTojAJN^B+pp!}Tj0w+?OD`4tJ)CLot zWto@9;}I0a6ySK~b@?X6@LyuzV_z;W5HV}@bLlTINpe@);z(F7NkbM3R6sPY8 z2-iy<#Sn_6<)%I*Xlru(JId$tkM*!;p`V2N7!BUz+s4hkn4K^33IqaLzBU?-0qnj* z8diLkX`q4PwB5t<;NjxS!X4wHA*DkIODFLYn*|fCkFVCN+8+@3?DeoAXJDt(xsbe+ nVEP){sW!2AxJJ(MwZ`@jvGw|rn$5pD00000NkvXXu0mjfZ+EP@ delta 863 zcmV-l1EBo$2KEM!B!4|gL_t(o3DuZQi&9Y-$B(aR>I@^kj4r~3IS9_^h>H?tH7$c7 zDryl8R1nk9x=j>8^Z{D5=`#d?F2ZHM2#{oSS^>>wv)3>q?`<~Q{sOHb>VLlBeo-!$zbtFdw(QjJ z?d=@_@KZ9Gbc8}7foDwy>Ngq^Q*xWWuIBjSe6yA6hjYdVk-(ToS6@&v} zANGaDOSN9_-4rNd^KfHhW8dfVO=3>Zi)uaNl<^t}1R#|?)PtL?HwB80&m)nD7z_rp z8m6Al&dvlisAZ<3VbFX&ABXTw#OG4pl-{LRsX?u&rhg$`!=S_A@KHP-chH;T@mM!` zB$!&VFEyw|O+%DuWEd0)@DLem$(s_IUZoZ_ach4mlo1#L#gbI?`~6oP4u@cjS-q%1 zEox>mnd?UM41r2-(tz}e)skBDDu9ny0$MB1dwZ|RY>-y7B%sFk2dsRbv@c@ zwQfOhbAL1%2?YPjJW4HUQkym`gF@h#Q)FfBoCiToYSYHNHuCp{9z~#E!L;8%(64mb zW$1AFsZATSC84y+g`jr3{UjEP*{!af5=<@Sno1kAr3EN|n+rNkqQ{&Xu6wGHRlU!w zsZSOq)m+eayZsc~vM@)zinUXEMlGpL8?Y6kO@FmszH9FA3x&ca1iq(Iscj0aBatKk zOSjuCTrL-nD?d^0luD&vv-Nz|^eC2&x8ZPj8$rI7BvQkMNkv<*8HHossgRnW*nQuH z&?&cKW_IwZakKBV#Z5J?lUy$MK!yD?_0#Eoh6ZnNwsAR~PJx(F+pqPV8*;{Lyr z;#f7p;DZc<2`F~keSCA>{G>6<`NjCxkkSK$<%Wev>}In`mH|$!mnMHe;MnVCLw?qH p^koA5uZs?1V$o@cSz=7segoal53P@BFs}dr002ovPDHLkV1g2Fq(lG! diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-active@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-active@2x.png index 17d475ff27596e29284b30f660986a724ca5d479..7b33a927cf48ef9833a979d6f914d2ef3f93bf75 100644 GIT binary patch delta 1978 zcmV;r2Sxbe56BOYB!7=dL_t(|0qvX1Z&g(k#?QTdKznJ+s}M*bkk(L0%cJRlD3KH# ziisv#69g3q;fy!Oe3z{9W6(_wL>MdTMIw_JhC5J*qUTQ`)_I_h_Y3 z`CjG@2R8XH*))0f?Abp9%S|M%DTIp5gCg@sA*}WF^+g*tY>3vbT^m)a*;=6a`T6L| zl`GN3ix;D7*MF{I_^h_)&YU^(Gbd%1(h1$OXU~WX{8d|De}8{8I5-&8-s$P-Xl7;x znSX10Qlw4=RnS0Ksf6y|zrXk9&6|HHB!||oUmpz(4LNwXZrzG>J;Kwn6g+$7%9YWI z6)W81=gysrE?v6B@pM;L*Vw63r<~hnk)8ILY%!Kri+{iW#*G`FLndKvWJ08_XC@QT zR0J~S=IEOuCwukk)f^6@lY_EKX@n}&52@Wefj4d6zTMe}XBr7145o6BN!hk-n`@KN ziS7{clgKEA(20o&(z7oBv}w~O7oKoc|}#5^G*!IKs-moLwC%1Q5!le^vz49>wi5k#R&*~T&Qr3h@KVTLE26TrQE(< z-Z0y|bltjj5g1ekJtmF|DTNX$#P5RI=Y)0dEX0xpgp!|H^I{luo5Qrg?LjUTMktdX zLU;(28#iurVDZkPLVPi~77<_|l~OtEu{lhke+na1ZPnWXTVdiu$F5hud-rZ={0-%y z6MruSgYTR;23sMe5JELFsmWuGf*3+67JBZaNLk8B(e#4ixk5qFFo!+ z2K)(1r~ooDVj?6WnZ{7-5CUS(ED9`O0-G<#z-%WW_CbYd^5L*R4d4N6mRKk8pfc{k zL&0SD!0IuF;L^zmN~nd9<$D%l!O#W}WS9)2VGbZVETO~0!#x7`JW_ds*=pf#gnwDs zgOLJeu-pH6_rW|=JtuixjhgT5?2H(p82B>6IjIgXfenn_OKW<$H<_16dp;PUGUzpg zTApWViYaQPymxn~w2&3I;koU~h=Re!F|DKnv} zO?pQ6D#2>l^1Eh`lU1i-HXE=B+p@~zo1;uclM2u1!JV~Q&CPp&Ds?EG1QjZd%3RCP55T8s)8WFWA|z#ScfT-p14r zQxmH8h3~lC8$)|7Yax?bdkZ~aHkeI<_EMJdeQH8gi=RkY8@*yjM~52|)3CR4NGHLD z4R|Rw`5qrH#nx0M6`^`d__UHM)%`wbNl|3ExHh(8ZoS0CJAUY)V zq_zs}Ox!yYYiscvA(UAtZ1Zg?`RGY+dm{9S2~{|}uNvD$uC{seW+xo)OcG8@=^l$X zY{XVd&Ur=Ej`<~Af$f>@LJ zgCw=nSd;w5li`LMmVe+4EcL-gZ1o*fI~>48BXjHPWgwuW>@#7>?*efpR9z47CR2xZlV7NWA$`Ymn8wN>I|QhS8N z<50T0yWJOi*cu~sUZ>w^`${XGf0M&;UN3b*^}Xa(f0cLCZGUzLE^iB{XkS=Z^*9we zCR;;#;lhRJ>Q$r3u|Ov>$B`E4y3hLJvrTSsB!BW9CA9;YyuYU4nIV@EI-yJ82M3}( zg%cc$xeG`oO2r4Ob^l$l>usqMx?{%Pf-N*(9kjI-+rulF% zTQfi=0NL?UCo`0Z0V|NmFpRxTz}kP0>mG+CrVgLXD delta 2004 zcmV;_2P^o<58@AyB!8(%L_t(|0qvXHYg|6(i9~8kS(FZU8f`||G!8cz-pMCNIf)7QM(i*T}1BF7N^agFy^pd82zump2 z(@Hw$vd@{xWy79xX6Br=zV)58)?VkFy-WA+-;WksI0nk$M1L(xf`LUs1N*@up@9jl z1eTXb+|bZaM_XIlJ38q#^-=ZCL;@ZjJf+<~)xW!U@7_1#4_U+q; zOQq8HGIubr$^XcvgQrfN`a7`PSmK&OsK|UlWd10G)!yFTXw8~6(aM!8qjEW22AY|f ziLP9^5?#1(A%D7d?HYzps=svdcH=49URX)qDH;`l9~+{-E|wOiV-< zFJ46Eb@gXO>UdBE4TKd+=wm&F)ZN`3ZP>5@16_Es&7%|g$VFFAPfsK--6jlgrQj#=DY+AhXDG}+0U4t@ z4TJHbyc)7CpPEjQ`YAUOdFeE{xf3eSI4o}&z*8^|&#^pbAoAFv16@=T=vLcQsZ@^U zp{rhIu7t`f%IeNLF^q=i==Ly|P=`S4=y=>q^?%=oV5%n|^nR|wbw~7~0QZwSArxcp z+$ruh8!x4#TD59b1O~8pOrd$K$;*{cY54$6z9y`BXH9#~JCJGV#V|;v!<3im+z932 zhd}QI_}aB=9sVM{(<#)4=>!9%l-fRz%?=5=&5cmys`rGn#Dfp*yHS05db*b5EZ60| zMSmw=3I;zou@AOfS}ug@&ZH`$4uKd#X%>3!OTiyjq8S#jfiVvlxezMAugVL0xM$bH z2{)=kXx`p9n|yRL>VpMLU<2cvAyk0h2JDIzD;#FaV|f@5SEgYwOolP;`Eid6?x9Nf zJb~ZC0AQt3GsE48um}N@ADs4htaeC{ZGUh=h3XBEEnBwC0km+NEgXyboshQ+YuMza zM;yq2A3+HfK<MR)^YS$*c(Q}Tw^5TPB20T2M4pMU$( zA|HmyFoGHEq2L80R3j&KU6m`}(b3^@r{%0rU_;0O6CI4+ORIXhcQAx(WCSBr;r1p1 zt`U9e;Vu%ufhXlV&DVNxf`f?25OtcbW?tM#GE^*kb>u z-DVarJUqNt$~RHlRjbvOt378U214;vFoPW%ye+ex66^QOgi6aJx0TAy|SGO7GTG*Qn*o19qv7{zc4;A)H=`$pC-YD{h z(cXin1=Ec4zzlY5z!q%swxxo@B(79V(`(-6L=rm5o>3EEbFCS69y(*oSQlk%3B# zpao6VGZa^OEk~8QG+P z`9P*IW>t#Wrt0aCx7iM9xQ$Jy?jYJF?U;IrJcD-<9)Mt^oDUu6z$R?-xs-bJn76$l znH&(CP$~LIS8N-3#^$cu6^*57jvQn*VI#I;vx@(Zz2sGjWy2ULlvm zGy+-l|Ja6&6#9OEs=xtUG{W*kLU-=mIVM5BNJt+K4+sNtB?{Tj*H&*MHv0!BeQ*IM zPqO=eAFMd#0-kFH)dnj8x(+nZuf zi*(&o!}^CKZgC`ka#cvoRvuXhEELa#18X=C4}IVS$KrmUrQ{6A;)B(?4_55@6KaHV z8%fjcmjCO)hE2N%Jky>VKJw=R`GO-cNa3l}sJ3PRN2Axb8lm+>LL!|acOes5PZUKz mof#*Pszqe3YoMw>Ecy?qMyL*GrEe?%0000) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-hover.png index c21168c489be4dc66449f28017468a2be9ed730c..f62088adfdd0ede85e73f66fda9216bf33df4531 100644 GIT binary patch delta 783 zcmV+q1MvKv2Brp(B!BHmL_t(o3Ds9kYg0iKeMw^y^PQkcV}d*BQkpL`(3LKQA}YEN z3o1wz*WHLBNPj>VZu|{`zra6`L|p1V32`X_$#*>GHB-{b%e;9nO`!uX!@PU%xo6Iu znauM{r&Hr1H19)5Chb{!Qt;#OR7BNN$1Tk949#X@N-NQf$tNEgfScr1%`Idn0P!+_1|d+%^V#OEfflO zLZQ&MSaYg!TiLiy84HY?#BxH`5mB$#`<9A~Mk6OVPWdvH7{oeduo-9*g#y=rbuSza zn>*M_F`ai*l^DdDVcJkUHfs}w0?$*a)CLtHA-9dO#D5?bF?DgYi30E$cC5LHbd*#1 za8nVBn7T)m7ST$jQVj-!H=w{u>r<#lMTtdB{CV6K?^V4P(eZd}swv8@QcKrHEUF6s zJrzM=vn5)sRxhJGPjJY)Iy*%oxo7!_38IggL$=FfTcX&d_W>Bk?Z<$!=0?(0P31={ zViKDbK z?D%PDHLkV1ln~cmx0d delta 773 zcmV+g1N!`?2Au|wB!A>dL_t(o3Ds9ki_=gPO)|BurEN;lc4~_|nWap>(88>ADGZ{b z3voaNsp8s=j0oZnn8j@T4T8VGKhRcO>b@;?DFx|wJtvb4olNrbk~WKZ@MvG|yXTyD z?|sR8>T|zWg6e4#Z@<*vuN<^bkWjr3cz@I#h#bU~2 zGEutS?mtAg+-kLc3mqJczH1ajT-=vjE@y}wjYbNyBI=yO1<{AT#Bk6eeB@gw>T*As zOztt=6d>C;mw!H~RBEqUtv-{8gqF+Y zJBdVM*Qz;Dx}9vSQ~CnqCb9g8^(9o(w6~1NY&P@5ab_-ki9xJ029JTXP(-Bmg9*^HZ1dRHWNwH`S>y%i;#K+^deIZU`DWUWE+=%8no5)U1EJhUz zIj>+711W%cUl-_=G4z4SI?S6cY5yc8W;e@lzgLv2?DTo~80%sn> z&vua3$94$9z72iQi(cOWP{Y zCjRqK&43IB1706KpdVXha4K{YL}6l_OMRGR>tO!}-w^oId#PO(00000NkvXXu0mjf DP2h8S diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-hover@2x.png index 85d983288caaf558c17a2f6841b1c754e8d70907..af5e4fa78ff88f57c2489b6a80493db640d689d5 100644 GIT binary patch delta 1845 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 D6}MnZ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-inactive.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/classic/tab-inactive.png index e078572a694d583092c16f0819f15c1efe96aae2..b228738191748d0dd1f44dba4b2c60c42bb1d0ed 100644 GIT binary patch delta 777 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 V0`Cv_Y19A!002ovPDHLkV1mh3Q)K`E delta 1785 zcmVwaZo6ow1N#Op=lb51|?00 zR#4CivDK;rMQ|iK@bNE*IM9JJCqBlGd?7dpY0`-SM~*u%gbtI*r8$qg z_qj3N4JYTGoPE~%*0eAL zJUrZS^ytwy_0Y>Y#&jIZCE!8lb$y)E@y*7@##ajq3tJCvmHVmEtfVwHHg;B~ey8IJ z&nEvVc~|G==Kk<3H<*~?hiuPB@d0dySt;#&d#V(sbm6O-wCI+0%?1_uYd+PJv57_F_XA@d&% z(33ZB-dymiAVpZIgkHROv3F-@XI_|2C&}E}+KM=eh95k;qoX4}us1>KU$t6obY^B| zC4~oXw@c0P+uPfp3i)ZKxq0U1=4QlEluVf5S(C#xvVYN`cFO7Gb1AJf4?TbW{3UtP z&v?`EBBh7^9_U+p-*&=+Ztaw_AhfYpT)BA=v$Yz7QnH0k~v4Dm(aKcx`5va<4u>=-_Bx-TD$lqkWpN^+-lP#bLpOAg ztRU2R=}Dbm53M&Sq4J8Vj@UaX+-YQE74@O*igna`sg%A4rjW{m3fJ=TS%E)A?8H0q zn2=Mwo~Wdkf&nZJQ+-!&*Fgyt%6CC}?AZN14}Uy{3=FYl9LxmsQo~@Fz!jneBb3b# zk#-5tJv}|8d#91bj9~DiI2=&o4h>cJ)Du@*+LTw-oi@Hxcj44m%za4~7O|2INr5BH28AvQ^CZXsC16Z7w z_J2Bz`58-jA_$=>P2NDSl}gPd)9g14hRHD6eP3tqgeu^(13y6muvH4il#$i<(_w^y z32b0=SbcFB%kfUAq`rz|>OKslY$>CVkFD9~kCjkhay2QvukZ6np<7-Fb*m6ILiW7V z5<($#o*yzSHghEYn(vj9S3)&~rUdc?9)G|Ndb5*J{dh3#!2~wvr6(Nb`mTkqy%H)E z@f5NtRpv4?D+7kfFdF8p&!_eCM(D)EM33Ur^C+}TYO9p}l116~j8MY}X0WFbmG!!MxpR*T%mjdJDjh`cG{Ovtd#H$e2ZgrDU|F*Wh-N;MtV+xlv#kiOLWGWvjt)uL z6%?|!h*#RwDFUL|&ww2p68ofAoQ=7m6(UqI=3{B9k{t=97Tj}dKOhener$0WpI3;m zF?Y*Ns9u~sEoJO1?R`c3Uwr|^5+g}4KMdY!gkcLdVVjGk6>P?K>yviQOsG8beF+<)W`E<|b+Y1NBGZV% zCTzn-x5lTU`M~a{oiqIk#QpN^Ckd+!3=G7zC)0zMXAfhe9)(P7Tv}Sf#2tC+<=NTU z-_q<&^-#I{SrxzEOHhqzfHI2V!%8NE*<03->ZX1T4&Wk<(0x5r0wyKkTOE(FdC=eA zAInn)Je1QtNfs}b*>d`Rd3o7>0`#}~@Wb3+lYNt)A0m`rZRm#vV~Uw?sUtftl_Z&r z-eLC6Bst7RTaG43-Bq8j6=%P^b?erw&Rp-chX~!f)A~?|fz%jUNb3Lip%$T@2fszA bXF~r4bN_O4W~J?R00000NkvXXu0mjfDC}{I diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close-selected.png index 989c036792725e5d797c1873891e2515c8751234..5e3852944759450113767e8823c650ac5d33cbb0 100644 GIT binary patch delta 340 zcmV-a0jvJ<0`CHlB!4zZL_t(I5w%mnN`z1p?ImQv1cu8PnuW9#YT?SDAMgj-*2Z0a zhxGwMAJWzj2wVxlU@Ji)7%5Rvd7U#b@up&CV3-Ss^Uk^Vo`;G;O1Za9?QgTp-Q5HT zAr4&EJt6a!- zx*=23XJ6e}x}6Qw*tYHaEXzK+u9FK;(`SEOzz9QEp65L^O(Wa3m1JuAz-V27?)(0A zp6Bm%T}wQ>BvaEzfW9)oG|f{CXIxCvbmlnDGntw``zr%*!#D_nha^b~%d#G!Z^_j3 z*;fMugt002ovPDHLkV1f~rq|X2V diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-close-selected@2x.png index 90e2578415eaea4f449d5cbdeca85a91212adee8..9852d56e3e8d198b3544c1f7c8c0266beb86c4d6 100644 GIT binary patch delta 840 zcmV-O1GoI-2HysdB!4DJL_t(o3GJ3mPZLoThHWWOqzDKL6B8FURWR+Yi7_Gi19b04 z8XJ%m8#Zp-D2W(0?1Z?+Z4-ZknlxRuBt{oi6OCBnfI-W7gk%qBzTeTv z+|c;g=Ia#@SdGO87Zw&K=|z}HC%QcjyEXW@HIBBeXA>tuP%*l(v2p9*;NW+$SbV1E zaWN!F0vL~DLxWb&qhj#5_1Y+dJ&9Z{w+u%YN4vhhzKn0T32?mA;^E@r;=BF*eWTfIS}shD;olq@wD{P*L*1MJ z7x0R>C!WvG&wr@ZYDS~cusVSa4O($m;Olu2{oDa+1%DSQAs&epE$~ry!qhQaZkWS{ z#@6D4Z^-Ke{1hUJPF0iyuOl3b#cl?J!2m(sjXBv`nk$O00v~*d(YbkL=AM@)1?z%{ zWipvJI%Z4nxg*%nptXI1?pR01eqMo7WG1uO>`N_RvWUap-ku50v)!9$l>nb8r*?Q+ zM;GVWPk#awqKo>}mUzd)a?rv?DwQe#V`tv(O?>bbT@|8{L?SU&DwV#euuxnM+1=eW z(&=3mfOB6p4zN3hYbx{ zd}Nyvk!}*;9kPoD(P;E_p-|YFo11&D=N0i-+<&oQyn+o4T0P$v_=dd_`0rDaxUKP% zstJB{*hxGT3SCy&t!VsRd=}e+c1he2SEY$av!(GT@kQ(?fsX$T{L}%y>$3PE_%R}I zP5crifsNJ_Xm_MJPcME7l4ZVQJMjeY(kb+*JPAx zP}7Sig)Uu6sMwRIgwA%B`WGaSWlttV1Y1Fox&#W^uDk9!GkrhHyfUpjID|Wv3q4(4ZCf1-_k)6o0_Ez9N$1kx0$V%zQjNJhW>I zU=14@SBnq6VZQ=`G$NW#UhD|I_DDP)zZ(jLS_$eMtjTt)wXFCi@WGcD^@m?&-gS9$ z&M%Aj>gwuSZ8OKLF4)kZb$vqKShe#Y$IvvH!S(g^mr7uh#BgwMV1v1LuQpm0K-Xl~ ze2WND0DoPuKaF+YN&h5KShNL%Ce;yPA<>*x%o` zCMPG;3jPk5vu5mE@wu%bdqa(=M*)UMMn;~f&;L<(b4WBbHO1GyB%Zn~v4#x|T72*o zJ+<%N+y~i#hyw5w{#kq#yGmTv_0$N`2RPS%#ZSTS zz_zXh@muT&Y>bLPyC)65AG_k0z|MNzaqbS_rCn&#g;x<394i8{3lw64^Drbv!@MZa zlt%RQzKtkQ0SaRNC*#f$Co%8-O(o)Ya7kt*oqaI5{~P1O+^onX>VIe#W zFrqOlE9(o;)&^j-T?T4)$j!|?&&0%J3lzH}BqY>|tQqVTpsCli4YsZ3xpdqpvw=u0RR-bw3;r89a#VX002ovPDHLkV1kayhPMC! delta 291 zcmV+;0o?wr1E~X$Bmr=dC2xQJ{CWB7*RKjd{PNtnbF+<&jkCD9xrKoE+V}6@P5S!! zzT-3iBmp!;=J)U4$G(00CJR%@!^3lnlate?va<3uObkRbqPqYr2=ueLxp}g^y?q@M z6BEPVzkmPl*s-JX^5x45f$Co%8-O(oR8>_2t*oqaI5;>M1O)~Ezk7f8j?vN4vGDWf z&!2!{Fdt|L?l71(Z5j_Sui z3JyHp$J}?%x#!(`+M3NN~bl68QfVkYzbED{n+>xPK3Mo+^OjC@!vHT;X1Y zJ2B3%*}{Gm<2P)+8~>TDg`a?fheDwzaI|*2pYqAUZ*3l9ADJ>R*$5GUqo)KKjYgGl zIQ*Vw+8ar5@K`LS!scBh5_t|ABC&3QJ2?`5f}Ne6Pacowjn!(EMx&8btJO{_mC89h zU18WTR=2meRe!(VuUIS=X*e8?>-GAF)6>(p6Eu^B5P?i4^9+&v?DzX}yWN&3ZvenZ zl*?rzAt{QY1_A+v2*Or!yWNt_W*cA~JUl%7WrEF)gb0vjHk;MpQ15oTaX%)uSLM=?pzA63#fA>+Y6N19+#ZXDG*3LzPPw3 zRjbu=>`nUw*&4s;5R89J){vMr86?;v0>3+uBQpL`qnavF6IYWXVFfri%2D0e*iZn* z-rVc;vVZ8Wudi8oLIvsa^77>D?2MJ@EG2{#05~+#9d%fQ7{{B8IHGkM>4u@mXC#VtOP7h>5$z<~18&RoL zqUgUMS}Pp0CIiP)b*}5Wh9}TLR7S8pbk3KlREl*5&Kt~rrit9?!cI3vFLn!e7ru(R>X$;m`ed3-Ycw002ovPDHLkV1mTyaiIVJ delta 806 zcmV+>1KIqs2D}E4BYy(}Nklp3Ap(bohnqP2qu=k#H#avDuh&cFa=EX60;hdp0%UxAe1DvR<9iaM1a)c@HFW|9AqEcR z{tC{{aR@tE^2cg64^cLo{j##MvP~c)KhJg~UJ~RSxC)k-yhQP4 zNSLd|Vll(z)*FSc;Pmu#6HsWqUrKPgyJ$R-#u|SBxN( z>w;i#e}DhqA0m*T=s$yq5so!PnB3jn9V<21uy+EWFp;|;{3?^lu$NLVdfF3GfFCZE zO51S$30JU_PN&Dfxs9r-Da=2BXlxQVI|D$ kp&zdWfrSDK1?H~6Z&#M09;gu*ga7~l07*qoM6N<$f@x-Sp8x;= diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-dirty-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/file-dirty-selected.png index 7513aefcf7c59047266d9f6813c6591585810657..f79a4a8ad50da7d9156ae485cea96c064353efe4 100644 GIT binary patch delta 314 zcmV-A0mc5^0@VVLB!3)9L_t(I5v5W=YQr!PltP;jY#PWZhkT*8d?a~Cdob>I?2iQM z2iUjZYfm{CY+_n;X=7WNNrO>k6#^a@R$6IByK76WwHSlR7%{b@4IcihKuYBIW3^SO$nLJ_1YG&X!cmlsnG4{e~z=M0?8sfd$>%Gwi7j+d2tPWnrSpWb4 M07*qoM6N<$g1p6$bN~PV delta 322 zcmV-I0log!0^I_TB!47HL_t(I5v7vRYQiuSh7*}X*NQN29QX>m+MDb-=0&Nm)4qt* zK0v!G>E(9iMWJP7(tv2&>_1FmO(O_B@R9T+|DT-Ga49A06N5ft()KoZxOD^0`4DT5 zfCH-Yh;mODuY-D^gB?~J$IG%Tzw5d_2_e3sDEgp$XKyFL*nc<(f>qNrQYQ?<6-=Jm z8q?{EEX#IPRY{#Z&v!6bv<*y6oY}TL$LUPRvaC4_WV3?-qFKTBxl!ua#Tk8vp)5&5qGJh+}F_bW3c>!^fCy{6w z5e2T`33J6xKjZkAKmidyBWyRD&D*2V=-F&Gvwh!p0N2d%44=?<@q(-3XAwWEpcni7 z{$tPcrV2#h*6pIyKn)mUABW(Ov1r&hwK3A<)?>mn3 zOK%vM0N+Zba(@plF_Mx@WOT7APdBK4wo27378y1cK!KcvG{->SxlUV zaD|`3pE#~b0;fgXE))vavnBusuJAQZoH>&OsK8cK!lXcP^I9(Y!InUbSc{rS?89&p zpZs_{-qkM8Wx-@J`3_e|)Jc30iZd)aT({fZiK<{a_J4Z4oru2_@4%%=0&_8DjYi{@ zx}9q(j;}bc;R?8Mw@WD6az|AKmo%%HlKfRj@!jZZIN~O}RTCH}sTrOYH_2zs&_w^x~ zwcG74t$$YQ?Ql4><@YYcCyo0`fW36cQWJooBsi`#6q2|p+z@!P3j%!tqs%{q&%y`c zy@2?h#9fWCX3_^Ba-QjC1VreYP!ui;oO4>_zOXBd1pH_|aNs7IQ3YU!KO=mY_$S~4 zLx(E}Q3crPQ-LZV%;x3qXl4I4PC&sH7VZfWVKj3BuCc{mkVcNJ@o!n?1W-CkGwB0I hsdp&WI)QZs{sQ+?Kfb=(DNz6b002ovPDHLkV1j_QRTuyO delta 689 zcmV;i0#5zY1<3`FB!7@eL_t(o3GJ3aZxT@yg*z%%Ew)MBR54*=VvLDybWQBWpF$+A zSi6Ef8~6HCRAd3H&<(M1Awd$vRAW08g<;0?kw;SX4f95ubi++fW-@c{J!kH_@4oS( zC|WyBJ9C=kaIXZcDsb*!n$Yt+DtFfOkY{qRokC%zoyFD3yni&?P{fdB1jrGOki{}W z3Ibs!OqDx%kK^M4C4~HfuvMv4Zuk5Br{nRs8;0QoaL+T|;bVN~AGj=k8uE)uy4`NK zAI)YnU!;)@ypvA}UxApVB3uQmld#cfJQgWtB|r8&#Mg*e|E~%t32VGrDwXa}CX*w5 zVdMgQi^bwSVt>gICnB)|3O+Af?Q}ZNgCK~~Krzo0yF-pR0WRAR0h_s8?g2^Cm|}M2 zPeb~`#1`u+U@M!=UQa847$e}bjhtsL0@(K(ixS2qvOU*wF%LEba#(FKM655(LwqvB z;c#DFzLo}~(da9&LM%`03q#IjKH-|p=H8+TuGdzpwSTvWKbG&eTNeQ|&$wExzSOh} zT*>iO%qwCAYXhRhRlq^F+x?(1`+_A%5`hoC#E9cS_$k;eDVd^J!)wAr;fV&)YpreI z`+j5t@9@Fb#1NZ_<4HUX&CyFI=Y%^TpU>Yam&RFHNV@a4;B*>-GAlTCMh` z*XwoV_kY%4pzDzY&`W<=q5vW!f+}1V*w;6O8v=qDGMp0_W&S2W_+EG?d=w5vKGoG~ zE^~muKsXdg2+*({+VngU{!%% Xm-#`Ufi#vZ00000NkvXXu0mjfbX7zg diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed-hover.png index c2b1772af4105de5da1bbdbaf0a098537cc0f229..a2c3c03d9e67a391e5f106d5d808b13c322df781 100644 GIT binary patch delta 244 zcmV1)~Q-4xY$`ThBH@b4=$~vGSw}D~x5t~|^2JrFmF$xI@S&E5?X+3!G zU@g#)!$1R`01f(sY!FTZczAdi*xA_`{{8#MFCZY0q@bW+eDUJNB|wAD0u6yj9YF&? zwgC-c0J=n9OG}HBm6i1pQ0N!T6*vvx<>h5yV`F0g+Q`7k$!+-;Xz6M$F0KW+xw&x7 zAVY8(z|GCg0P+SiGxKer3%c|2@@~UyMW%5YAR;32lZlCG0nq=eLqkLVB5OingTezF uo;!E06gF{mF-DL8B>-niB-YS@Hvj+1)~Q-4rU$PyP9H@b4=$~vGSw}D~x5t~|^2JrIoG71X|TLQ7x@x3 u;kk3?N?{X67h?noPy%qKL}Cpccmn`JO>7i2rJ5B000000n{NkB_)MWdT-AoUXq$yzB=H>7Y^rg&bjaNyU%&gQIBog zYLlUDQk>db0FeWpd7S6Ai2&wXwhNBXc|nK(ve~TW^Z87O?|%W}G7VW5LE zSqcyrGnvf&WHNcuYPF6AgMki+C;)l@`RN8C0$>eLj1WlvS57(tkej7aX}8zwJ#RLf zFRlhNBArYo zKOl61o<->zVgNdTW2pYD0Yc$K%(e(FyhE`bG#ZUlSLOMH2!J)fQ^3Ykz+jKdX$Cc& zPQRfJ4k7#s{ZjvQ#5WHv0s#_*$B zt@eiV(rFL@z-pWO%!aU0V8<9ctW+xBrNYj75kM>!v$)@|xDUGzs@3XyC()|C2mn9B z)^Ip{42UP?a+$|$?eHvFtZNIoB@mwiw?wOsZvnOrY#rFR1HS;q1gi4ZwcZT?0000< KMNUMnLSTYRpc`cX delta 624 zcmV-$0+0QY1(F4jEq}d>8c`I*M;*T>4q`-wjU=!;2GayYk${DWU0R!f5J(}UTZ@2& zU>n#X?bb;d0$Fxjo21iX6Y>`%5}PEXNScj0IcL}*MrL5H3N}7?%=q4$dw%cS`)2f+ zrm5_*`FAN!?LC0V0pE4oX4*sm*C{&!M|4{dB7jn<&R-)rYZIERLZ=Kpdb4)}asqzySg%5cX#> znMSQvdj^<82;W4f8@d!i1i;{^0Sh7;jVg&mB3&#NpVsU37Vd9g`VJs&qpMy)JTJ5Z z5`Y~d)^)L1_`!6V7Sg#EuDe%kN%FR*)0|8$%a zxs14SZ>NAej>qFlDwVR{3)t~Hh(GT2dgorn8Cv83i;%)Na*C#DRvpae^Y0LU(d~BM zudsUhDQNq?~Lve@UbSY009d{}%6HkbTA!X9Anz}|tKJMb5!9*hRgu}u#E0000< KMNUMnLSTXx%_v9! diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed.png index 4b156366001790df2c08df6f1ed1d88718d4f227..9a22f7baac72750e55044c817206636cb8ba1a0d 100644 GIT binary patch delta 293 zcmV+=0owkz0>1)~BYyz~Nklk0}ADn5AdKm};X zVW0t5fCl|UHVCHy%*@OTtgNgcLwGbbHT^|JMK$l-xw8Ri(0@*#A^%{8;4}cJkO5=} z2rw}*G3e;%s99TE2M7oV>;($_0!si1oCbh2gAC!|;9!uHl>7^{b@k7mKNr;3*TXe~ z48d!FgoFgh3%7s$`qd2tw_&y-(>M)KR#yHAw0*&?TenuPTD9sgvL+NZ&}j_Va9LTI r6gF{mF-DL8B_050N+j0Mfj0mE$60T96kKQ}00000NkvXXu0mjfBHwsi delta 285 zcmV+&0pk9@0=EK?BYyz?Nklla?pW@XI z=qEbcrE5W_t{pqM9z6C7E11m&hJ`DFhXw|hap%n-;<~QUn2^Ra^IjoKjN*^>4z4U#5Ygrbdl%4^(!MA#J-4z0wrXgL|m!@g@MSoGu0l5Nj2Z*&HAt1{# zQWS+FQ$onvwr!H<`4ONKAkPL70+J*lxcpAOO&ErYD2g_QVO*fO90YKLH@mJ|!PEm| zY@Z~_rc2J2 j3u8j~Ck{pb4;H)t;HhtfBaPr<00000NkvXXu0mjf7JPqw diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-closed@2x.png index f9092e7910fe591a3a5de041c062dfa9eebcc8d4..32cbcf306f2532ec39e20e120d713a5b6cee3417 100644 GIT binary patch delta 622 zcmV-!0+Ica1&0NYB!5mxL_t(o3GJ56s}f-p#$U&;M)Tuh4yg!ImWp}>%P1&XwFrrJ zEo#$$;!aHOqHXJ1x4Nw&2%)B#m=A7UA zyk}m(an{}eh!pUg*d~`v1c1Um_)T!cHU%L9$baYahS%%eJAo(xMD)HB zv?xKPQZeiG`lDnrc^=6w!fyj2;gs0zJrMwt1_A-ASS&s)m&>=H2AARnAok&09ve(qJ%nJsOQz*aF07Y;4~o6k-K1YpS4v!JvvpqkB-}L9JH1W11$_ zxC)3X@Ss}=v3~+cuIN6WPZ5TmN~QLp#)aW<_y&rw_Bq6Sff`5u0}(5r0rZ(5RO2kH zP$=AQG#d9XjH7T3G1uW|{skdcz%UF&rx8M**&BqhEbDS463L=3-uC6DWHc3W^gP6U8i&#=Y{eguGncDwy)J)!hk1c2lV z6n>5+c+qOLj-|z%;yo^#)#=@h+&k4*unB@4z)pdk0$W$$4-N~EiWZ0CEC2ui07*qo IM6N<$f*=Sl?*IS* delta 633 zcmV-<0*3vE1(F4jB!5{+L_t(o3GJ58YZ^fm$DixZm1qssMbs(;ErJq-N=qQ6ryh!w z+5RVU1r|A z?`OX=JFaUwozgO!u}pE+UIB<4a9y|iTs9E^gguCDa6Gs#2!9bkHk&ovZuj~%L>?fb z`A*QH0>xs{ESJl>u~;mQVp+rsKsIfTt#p$3uR{d_Eto zq2+S9ol2$h8n+STm5}oc5m0lSt_wN9;vC%6Jv(9;2H_2P91e#cVPBlJ+wG4?8D1sM z`4I!K2*+$wFzg180l>st@~T>`#xS;z+~2eLv-e^EHGlag1ov|v_5)!*&|omICzHt+ zY}89N$LT5tP?oEw=A!&ffv2WqGI^z8+xAzCAE5no5}oZ^7(f+a+f%6&Z$cW4Mt>lF z*lM+o0j2e&^Z#P1sv1{zAR3Jl&H=(1z?Of4<$KL$^GH{mWyB8fI^#H|OeRCYV33Bx z;l+47esb4nG(Kx^-%1e!Se8ZUbednkX}{k;g6(Yi-&>V6J468ZCmh3Se(G~LaZs<< zziH~sO%nm2$^^piaq@kr)oS+Ks)7NR=4KU?l%+2G7RY4gXRiQO4y+tlx&!|J@KlPo Tk@~&G00000NkvXXu0mjf$K5RJ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open-hover.png index cfcb4812196b8e41b6f55f3deaed86d856309b2d..55d88a80221f61af9a2cf51ca2af780e39f4f6f7 100644 GIT binary patch delta 231 zcmVWV&m_}~ju1foIe1O)|mH#If2A&X z3}Rz-0Z3xXlqsHHzI-YB^XCr(Gcz;D(8;;Ex$8i3$N*df$Qog6oCW~>{GXqnzh6{T^ba>T_k_^U(7!NEC^RT=@ZnXfRvF`y#~B7Fw&3C5 h^a36|C{hD&00948bOhvWj=cZ?002ovPDHLkV1ig?XIKCL delta 234 zcmV))%G-M1#}`2?_1)>gsAk7RPV_ND`=UHqa|}U%q_d2hw28TwGk=1O)}B zBWnh+QQZiYEh{VgEFd5-lb@fTft#C~ftQz;0qFF(K$pEI$^eiepyFN~Vq#)ffi^P$ z>3fQbiW|{2gZNlo0FnUu`9IJF{o>-{e}Jx;5E>f#7bK1WK!Jk~FI~FS7@s`WSirCa kj{r_D;L(F3HSh)i0Qh%sG$-f}od5s;07*qoM6N<$g7I)=q5uE@ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/fold-open-hover@2x.png index b3da623b7cd5926d052f972be6d6d71b9cf0ccfe..71cdf1d2d4aacd574ac28e9c4c426612f22f1bc7 100644 GIT binary patch delta 566 zcmV-60?GZt1kVJJB!3o3L_t(o3GGx(O9D|8edG6tqT&ZG`VT>ipjEqmK)5u87UDvP zc5cKFT4xl}Dhgb*(4c+(2bT|?!b#-a_WvQ+RVW4-n;MKbM8C$zNf5K zEAgM7>p#hf+eyGtz)`@%xrU0OB;coVeAoEQ@YeL#*mV=P1b-uVZ}MVM%+QLQ>?{S! z<#KH_8Xb(sW8re~a$YW%A`}XVL?ZD91<#0O?IKncM@0pMbevDwWbeer-ig zIEqDLKR9Z&+R0+EI2{g$0-!(kjRwMuL?XiP_qQvR%B^wFJZC9DLFsh*3FJrf`Fv+K zn^~5E6!Q6eB7YnX52n-Uy_wLw<`ooCg+gIYa>nCvoxtn$igiDclaJf&zN=QNpJqby znq2}JSg+SFpupi|GSN-4DP?b{sw(hz&++|Mdx^&`feZ))0u9u5AB3u=Y2W=ob*J7y z?KSNE+ISL+Ms^8gP_bC-@NRU>mb&PpBlgTus&CY21 z8k)hO!Y>PKKC_*>-dX`Jk(J+8JzxcD$L=KHDBvjY1u2!g)kylT9RL6T07*qoM6N<$ Ef&vv7hX4Qo delta 553 zcmV+^0@nS{1i}Q6B!3A>L_t(o3GGz7Y63wNo%OlvTYQkxZ%7gFH_}<8NugyxtZbAt zmO&E4DkP1hF^~_iNS{XX0U?k|6vZMC;<`Ck92sPGGrJZxyD-e`oqO*&_s*HoEX!iY z`~t@WCv2a9-vPe^A(d;WX<8nBhKjGm=Lc`iL}K5VRF7Z*Z+|Z@7RAi%z{#%EfqK1u zJ)h6Z%jJ@x8&3Ild-TF45($>i=id=?g@0)QN(9#H^=+Y0IK><_O;edfu~;<0sA~sK zY&9EVyGrFqsngb|ssUCim7Zd;*jFl* z+7JkZLddDe^?N69+EXS_tyX_=mfc(~N0r$QB879O-2GOo^-T+>PniHE zLBcn*|23PIng%v8j!Bw+(2e!KCL#+P_PbA@q041o^YGbs0M53b?51kkTTuFE$0oNiF zgCWKqNR%NuovtWm>2oW_?5~wy!u>%0KNt+o0e(`cRC?UzWy#p|7B!k9s)UV0~oQzLSbQH z$&)8fyis%mIY7;O`uqD^k;SnFA{!gqOi@wMPauWN%*+fRmxzdneCOccn1-ww#Kvks zXJ_YUK|#S8+}zw?&Ft*#3<3fIbKBe7-xFm3NKso`+YX>Bu7kWNCMF#A;O^bK8_+d_ u_*h*4k^pM_4|H;`l$6w8piBCZ?IaXcN^VF;#RDDy0000g{N z&d$!y{QUegI5|1N1^^9Y0P^Ryx3|A1$^eiepdmZN#Kf+Hya=@Y0T38$K-Uc7V|4*Y q0;usn(8;~h($ar;7` zggPOAfS<#Vu~@7^3hd&F(%Z7#@@M|fJu3j9R;%?z*AmUfmGO8I8z}&w$z*azZFh$U wbu*Vds3+zIe{JkWC0L6t5a1K=74Q}K13X5maA5K@VgLXD07*qoM6N<$f;R&KRsaA1 delta 567 zcmV-70?7Tx1kePKB!3r4L_t(o3GG$QN&-<7zN4cl{{J?j=sN^0dV==7NIpO!xbhDM z2_!dBi=a(W5744bt2RO{TtoyxlGsKnah#dXnNX%-?sy#oH*?`Scg{WYo$r34GI4Z+*98nbC!1t_BC=dW7JkMKB^vUINNaCtquRmcR+F&ApACE?(bHCsJ z8i_;*o)8B}`+sL@M)(N?0wfd)4THhp@ytehG85=@IuZnK34)->i@}WpZAdPY$&l%E zdQ>i#zl=V{cV+_kVXaoXhak7V z)$p*`W}Sh#Po+{a_*fR6p2*S!8jZ#~biyfgg{FcY41cAE6kLMOv)*pCUYY=c`Fy?$ zm@m<2bdHUM7Y>JqU_Y4~85Xt(KyghaNfP12>Z};?^pnYisqpl?Y!N_!YPH&hLcD>T zYd4<9iB z?)7@ta4_xu_9WGdzOk|bbZE96+eOF*zu3zm;3(iI@B?jzs#OYK_CNpt002ovPDHLk FV1hlx1$qDg diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..8934679ddf00be131d3171917c28043aef94a772 GIT binary patch literal 250 zcmVbX|ug?>$&+f$Bt>=eeLFfVWQKUwhsK zK4D$gJ>K53j^l{3G#dm=(*#n=7C&gM3mpRDyJ>huwGz;bsDyxMZUt~%>VT8!2EKFM zpLC#Z6EV37Z&%eE3;L_WGH+bDQ2#>Z{4*&oF07*qoM6N<$f?cL* A(EtDd literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-hover@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..a5e4694b31e3602c727eed5b242279408d66cf71 GIT binary patch literal 403 zcmV;E0c`$>P)4vE&j)tiw(YODzf<6m0!*Oy*OMrX55u5F6-TX$bU1xqPmJJ#Q^b2YuNxf)!a%X+Boq_mX5c literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed-selected.png new file mode 100644 index 0000000000000000000000000000000000000000..8089d037517b053bc96122f177e8c35f4e702e54 GIT binary patch literal 266 zcmV+l0rmcgP)DKCVs{R6Xb@G zV8H}ag4GlJonSP^oFM9uh1HES!Xu~9L)u~6gzT^34Z#7j37hyY)Uv|I3h(wy3io2Z4we~IK27#{Oz{dEAWZRmiCm9dX*RR;`>5Nn&C;GVp zd~Q>%eOH>#CA$#vk8MTFIiOP*Ci1yB6?c)XeRgf|=C>jiSt=D#X*kdG*2q1jy@zoe zgV<7bR&9r>FoK3f*S2@pIdoQ$uIWj<+|T6kt#;gquf-fDEfA0|#jhLrR|<&uMLZKv zW#?G3@%7-D<^ELfM{E*f=t<0)p;Q)=##BFh`VuHP5b0xvnf@E87Jj18+Nr`}SKj)VX;RU;fIQ5eAiqPK!}PBbRm z%1AFDP|^qpn5mjPIB=+e0|LRng5W6#?f@yEXh$9P zcl4Y1K}@G}CLWR&Qbu=$jYdDfUl6wxMlUzAac66OAEFh*3Hrl@?i_2)N}V`B9dJ)$ wxY`41I0#uUiD8;j#*W;7ftwlmCHPnH13@T$fgS3x$^ZZW07*qoM6N<$f?qM_8UO$Q literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed.png new file mode 100644 index 0000000000000000000000000000000000000000..4d2dfcfacea39983639f8f53e6cf445215483671 GIT binary patch literal 250 zcmVs-(~^DgiT>$>ix z`jvGYN1@f(pvo8n^E_YCgZIASp+OlO4X?>|faLwhpiEvmcZy?({u1SD2Qonf`IUvx z;Bg1mT9~Fu7VF{=LKCkqv5DRQ-*Hq)$ literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-closed@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..717d21766be7f760d0b67fe69fd0d9f324cca800 GIT binary patch literal 389 zcmV;00eb$4P)jW)@K|%c9O~#hJ;GyYd0h zT0eYqup8!Hq50Ig>oLHcw{80@%Tk$lYcr^sX_|I+%W3rzW6CQ*UDwJ7peXVYr?;n| z@B0M_s;W{LT@1|gtk}2fxp~@$VX!Hr!)M$vz=TY|cdlb#jN3W-92tcH6D(NuM}oS1 z4PfV~$tR{OYl2NA=(_F+&2ttQ@>5Fl=wv_upyE;azW^|fqn8T{06?JUZ1`q?1z!^Y zsA$)*^DRuZ$6^WqI_8!D$2AmF0Nf0K0rpgVQ~Y8vcM^p3N=dl&iG8vK0E-Zab2h{a zZAt);s$T#I#k2qbX$b8%%p^;22?hw_o1OaB#A1pBP1Brv8R5dft(Ch5R@bqUB>5w9 j&i=u8`XA8^;AY?r)fafIy=+@a00000NkvXXu0mjfbZx9! literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..702caa4ff23ddac5f25f10b3f5c55f5aba7d6af8 GIT binary patch literal 332 zcmV-S0ki&zP)%vkxS5zRRd`4WRrhq=_Qbeiq8Sc_W{sk~k?|>Ga!v)FWq}A=s z?F_pEs9_jr%d)nR!F6;2rPM(`b9e9h*_6)$AQwgP<~S47wk|(lotM)jJ@FbzLX*WZU)_0_b;w%>rQpU7g@qPcB>m~sLj?apulBXYM`T=$_|kmmJ0=$-#XUU`CZd*Y&5UJ*<;=KH~P1?p?eDLI&8) z2ZO=8X_}g01U2qJ1T0m$R_ z8IK?U2SJ)dJp++FlVS)0s@1Are;IJnHI!hrS}DT?0qgbpaQnFWDp4r_8H5PcfI}Z? zTB^Vk?f+3}K6xy3nIC@P%zqHY#fCD+qcgl$i0UI3P5(Hpju~>Aev(3@n zSOV6+y54fMx%TB_SnFM6E$Yxm?EA^P$yheaqr}0oWXm z$5Uz^HP>~I_5!kJ+cuiotkGx$jK>QJeC+Aa3%S)n-2jkw;4CmmfJ-G6jK|{!1T>q? zId#Hrp-?zl05B-^y>`1T7>^eqwBl?w+auM_l}hCX21+wvWGdyH`iszV%|Id5x&rC)rrFxr$+(b-TjSYklyU2IAuD%&|xLoNqNP5YWykW`)$7NYI#4DojQc7uI&@(sq2m{(l#~6V3!=V2MgMNLGhI8(rvCuT_2H%tV zVoN?HZl(Fa&|E}Obf@e3MGJXtD~{uP%d#$EFq4ksjI%7uaf!VyATDX(zReRxp65L! zNus)dxDbwjeZIG$VHj6I5X=$kH-uq0H%;>z;vpc(U<=7IP1C$63e&djQ`Bq2b=_Ov z_a`7s=>mz!4@pOU4x9m$x5W(2N8kbCS0=ClJ*WfV2%z8E7Vl`j0B<~rkWa literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open-selected@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..045460eb4d80b917b243c6abca796fffb8eef0d4 GIT binary patch literal 821 zcmV-51Iqk~P)luQmz9s1~EI|6hgu*an>BpSU5FO0VjppiA8Lh=G2|fARC)D41-AZ#B(q35AfEoy`zyTtF?TO+|0nbEX*@8_k zkI%k00@lO|BH$w&{j;v>-;fdy>DPWR7<{bprf^MIME3RauFw}aFD@|xZPIB?t2!@B zi31k|7JnCz=AU8i-=5yY7B;aB4-Op5nKj3W^;WC(EEEbo(8S86*XzBCL?RDGG!(2T z7==QiJRA-OIQI%fG7C>dRG95T#_Xmy1{LUke=wOY+grBb(JvDi|-2ix1*jZ7x< zR2-iK+VZC&?Y1E)z^iXUJ)6xw9}EV=Wu-#4%v>&4#kS1>7b$Rs2ymsMErN|kBN&gz zqlrW!>aJMeVQXvaW2I7gArqg4x^SS)x%t=sHST|a^ExKn4Tr<`m5xWzXq5ZG+|q0| z`^jYTNvG3!FC#m`58+S_`0kK~t>9kQ7WTT`?ryPIe68n&q*N-s5ji>E6R^!$<0FT_ zdn4k2xGvlg?g_U9z6WC;Ea|2R1=bP12p?PQ5uB&*L0`K^H&I3%pdjfC(m`QDj_x!< zo+0G6Ov};MW4V{!5}yN(@+?Q|TX2?yqg=lM#MvQGk)y)J00000NkvXXu0mjff`NSz literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open.png new file mode 100644 index 0000000000000000000000000000000000000000..bf450fea77f88c9dcc113afcdd118c57b88770aa GIT binary patch literal 334 zcmV-U0kQsxP)%m1KZ%j0XJDk(oGRk&oZV<7qS9l@IUX`JRy5ThCu=$ zz0W;JSGJtzS=bE6MhvZ|1EiEkJ*Lxr=&@<901#(c_U^jw(l?Hypxl*a50&genx=CU zMZ$5`TTRm>0O2}Fecy|!sy0;p1WPkSJQ&8YUgMksBuOGXPla6Htiv#DVgxwO6U`98 z9iyeIozU-*aW8dUKbQ#8{C3G9EM>B4a1#0&%ep6Hx%Lg ze-l0Eg+>9e@qNDxg5U>YG?^x9)c4yo%`1zss_&pEiUYmE*Z%3a8Gx?q9?P=)Px#07*qoM6N<$g4{}w-T(jq literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/folder-open@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..27872ffa4d05496d5e3a068e68e4b5a3a596127a GIT binary patch literal 606 zcmV-k0-^nhP)@sI&-h*qPWw09&b4($d4^_(I@jvyoHAu_gdsAONw6T%xc@ zImcsD^w=i9ng9;nA#e$hHD;VFJI`wZn$2dMe}vek{W}GyaD+a6{QfG23pH;YD)gTE zRi0x(_8;2Zhs44y^Cc%vJl43p(P;F#TCG~^^;$dgMTtR5tJSJ%G#Yclyp^~f8K3L* z`VE~~AC9)wFTpWpOyUT^mA1RRUhkU>Q&Jb(AfU(&P+)@tT$%t(kh(G5`wLvZs9vZp zq5}(<0vHFFzy^m&K;khP33|m1ESl0qJP^nau z@B8z9zn>^PDJ1dO(MIoHo2%y5dquqZp sX4cmzAj|m_^NAWb1wkI>MZhHjzZE)d|BF6JApigX07*qoM6N<$f*>{%Hvj+t literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-hover.png index acc7e6242fc3c462775f23923100e14a622bc2ad..8daa831ae54c6ad076e811ef1a81dbda7923683a 100644 GIT binary patch delta 249 zcmVfU}Ixr02u-TtgNgc!+;L?x8T>WUv+IhK0Z&u5>NuG z0U*tsoSY0GUjS|20St?BH#fIS$Qog6tOf`O2>j#W;kgL3wZz88W)Dm;RvKsn$QBd~ zjJ^<{7!$fUHa^x^z-ki?L98hehgz({18)ETr6`gvlDQMM00000NkvXXu0mjfr3-2y delta 250 zcmV+AcOqnv}D;E%e6#o17kKy<4-~aF5zkhK3`t_^s+_|#@Xvlq-Ay~b@#>U3L&dv@~ z!vJ&*qmhx3gNccWJ}_+d!zF+WtOl^MvNC`S0Ra#jXuv<9cNPF`uJdm3@p%fCfD%{@ z0BPpr%mD8_^?j*X8s7O>icLlA39#Gw|e@W2}Y0A$IJMj&az1ONa407*qoM6N<$f}@vt A2><{9 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed-hover@2x.png index 0a20c3f17776965ad9371a7fc78860b855783036..9cd434e8276427b5aa910826f666b973b6fbf0a7 100644 GIT binary patch delta 686 zcmV;f0#W_J1;z!CB!7)bL_t(o3GJ3ms1i{W$7lQ+9hE@BU2C_7%M>(l;{)kIOINL3 zSDRXd1QH37t5$84ph6^gAlRZ8Nr9LYZ6u5#2uko7VKzE5_CJHZ=K1`%=EU20;c)Mr zxpV%%bIzSR!%rp??kt<-EX7HC2f&;Gp65OPqg>1Y5Z?nt9DgR53BnBE^Z7&wpNCxm zL>R`K0m2NBPN%b}RBF%dc2fo}z^;Pf;p76gWCjk0LzHE?+iJC*R4SF%s;bf< zBE#Op&!>NZVFt)%vjeBoDN^Pk%=P>I&-Hq}RIAlKPK5{P-vH$MCLqiJxm->Muv)F0 zrfIqqF)m#Qte!3aL9xiVJ zL>M5ENTlNN_>RqHV+2maFN5Lix)8Ex9Y`jVU8B*c^m@JTwOZ|Qu~>XP8jV;)dhqwy z=hJaugaJ~iRM%`aD~vpZrB0{wxl*a*%H{IM1K}>7ZvfI-1wXW5O8DNW8+lp}OENAHo3HY<32~Yajqk3xfRU)$8@$d_MoI-ERNJ@Cd$#Id3_4 zkr@_sF6!gS=?2665<<|Cq-L+zd&%SRTtK2e&E<0Cet*BeXuDb8{BEicVzEfb3u!PIFfvOE*0BJzMUbJV zQ4~c=y$Xnb!!P8P)9JkL_xqP7;Yt7uu{H-NZZer9yWK7k3Mc*vzYD(;i^YB{i_~;% zC4lKQaDOq|6ebV^M@Y5@=)M_=M80xd)Pw<;gmh%O8fu=L_YC1(g!_6Z6#5`4s+}hc zfRj!f4u^VbRzL_>@w^%c1fFX*T(vC>;C8!*2dCac$Zz<3zK0NF#aqjS0dVL0IPq;n z;vU3*t);42EDV4udnOnR@

z6Mh$LR8_N?@FG7V&6yNaS*C4WicO(v65hzx-6??kjj z4vGX#(@u(_sD3HqIKFip=McFq(C4-MoHtVdCaS7B%=5gelVw>RMbV9Eng^)avA@mB@N~J1NXTAjp%va zi(wddzVAC7*+&Aj|TRuItzE zFTfD&g8^v$0qSFQF)WAf#SW0b2Z%w6nxBQiZ&^ctvJlz8K^Lb$V?nWH))H7&+?89r d%3U=}W8eRh)ymXp+T8#E002ovPDHLkV1mR~wut}$ delta 388 zcmV-~0ek+M1D^wsB!6Z}L_t(I5v7v9O2beX#hY3XDXtwH%wPtGt{tR{ATCm0Krqjc zH8>k|Z;MMR=5MG%r8Nao<8=^z11{&~LGTbf*pfqrmulY7tk<>t#3@;q;B zQq4`ml;r@zZ?)=zO90Bsvb^Uw&LwVlp?^Uo0NyG@QPk;nyMH%f7(Q5*bp}Wq+FH9K zY9bR%t$x3Mnx<*KP!-Em#IL=o!pU-38_s6=fAHuWE z!ZiN*jSCn7i=tkLqIjVw$_XY0cz%H=r9}>atv9kPD}wBjBuO@zOkRh>;kE0!pO{}B z9u7bu2B4kTwtfAsY1-X%I(@>@GZd1C)mnh(d9%@I^k|yqJ~4{U zOJDM3hS_=V`{vD?8Mtn@dup7<0IKUQ(L~gd*?cH21H*d4qE@A8pU?e^PVulFf35?Neayo$(^0LutvPM@)WN-ZK# zkzSdbo4eQPbh?8e2*Q4$P+0N#eAAe32S^N^9!@E4|CpW(P?7TePXG2%1CvgtbAEk& z{Y5sLy^mTs2XYHw#y8cIrb_?<4Lm9RVoWB}g_)U|2Y;DN=K0pv)?K)<`k)h|Xd@NC z#F)+IX}8<`uv)D?iO1u&z)UAakxMl_{gNs8QLWW#St^x^4SsSxDES|Y0NfkRwY9a! z%gf7;K>7}#)hn)Gqyp;oy0E&snqOL4dWn&uQCe77c(uK~y$cc|{|J2Q^=oM90c)d%h8G};qI?Vy z=)ltHbk*+A0Im8b@COhFk>lIJJ5POGme1#Z1cSl1u~;k*hc5sT z6DaN9fIXlEs1oD5Y`SWI=XnW_&t51L%7(+?9e-qmh$-c?VrnY@5$^*>`okjM)o@a@ z-ZMMpa5%1`s$HmZy5nh2**`~?{1BoNLMB<}zK002ovPDHLkV1k<2b8Y|t delta 798 zcmV+(1L6F(2DJu|B!B!#L_t(o3GG(DYg17e?Mt5RYlucs$&fBZp#%q)b_?R1p-XVg z?qL6e6gpTaBv6R7(12T)ECES~h=X)6Z4g9_86rkgnXGW*8(nxz7a*JOgsC8jvLE0)OVH^Xll=QMi4pT-9+m z9kSEuv~WT+8vTt%hA4`tbz3An#$faSC)^@)AP|_U)oNSGWb%R8Y`)HKzz87gtrh() zQUk-;Y}RZv8prH*`=fk5|6*-z%?WZ7pdcH=fD!#uSuX(+yehd|?ur_b$z;0aa5#d6 zLLn3k2B$FIDSxDwzhfdr77Uk4rAtRwS}c~kE|=>$G7?x=Sh$JtQDEdCBNQ%XYGhh1 zJg`V_OixeWuh;7>JyfgJ{Y)m~ce~x=m`|}3!_y;a`R)8Bl7XQ{qhaV=bhMACM{PFS z%*x8jt3)C(gIc)^as^;Ux}G#40dS;yQaZ)RvV3)7Vt?XcJRW};i^c9?nOYw_F`6bU zKy$UuM@3P_CnqN#@9yqC3x~sZFirQQbWKmEe@1#zE0@d4_V)HgtYbZB+J6y%V_RBY zUViHH`JRCF1HgMiGlISfC>D#=rKP2m*XwKiCvO>&GKzVqzs8ZM&4u~aIxH$OlBJ`@VExKv`2 zGwnZsJ)i;z{Wl10fx%#CVsC#fE-ohge*Y!}k$;#eXT)s310|pgFyba!A#2~&dNU86 z*)gltdK*=3N0sxA=bn<7QGWw|0{cMiB*d8Cwq62wQ(gmX2awQ`@GDRRxX-L`ZnN$_ zV_esI2_PXK75)JcBw@5_fi+J{3?FS15x^|4R@hPie@wepHpYdms{mmyXPC&S25RR5 c1{a=ze+>llD@<&~k^lez07*qoM6N<$f}X;0%m4rY diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed.png index a9b08c71a19feef2c6b9a2ef98b5af1b45bd2ae3..f85890a9b4bba90d17a1f7e10c2db1df38ce373d 100644 GIT binary patch delta 234 zcmVRwkyLaz4(14>r_3x1l!s-H$ zW;Ql95N71y;4t&`^)-6-?AhM^`}a!#U9=f!$V-?ZSPg(`hN)oyYGBdU)^;~AFwg?B zx5FiX46Fu#TmZr#05Xh|lk?BNfB&WfH8&L(7ry~ZKnbh{uw$^WfHX01adCl^tp#eX zEGQ@dg#iY@YJi}iAjp8@zkmNO&B@6*ilGZx0O%}`A5rkMY12Z1V&E7;mqX`cjYeb( k2(huIL_(U7pv?iE5n~ZfBwID^XBxKGiO%ZyLaz4(14>r_3x1l!s-H$ zW;Ql91|ZESEiG*(E-r5L?Af!u2M->U0J>;1(2$ofL$Dga!otD;!Y}|dj73XJ%iX}h zKnrNVc9=MbMyNs(1-Sr(K>*|uPEO80K<0EHZYnM=eghJR0b{HNfNTe9V&LN90;yXI zw6!uXFAo$37yzpQf`Wn|1CIau_pdZ3C+8@JE@S~vETH3Q)24+2#lSIyE{D#?8jZ*n k5MpCZiG(yE%MZK(0E1z59~ju*Y5)KL07*qoM6N<$f@^DGod5s; diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-closed@2x.png index 6fc24851ede9697297f0258eca1efc1101ec4a87..6d4e99534344e31021811f932fbf801c055c662f 100644 GIT binary patch delta 718 zcmV;<0x|vM1?B~iB!8_*L_t(o3GJ0lh*D7$$KUf4Kgfk56e^n7LbvUOHZFt+!llqw z#DxqJK_Iki)wW%WU|I~+BB)Iv_(ii2n#Pa}gn5WWNvKRS^8D(5UcPc1eVhSfHeNVB zpZDDJ|GodY?<&q_GvP9a?lQ$mdkesFKoms-!ty`b#R7n^6@PIX5K#no4U3mm%;WJG zY&IJ?a1C)65I0%X8A&4NHH=21UZ2l*AW70*rBW$QCX*>@%ZN`{|COP$HiZQci^aTf zLDK1T!f-g08jZ$_TCH|4uX=*(K0v;$RneGW1Ni-Z$zU)jrKZ!V(C_yT0kPX`Hjn1@ zd$^VWa;%|uZhwjm5DW$-qtPg!{{pp*AjaeItlRAttJUgWr_=d~>U6ArM--7WKBu*&eXwSeZWR;v=^1jN5U_(3k0dqs0vgAJgH zFSdZh$bTJrY8nv!fp|0+44&rmdDd&l22dSPTVOJo1iRg?xB}5r*!=_ykF(kAK%rrN zumMPzLKUV%3BpsH0nf4V9%eF`HdFIV8XJHda5x-_-L$zN{0_pobUOX-87u(&^Bp^#&Kt>O z^4CUG7mBd{j27}Y{#Emo+PK<^f?I&C16v1v0~c8o;55CXm;e9(07*qoM6N<$g6#QH Ar~m)} delta 716 zcmV;-0yF*Q1>^;gB!8<(L_t(o3GJ23i&AkE$FF+Lcdiu52P)D+x9x=x1Q!v6`~Cny zVT=p{pM5=V0QtF8WyJ&=z~k{q8jVIk`UPY_=ytnDfY`0oYRBjC z+qmWda{tK>gW+HOmu0|cGzw${0`YGEe4I|F_m!Z^XBGf`q|5*b(V*3u z&1U%?Cx7Dia5#M0>-An_G8xt}WCP6VPaq_YI?KZJ9xFaWUwDqWXQ@=GE7LH4*Z{K( zP&2YvEVSdP2TZWySJ-Gz5{bkQrsf4VHUO>EYPHInjG7PZ1J3%xWHR~rLc}#6SO9@Q z;M8Wb$;S}sK7u`p$K&tU)LQ7y0RYhlT|1uVfQLEj^U;%VGofDkzTi9T| yji}BAVf`7+`EUGoWtZBx+Jb;vfNci08Tbo_3>Zwg!?1V&0000XktHmD=FFKR^WnpXWiSQkw4k71IIB`3k-{Lpz}RhSy?NvX$Fgd0tg>IeE6^u rK6#vhh|@+i37lborVb}};0*u(y-R!sTWlcktHmDX3m@`^WnpXW!S<7XgGtQpkO#mGl*uyqL6Xw)TtN$ z{{5@V#DwtSzkmN2SXfxDWMyUP0rkN{9>WD7Nq&C*>>odV90F;A0gz!F92{B5nn7|1 zD_|;tbWBXlVW4fhftZ1vogHK|11l@*9*{VaGB6XH3&0X0A|iR;zJ0rC0`v+q5d7uh z;>tzW4B}&T0Z78f$L9vn=A}T*3_#~E1@S@R=m4h+KnjI~gvxSi6951J diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-hover@2x.png index 07234e5e23742bde27c1d1638ec6f32dbf39a0cd..d3298f4e5ddf91df15f9366a345fd7f75568071f 100644 GIT binary patch delta 598 zcmV-c0;&D<1n&fpB!4zZL_t(o3GG!qYbrq$-Ce&w8>?+BgjDHXCpHUK!A|@EA|Z7e zLq5eOy#B#^jfD@p1g!D{LJF$~uS}UDDNGTIV2t2r&It}=$&Bl+g^dHl?wq-E&%I~v z%;@v^T-#(bHc3wIZ2%ir!0%K8x~|hE>SX6I|Bhmx$ZQ+~z<;73`X0JhwPsj*gYGSW zdc8hsHk)sQ!N7zQ*DV^2K36K0k!qo%0+~!ERIAk*y)a;Vq8%^R@?Y9k+5)k% z-EQ9wheK;T9#dKY+rCr?d#;7U;k#0)WZ5Pq1GyK8At7XTgngZh?EQYf_O%O~HVCk) zaASjA1yC#&{~?I0SSnZN= zbpVAz;Q?NL$K!FE3e27u6#BEU`9>xKTn9iWD6&f&u~%jt)(dl&l6VK&xKk(tx>#D8v7A)~d!YXQjPo!&d`dwE6`dF)Zu7R~! kzbxJcuvK8Iz{VB$09HefWpdMaq5uE@07*qoM6N<$f)uG3Jpcdz delta 603 zcmV-h0;K)#1oQ-uB!4?eL_t(o3GGxnYa&q?z2p0d7B*>x{Sh{jW{U-PL2Y6u67mnS z#kv8Tu>C^Ph-k4O3s$?`Hj9NnAVpH9h(c1BVnAISeC##Pt)4#U5(4nqigb$Urum4cF`S=6F0l;6DQ+v8wgrT*hWHnfpj2 z@(#|e3}`ApqE@SQyjU!nlgUK*d}@jYDg|iy zC<9=HZnyh)Hk%z{9F|((NXY+0EhnAh69ZDmRMKIUT$z*a(9gu}>MjvAkdn)tn zR#yzLP_NhP8isK+pU*c0N#TV;ApzC@ESJkic8#Ua6@LRv!VT;3c-)Y;C*3sp4&@v8 zRKLguPOEDGrBdk@ATIccnMc7r&(QpmF;0!8-!%XxLWJiKnQp%Mv+#5PIhVy%R!;zm z#o{B>{taS#Vu82ko@a$ZVXV?v#&`n2e}yU13$<9>AV~Jp(&u;6Jzvw4%`{e+vKr002ovPDHLkV1m8O8m9mN diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open-selected.png index 71446e4061a27b594aa4c421fb72a8c743fed621..70c63f45e8ec8942eb5798040074c37bcb1e72d1 100644 GIT binary patch delta 356 zcmV-q0h|8(0{;S#B!5OpL_t(I5v5YQO2j}E&8*^P6(4_KAwv9uAPA;PBc$;k`~r*Z z0#@n$iqxqr45SMwEmJ6jh^D!pnop~sH%FMB*{n9G(tuk z$8XT+{Q!9eO;uG3$8nzf!L-JS6x+6+V9tRh@Kdr7>)_F;HdBE z?%$FS>;M;*Wjz)}!OOA~S(Ysg!*IZ#(|)U^@1KAbsVIjK-gurD=Xow%*L^{60tbEb zPyo&5OxN{?G+j*#P1EM+Pl3t50@kP4hvCXJO)_5sGucc%TZ^&(yYQeG7#ip?@ChuS zUHxmUyV@E+3S`h#D2TkAIJpJ`Wbvk6en^`%gWNX|!$kvO@?&WL0000z zA5j#2f}@QYY@vB>+xBZ!Ra{0{mfW(e7xd47`PvEEDGoun4T2yck$~rUAE@5|`$F9C z<)k&(1nJZ?&F4JN>!K)lnx=ocu0NrE0?cGh=N*U!NPEDw>$>k*mT|{%-jEx>uHc9` zWI*@hSktt}Bw9&+RaJdJegt$*CzG`XO#`PvaA_C@i7$YKI8~0UC8OV6xKK33L5pw1 zA8?y*Ei(rVh}4=eY)MIZxHhen&qH!IL-X5F?2Q!b2UR zzrjnFB7}rG2oYWy3_M8}5k$|EI;`*@1;IeJF>7jW%$P1+&b1%kKF{=xuV260zT2T) zeBi_T>v^B|d7kh4K3lV4L*TZ3(;tF$!4<+VB_+=CE}H>1 zh=}K#5F_b$QTz09U{P zrGlrotNPVGfjB5$WZ@?)&(6+%_4$0?lmTNqF)NEe*x12Nq+vUV5F zLQ5rEYRysxFs4KzlE{9<#gE0VkrPXMd%`LAN~V&I)S|;@06* zD@m*1ruSK5WlYb7X*K8Wwzjr|?d|Oi*=*KU72IyOF+4n+4242TB9gu{g@B})xB$Y~iZ*6ToiLd=QD2Y|_LMas!IKX&ja&q#yt>_qyjg39RhwTKs7+-E| zY`hnb$Jg@tynnG1qS5HQ%jN3C#|7A@HfJ8IwP6m!Tr3vL_4oI`GaKs6ySuyJ2%sCM(h^{&*{!WD z!_4yX@_%2S&-WCa+i(i%t$eEYRV#oKRfROuU$8JUGc!9fGBT^2sBvIm;KSDkK4N@{a+(-3IUgvaA~!rDzZ4t3Rh%DPqsaHgst zJCdC(Q0~y+;NV9Uq_3~#&$iCc}Y1;8i3Nk*dh<<el9#TKGodakxpednA4Veo zlQ)18wp4Z#sCfnc0ibH~51?u5&Hw-a07*qo IM6N<$f+u*FcmMzZ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/group-open.png index 60e87355909cc289ee043c65c483f49591d40a23..96a2091386d61b46d666f05d954ced5c6f69eaa6 100644 GIT binary patch delta 246 zcmVcktHmD%FD~uKYaKw1Ev5*gTji1g@r*#NXQ)~2BI+x;N;|- z`|sbs@?XDxDFR&sR|_(Lot^z?b93`9xFC>$5eD`3^?!JHc=9YIH@PD9q zHnL_An-Nzu0u4I#`}c2CCMG5Zpo>7ZuLg>Rp&NkV0+<3mKE7;jZtnkSAb$htU%b4$ z#V|Q!8m9rp#l@$Anzt}BGlRS_t*EHzF|tM&8zT^55+EArih`d%f0_fCjmY9CY)}~B w!_%fsQ^F^YGZ0a1!Nb8B26*(KNDaIJ05=eD8KM}G2LJ#707*qoM6N<$g8S-f761SM delta 224 zcmV<603ZLk0=EK?BLV>aktHmDN=i!9KYsi;16$ZIGcz*?3JSWzG=pdi19*6N=KlNl zul(oFpNc@&z|{i97&tgMj<&bA?}7^g85m(uUtj+RXh=RgJ39jl3kw``adG|U=H|{u z)(m1ZVsk1;qPn{J)URK^OhK*ysR0_ex}~Kh3?zpPFkAqWv2nZ{{AMMcGFpsia#1_0@4Ko>nm)(B%`1R_iVL<4Ou00x{nF!URd#ZlNG|KXDi a0T~pc0BvWe+LxjL0000uf1i}B&KWNb}5d&T5A~H2!NDu`< z3nOS(yC7P$t6c;w+SD?HHZ39$icAn^7&|8zFq`pSAK=Cdmpjfq_dDl1_q*3o2ZMpK z$mUoiIk}et7VdycR0FE2x`FYFaDw@76uV)C5emSf82YN!Vt>hZI&c?uSfPMgt@hDu zHno1gAGHfI?tM5MZY7gRTZmAArfG2mIBmDv6KwxDP-QNnN~O~1 z^?KXgZdY+Q#C6ME_6Db`=L;1UDx%vj9_9s9uNC|dut?7$irH%T(CNeN(pL?UtQ^?F;(%p|cJgTWv% zXXSGFX&gD{qy?l>DGk{dBaw)$0~AD8AfSiXWfA#A0thY?3KyvCd!Nr|DS`q}E1Wwl zl}fL37l42R!!S07Yk{O@N5w0FK%k9n+La+EMtTd(fPeXX{th-@g6NM6EKUkT+sWl} zpC(>3PAq@{k#PZ5>t^O0S)3@+mrkd1BqAN2pGrVBn|(&cC#-g6&c^I<{);%W{b2&p z^Hc&D6tz(I`~6*JkIZ@FJwVvGh;Vd%Y5^d7N5*@s1v-J}4FsR~19NHt%mnry;7Go~ zg{w>^b3Qwooy_l<1fuWL?|LLoE$qxQcRqAUrli1z%U>0oqL{p?wwJ!-EJ$> z+#J&sr}hkB>JGR>GoY%f8!>)SoM8PE!)^|X5dz?$dF)HW9)Fg+_l>wyt5_jGp-^}$ zm&-=8*_^XVVs}3r4nHOm3Cj>6fMFO5@p$~GR;!KN2A9jF#A2~Ks4rR(s4^d-TrQ_K z8jW1NURN*^R=NJ%H%(KCMx%<;>0BHQe6}rL1A_x}c_xt(W zg|6$C;><1e8h_^V`41%3L!nSd959OAPC)eD#8IP&-6jRV1l4Nwz~}QldORLm2#N=T zK>{8Zi^Y38ajr=NR4Nq%E@UE+NUwVImp$(7bcNRd_`(2gh{Q(%U@%&7ioV{!i5|pY zD}LLwm(6CMCk}vwFrlrXza4iOCQ0)bkq)!LE~XMc=z3%UtW*)@dDAk>)!LUdgq z7~YZ+oO|3edOE=Bv1L$w zvtK}TJ{ACzqWVp=p-yqH*UOL2Z91Jk5eW{ij}4GYrC!mBZB9L%!1*eYkNgL7Yyeh* zs&`TS3q*$U96(ot)yZ7{3m}k4c`C& diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-off.png index c766f0bd0aef6249116f33d3f6472dfcc7e4cfbd..c35b86e90abb24c1ade9b5f16309d219aad7cb38 100644 GIT binary patch delta 394 zcmV;50d@YT1Ed3xB!6p3L_t(I5v^0bPJ}=Z-sQJB;)Q@QP}xGrfI_7pkO0nXG-2mX%q2`hc4xkCzRb=Hd>lu%gegmipvGcC+FV7B zpBMfhq5ehsqkJ!MN!;=*bbhx)-+92gD3hBe+K-XilDzIWZU*v9LJaN zoSh&*MOd}2>rZK#ULk%+NRW_7KAt3rABN#;5CmfsMQNVrr-UQrNRH=uFTU>sUkG!k z?0mc3K4w{V27fWpPsHKNsi-6b6^xtB=2r+YOg{NJ1s8GTfl0a29^s_OsPRYqoc08I zC01e@!@DE|V;2jEeZi4a!&QK0v!-b%Bnv_E8*K3fZuDPb9DI|YOoCj;qY z8M&_8O(qku+wDYA6mm>E3bHIqn5HS}x*lVw*yz_N8%@&$?SKC*$-6e6&+nv^w}xQ| z+qP!}YTH(nW%=1Oje!1fyWQS%@Loon&1MPM3g4RPx~{UJo$m0zApXE1bl(Fxj`N)) z$u+*SQUYW|Sx=|a=Pb)^D1Hc}L?mP;(=_#iAb49W7F`&IS)S+TOfz^hxOkrT>ia(Q z3M`S?WgN#(>woq70zOqwiogt8U++vR8_@8Ll7RnV~Ve6qW@Yn<fY z2g944H}8AjzL|M5re#^ic@p1wB10}0guGVdB12vo3V2PB*?*(asAZa_;q%p!T&-3O zp4eVL5F!pwT0Wo8QH#cL z%t;*_96Zw@<$p;CV1AH#g^VNufq+ejBtoH3gNz8o;Dth=L53S0#9o5TJZxIGxM!sX zPH1au>rroS?>n9>5~JZVAz5QU&H=f%o12>thlhs?82Zg|9nm2~oPXTR%uH^1dHD|dA4LHqDUl)e zxhk}w|9)d*l-)z!Px)6*o<5*cFX z65?E&)XK`r*V)3E%afoWumSF&~aqq=IH2X zaeRFI+uGXNRgA^ZeDygtHhqafEMhusZhdJT0?2-6XJ?%Iqg*ZRh0akr68x4BF&MrBWuHPJdr^+MD~*yR56LD;|kN?BiW57MuO} zT}C^^(S8F>64|`5J3p$1eAc4B;)q5s8if8f|zo?%;+S;Jb!3~{V27C!!| zj(^afzWx3Ewx%D)yhKV!1SSY?dHyMNDt zOE=?%Ati)@_jq@A_XTRufX-%A1>zM$GGk+7PgL`0lNLtUSTkzpo-_ZAurw&Mq`F*x z!P&L8w8@QF#8iqZLrTZ9GO99kRxy>h7aD|=>4EVNPypwKVxs4D00000NkvXXu0mjf D#qh$z delta 928 zcmV;R17G~N2et>0B!7KLL_t(o3GG%*NE=ZQ-u(HOpuw1?sNl&z5PIm2_zG)PDb<_nuKT>cW$}3YCBPmrFoVj7L?Thng%ELAY59CUM=gWv z*puGf-F=}$%72p(zycuk1{p~PgF%ZBNrc1UIvEMX;Dth=PKFm9#9o8UG;CU5anDW- zobcM(+LO-C&JV0C5uRvot`Gk_>Uq zQ=tv>k1H!H4@XBw_vuRjUC|}P^Zz|NJG*lif(&Hrq)3KRBeRi8rS6Z9kCRA4WQd_l zhmYVu;)`Q`10I&X1^f~ch2kGtYz17#(w~b3_Vh+<$CTcneoj@jT4-O6% zhlhuMEG{ly$66fY1R44ggIL6@`77fp`w&33TU%QbydULq+1T3JI{jM_!a>BQrlxLy z(ATAb=O+y_E`j-JLi%>3?OB`FAXG%hBf^2jYsUio664rQmD;SPHL-b{9pb*nb zl!OREFgu_E%rSkkSNat1jt~=@lUA0!oeb|lvqE3{%X#4_GbDs~Z2U7sS(|Kpd{Z5v zKYx9@ySpt-KdyO+l8^)@2*2`tQ|d-Ue8opF<=IIqBf$A*rBaC#l!MjMj7V02#gFce z7lx7$8s5{*&COTnK|MN`(Jc@^F(flIH1y1EA8pFU2pfCGQQdRr*$7*MYnGH%2FE`b zj=iNxZp0#{tGH#z)$zQHZW%hSn69{&gc)4Z1LHqaR_BHQ-s}tj0000zc)v9U}@6vZuYI{^6s@Q&M)|I?NVp?cS{tS0M9rBa88Uw>Hc_p3&Wj>ltx0gX|A zGoR1h2;)#50u(?a5zaL58uD)f#vE~+Ao5=gg(1^4uQW|N$9X`KB$*XEj`IV4(r&lS zbUK{{|HU}+3!~|}e%)v^p3o!@heLsZ@_U$J7$z^kV!zN}G#a%snM}d9?Isf;U$58g zY&KgU9?gqikAFs+jVGZ%1@?iHNF;JmEEYS}YSqo>^UuLxP{(<4##5cddZ+y?0il&j zrH4wTLaTlZBx14HWiFSig~MS3`4NHma=HA7SYy59`L7mL6eUSxz-STgy4~(Age=6b zKpQwEUY6wqVp@j|Fg0FCnTZe)2i~R?g75)PZ@CA=xosL+5zlxb0S`GPaP=Fn!WXa_ z-|^gNeT>plRh4eD(t5ofjoIgmyoJP`(AJ&-2f!v@7T3Nk-azKh3NBd12nsT qzi{N)1QFfl6G9F<^@P~LfBi44>foPUMreWn0000|Oo!tHk^gEa3>k)Tt!df?&I6Jp$*kD6?GgByWm!f# zoz8*(VjTIE(K4CLO{>*NlTe@n2f%4061gmuO5Iwm<`xQt*I+Q1!Fh7QQ=PrSgneW4+}0uNGAlB}rqzXb~TJz1|&!?h(HO z7H~$qEXxVRv<_WhZoH7P5FsKCd;lUa0O&3EfjCz~D{JBzFC^e0=LD{P>s9yyR^vNf z8f}PCI;yJDZPr@9-={H$e37@1*b~~?bKnqIx+&j)n66F#Tpf?>y%8@&pMM9SZT9aM ojy#(o8UX_c+3eI4Vh8{AKdv9(priG!xc~qF07*qoM6N<$g813*bpQYW diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-buffer-on@2x.png index 8fb36fb9294e91ee1db7f6934ac1f0f6abe9ee9f..b6e79fd1a58036d8b47ad22c00e0268c625aa578 100644 GIT binary patch delta 1204 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 V(60PKCky}p002ovPDHLkV1ih}{gnU! diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-off@2x.png index 1c2711931c8e35bc5bb098ac955e0c03499872e5..ee839127f8ac2e0f41790ef3dcdf06fab3498ede 100644 GIT binary patch delta 1333 zcmV-51rEXmr4f?q_9)?2sA!)$+1*>e@%z z;wv;v`+dvm>VN9&*jp(oDk>o$=mVY$hU&>uBoYY&oM(A?Ir<9m?EmSA$KxM@2?2(n zi;IiL91h1#+60I_$Ii~q*@J?qtgI|Li0}WkNb}Ci%k#4|ED;%LZ*L!MZf?FwTUWM$ zv@q>wZEbB?MMcGz)z#H^YHDiY=)o}TjjF1u@1R|pV}F{Fv1BNh8wnnxTsHax#NTss zbFafRwY9a~!`@MX+S=Mee@8uQYinVBUa)ZMT_ zG6esAuw&z7F@ekF8iOx;B?^#YvUrfqX0zi^9~0>XasOi9!6yPf3Z^t%SXdZAtU|@) z!o_k0%zvHK$I;FR2+n>cQg4;&?d`n*-y}2vhQ?*t=!1iwo3`oc>02cwC3i|oOTX#q z>3M&Ce*W0h)YLJL$8!-EP|%A6$#>*4=pK2lWI$aH=S4KJ+`Bt7GxIK@WLcbEulF7j zA7!S^&CLXwyAsnD(q%4y?biKHskXVJqhlPbpMORbl=b!Xf0mY(T6KO(riXC=l$yYj zC>L(}b6;PdZDC0 z6^^jn6vQ48hVCE9g0|Gy*!Y**?S75avVU-UB^(ZmNuv{!Ih{_0{Xt125=tl(`fYJ> z@qZn$p#G!2V_yNtC(vSom$5808N#JDW@l$z`nWv|VN}~uUjark#6%qbbm2aQpt1iJ zCd;$By1K?S#F&w544}`bK<We*@Y4Dh=pDOC^42#9+; zjKJ8Sxa~RQyu!Qy;{(Vu%!R4?VRCZvf`59R(%s#C7O}?}OHCII5H`&%EiK|Pq!TEm zM4*BNeV_yaFiwJJ2>^i8CIDfrkCVi*V$%M^`VN#m#rJ2Im*9ou#j&o52B@K-;Z0Cl zLnn?kdT10k`&{$PV03kL{e;9PcmP1XftS1<&Hn&-JOsl&3SenHoUIJ6swzs!A^4e3P&n@2}wtEFsmhdU;fZlN*oBoWG)!y-%{hAJt00000NkvXXu0mjfZBmXJ delta 1310 zcmV+(1>yR~3a|>0BYy<M*FYG`N}5j(OSw6Z5zk&23nqLPx5 z_W^RVtgI|bz7XqmY+o>cZOP&iwv2Z9Df$5{ zA`Ez7d3ibHEI{FY;y%I8CpIPWmnh%8`||eE($W=Ubo@!k5UuFWK$J`g3JMB_iP5Y? zXq~xP+QTJ_LP1A9vn1vp_I(du86TSv5bcPyi{yKq zot@VR+>A{bCv zSy?PS@5BQ-)UxvO@+wa72kpAgv1Q$woSb}tK)Wq@O-)TxBs-&I8yg!tNGZ2%)sZ}t zwcqi*cd&N1w6tgrFK8Cm*4BQXpP#RH_*sb_#(y3Fxj4Cb1Ucn*U0q!nv$M0tx3#s^ z%jvI2A`$bigO+%?k!ssjN8&BkQFN!bx7RZ|I(h*{oAdJW0{A~>&z07W7_&UYP<3_n zuYSM(oB*jc%#()KfK!{KBvz};8g{JJ)zu&8=H{MvCf7Sg{72Z{1VFC#1cVthb0vm2 z)PMT)^mKtU?g56ls*lsEbLz9yo)rzI))E1dXkTmDnWau{R$y^)aanuV-Q*eu^hNog zw_yDS1AUhZ*3b8Ofg^`K778=+03aOHE>ldqDkd-*UZ<#tEdr(i<@&#d?Xv8E339_O zVz)TpS7T#iZ`uCs?d_Lf=llUs+Z_9i0e`~5T~}8ZyIluwMOf6@?ATFCCLo+LPrm{U z0NpkNi2W{rn1e8bzhK`+HB#wP)`FVGDca`sF0NAYeNi}R>&YZD#2D1=0xus5%j8r14P3jgBnw4W7 z24e?d9=vwwU-06kgW#c~5GEAeM6ipfBbqcDt;tt*tE9 z54N|rcTnLqv)Sx~|00!2eFsk#7ZGk^ibmXB>D4NY?KVa=Q;M(o>yBixDE`z~f z%jI$z%73xhY|f>nr8*8lLO$&8sVy%rYa|khvC(K8U=v><@CLl5F`Z7oY&M%Mw09$u z$?TQO<)Tn1R6%48t^Pa_I==a&rUSPDSsr|$n@m>%a~-$|=o9e`(8kW4_){1g;63uI zB*X_|K*|5STM(mLO)<)A11>nd>9p1Z^h}41A0EulZ=lJi}AqYz2}^J z-`_p=L*{fkWh}y2ix44=g$c1|SHxs8x$s3`8T7#cN*|z|Ykwy~5TVs6GnvdAKA*qV z>-F|+Hrp2Bb%;&oDb1F)914ZDxm@nrcsyosI2>6p7~FvXt!}Q(3ayyUX7^|`Vw6gy zI<+Q~={^)zuqrYxkN_3f05U)^CVk~d2>m6gRJy*oxw+Ntc1PiG_%%v9Bp+oeF^(Mw z1TMwn@n@-2>VIQ8o&KCiBvNj-`v!8fuP3L*^LRY>SS*&HTrO7{jmB=JQmK*e^Z6VY z$FEkaJd}MopU>~Ug9(K~v&-drf}E6ujyGFkFc_SJ!GMv=Ao6pcn7 zOC*w2j9;QTBqY^pwRh;0kbF@n6y76t2sn1T{ifgV-`44Lw-L|L7^~Ilv{)?rJRVO% zKJ3q_A%D0b7K;t7R_kZ6SWF=C3cRE?o6SD&bUIyyLU9FqSEaV{- zM>n6;G~ha*zkwq7LN}SN1o|3q6;N&u$N-f?J_oeGTa2%h5D(D%qG#~W+kzO~YRZxI vz!`9S(`hc%Msxa1{Gfk&n|&guQu__$K==rx_YOQo00000NkvXXu0mjfLS;c` diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-case-on@2x.png index 05770dfb72a87d989404aa4041d0f7d299943541..6d4fcc0936a8f2eed0ad390de474d8a205405946 100644 GIT binary patch delta 1695 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 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-context-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-context-on.png index 7447f94face1b4c909f0cd5893e3d9c90725768f..4807bf19d8c9d8bb111bd48ba91b5db641698d9b 100644 GIT binary patch delta 59 zcmdnSw2f&4Gox|ar;96>J2WOAIMB9S`H2D3O^32mZPG1ysw@8-oc^2fTXG(6QIFFR P1|aZs^>bP0l+XkKG8!4r delta 59 zcmdnSw2f&4Gox|Sr;97o9U7Al9B5mv{KSCirbF4OHtCi;)s=q^PXEpL%{!0xZi0UW P0}yz+`njxgN@xNAB)l1O diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-context-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-context-on@2x.png index f04193b953dae3f7416bb2c9bdbb16b30720613e..06edcb4d5b9e50b84c8649aea61f7b4cf84b3977 100644 GIT binary patch delta 569 zcmV-90>=G<1=0kNB!3x6L_t(o3GG+QPQx$|On9lF0;+o8gb;8A#Gyyd{fmBGPy9l^ zp$8aZOrj7W#?dSb`z>z!;P;%uCXaUakZnt}e??@Vr#tu8cPex#Y26(nG>N6QVN2 zfNFz5;tyEkXn`k4Gk_ZG_t0nMrvl)rBoMgbO~a}%I+t4oWXSscXb;D^U3lE9 zcrTp1?NMcQ(0^@1_o3^S`V!v@+upD^H@%N=Gn3~CdWv~()nJt&riN9(b!J6T*nTpt zZ~V>n^L{vD7Dnq)8Rav{;j#XK!)iPB! ziF>UFoWWD9%EXCms!ZU%F%_#aF|nyKf%^tjtjb)Ms0;Uv%H^-BjL(Bj=q_~pF5^8! zAx2$h$3q_BOO9jcIqtGMHC7#r?{K6uFVpDFa)b6Yl){G+akGR3atdFI(4%;PVD<-gKd$u2YW?=tiW1$)Yq=3SM?00000NkvXX Hu0mjf)R`Ey delta 617 zcmV-v0+#*K1cC*SB!5XsL_t(o3GJ7`PTN2bhGU>86jTHkE+AB?M_ToeBj>zDo-U`p zLY^UqN^nF%Rk?vEpth;nIN|@cyD5%mZO4s7kUY|4cmG}gJ09=sOp@tz+FB)9TP0(# zr7@`Ni!>f;JXGd^viXhq1xb={uGXc@h551isT2uw$=_k+UwXj6;#hr{8e+L?4Zof8Ja&u3tbj(Fbc zv1=&?=EU-lGiWl&y}$ua8+eYi1lZU7z4V8*&N7Hkv14q!3xR=KyhYfKCZnI*GRS4y zG!17s%N@hBtA9#I(TtwVXM05%E`2BcC_VDjU)6r`{bP@F=ob+VGd!QA7uxsMdf4(1 zhleP*lM+z~{qL^#5C7%IRU4j%CmLZ8Sq6c(27|#T*YQ8yZuiu}pRTFW7te>Hb(TTy z9vY9izcRAT$iOp;Dh%*Cdzcb4sBLV#EQ8vzWFdoG1b-L?xi^a8=Ih46Cdw?|{9lyy z`~6Im^?E%QW%IbTa}g$L4eOG&0py8QLaUOaELZ;A^x?l`Z00000NkvXXu0mjf D)SE3A diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-highlight-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-highlight-on.png index 42cd598aad8ba721fdb851f3992135f7ebcc9c39..7891125e19f9c10e26c0fdb9d3f3fb6a69bbd18f 100644 GIT binary patch delta 394 zcmV;50d@YS1Ed3xB!6p3L_t(I5v^0pO2a@D?TCFzwOH^AT!?PnH{=`o2Y!UB;6k$U z8^ldOS6PKXb|Klj76rdRu$Yw4Jlc3}aE3|qplUCiF>Yps!Kn!HA0*IFUWZd;>Tp`2%A7{<0W5{)Wnl!Rfp7!HRcu}zB; zMG?K|m{a7Y{j@CW5n>U&r#1vZu&{0W2_kbYB)7;Bg6F^mK)oh!f}t>a`Xul|K#Np* o?Kj;|M_K@)`$a|w{>#1r+U=?nGYI?200000NkvXXt^-0~fs^X6o^Q{1!#blK9PI{AAvj2xpK!9 z5S1mUTv?Vz+Ko?-ZDx)LhKnx@}(HD_~5d5BP+t-F+SgNX?(0}AQ%d%`XnM`cUvhKl& zh{t%1kkMHX1W%f#_16i=@ia|8Mx)Uc*1u6)&S;=0O1~^inZMR2E+z*+OQ^e8Eb4{B z;qVsDdp&4>PQx&~`3X}kYm+l#MN#nhE1VZTSo)t!JaHkiP=^!7M8k;3aSYP_KzM0$(;d&J=hipha4} qwvP!N_)YhJLa6R3kr4#`W#8KisuNz3P{RNK002ovP6b4+LSTY%54Sr2 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-highlight-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-highlight-on@2x.png index 28fde6b1886a721eb5a3ef3ad39eea5d2fba1075..b0e05cd378bad6e17c35e45ba3062ed08da8d255 100644 GIT binary patch delta 788 zcmV+v1MB>|2C4>-BYy(%NklYuW6lFlH)sWVdg;9tLH@MKmg*)XxG{BA+ zb|(A*{R{p9qB|n2Bm|eN(VdGf+_15UNg*0Bu|X?z`0$+RXovUi$jmULHSs2=GxOfN z=bgFl&g*>`^Z7h-o-BHv#8AtBLCaR;g4+PYFc>|8V+g|rE`Pp_iAqdt%#gb?iN%nT z+7@#W4wr!|z$GB4D5sbaczys!z_BDs6XX!HT`HCC7YctF`EIufg%P-^ccp|V0!O=9$VWm=eRIk_HqWch0GM&Su zuZ)7W^ZEQ!M1LhQo6RC&f}G^$902E9Fy@77%$=)Prn^#pse^+&lbdq@1AzJT$0R{i zorp%Gn~cmljNF_9@Dw46+*p?w#|v)YUJHoUfI-&YqYBDqvoFy16F3I^7*k-g)9E}` zoH)0Z!;&ES8Td>U|Hv0Kd;*4Q$C76*%{76K8?9!NAb)G18XJZdbBVgKNlH1HmZDXZmxp7zL6Vo(Dr{vkk=3g zjf9(@=LqF;xs~>zX0xg8L!u_ng5VwYKIBi3uw?7lhcr>H5fTKgv=0eQ|h@o(q># zlTIBx?pz1%WHOnDyRcL$bpy+%m7AwD%kOa7Fn6u`fsO*1;HwB?&QK6H zSb}qCWvLb+42n2TuV;UhrXZT|V?)sBoDiOT;*2=q@5hGU3}Dq{jf`Y~cN)g2;5JrFk!O8i9_l%SHc4u|xy9)$~k)G~$ zZfExG?CjpGHJi<1r%B?ci43{?C*-vvXG|R|%VP8h_93hV@PAlYn5e{ffhlr#DtWP_ zq+XCY3xf;5MIZ$PCACau1V2B3J>Wnhr4d>pbEQ(L+%1(#kJ9P%okSu*a)D(snd}XR z!?&$g>q)&{e*;Hf0gaSIh>=(?m&^C6)#~$PGPxF*g<$A7&Tg$%d(dn)U*q!^M2X9I zB+@S9Qe7?Vq`DggXZM&}; zc(l*qd#cy#JyiN};#v+%5u%-iZK~MEK0)yT*i}Dxw12rvuL=Hm&}w2LG~ZcD?~iD2 z0iPAUSMw@5SZc&gMn*!yj2x2_A9_ddCz=@vsWa+!yH7nb4VZ*>KqdMtrj75+cz4G zQ0I{9Pk&?sr74GIerIHI>_ac!P$fbvg2ww0l&}0#M=s;>_yA7%_tpw7i4betk!{;s z*=+V^=l9@u6Zgj~moltWB5hSLW4&I=t0GQqsl6P!V?4kyHHz3#g~A33O&100000NkvXXu0mjf7+8B5 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-preserve-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-preserve-on.png index 1202888a7a167d7b7471aba78e7b5a245ec43d01..632caa0754bf1c4d5cec1159c97a8624a71a88f3 100644 GIT binary patch delta 476 zcmV<20VDqM1NH-uB!9h0L_t(I5v^0dPr^_TZi|-iLkYM!>ZA@1Zo~mt8Auphjjk?+ znHXbO{3o15nF@_NOb#YE5(XoQE>3Nu(h@+$?_PY(MW~4hmwdf@_wIf7Lf&UU+Pk}<1QPNXm* zieJg+^T)cb$A8D;F|B<;<;iq9eG117AZEsiMz(GDF{603S~YsTo)QX$D7#O<4H^U1 zDL~AOzazU|uU|QifrP=;7)KY= zy>aN|k8l%ZD%2mq=3s)G#=%J9bjm2n|zF-=o3K>z<=X@G3OZ-6h+A)x>PEaEwmjS5j4#q9+1;LT$jh0K`4x z_sDBDn>W7i2lR6ohH-|JB{w>?KsmU{~R;wN26z_?^OAD}m z0^R|NwC}p^Ng9UvgatuBy-Dg$N`vwU=m8%9i9NSktx;=~8d9ko)Jw;4Zo%4=Ww7yq&VcVH)+1djK zzT{6%74M(a4>oVeF%pRTm++3 ziL#Cg)LRj}PSN3z$zCVZjro1RCEy}pMaoP@h2I|_4;0%WB@i-^8JwG&yE{ES{cL1p zV z=^z{qKVDl~dw+-FeMG4wakK1_sf>gMgTdf)r_<@i&J{c09M0t$0N1i*%!@{2?hFkN z58p1AnWOfNb1`cO$O6o#Q=5zscg17sWn>JSoSa<2eE}%yeTM3o@Vy%w8&C9pUV4F1 zU=KM-wV4y2yR=lHj1c>JkPEof*$=cbz#%{~^kXzH1AkXDnT*n)STFhy01`Gz!m$cL zRGRQILbci>Ij$5I{tWB^B;uZ$nz}VVKmTZKY;3%{n_VZixw#oWX(`M@L4`6>GD2L( zA&C%W%m^`31EEkTu(Y)F0z~@JRw|X|br28l^LRY>78VvF*zBRbIYhl5#A2~{KA%?`bA-o4GUohh=YL)&BRpT|fFldvuS6p8X?b}$oJ=OS zHBBp;iBVA`^*_KfSjz;ne7VBY58MD);06E|8g77RKf1oY9>TS4L)mzU$sbTmznK>i z&JcrqU$7d}k@>{>Lqa@r5~9kq4&)H4w_pMxepIo7!n5{0gX>R#<)6L`vj@N?pVDWD9fz#)($FvN)M;>$29Q=vKeW%r1z8jVI{Sc5NK;SDY%HJq9n-$`(|222D3 zfd@#=px5iYj^h)GoOvPSTnPm=4Kcjg+JD-5i?Iao9hfN0QcN2oUG-#;4_#^dYgZ&F)? zOr$6ZM%k=F4$F*DqN-|g$5F8@Slfhzl{(a6DfP|C%Gnuv)9PsD_f@CUR6 z!+{uL;J^_t`VWxkkyG^~G4#+jAzt_kj3-Gn=>-#!*b-8-h+1s7zx}?Z+w68`+ivZF z126e9v$JpBeDmhLH@miSxvZQgv7aY04^OSEtOT*yS{8CD=W8O%vdTMpNRR3S zx}M${Sbtbpc!Jv0XC(uv+bj#sJXDr=h=uV6!0hoLlO`p{idW60Q5MPw(S?OfCX+=` zysViI27|BJPo+{yJRavxEA>fxZ?R<-@~G);gUARmEmbV~)#LHpHfZA^m&+-tswxr> z)~hyq*6v4xo-Br#?`Pp~IFe4M)%qAA5kend?SJ&K2Av_SFAPA5h3_;PjegqL*a+`uh4I`r5t34e+uZnVFgKqwToJ2=TsP zH9i8^Cpi*gfRhljEKAS)r?7+wA`s$76^a;Ft9;L({RwCRNJ1_ki!96`3ytL&FIo}A zTz?LF+^nG}a;`v#gCJD|Dei!icw19Xi?I8HB#j{`Hj-vpD4R<{tZyX5F9w2tC$wb{%O2uv9Ge3TJ;e)G|C?kM zlO$BRlF(|uNsQ0V5Q0&X;^HJm=(7-yQqf3}L=j~8EJ#}(60CG^z|6%RT*8{E3mHPq a{Phntzm#_O#lqYG0000r$(T*eXZ}$3G+z1Yw-Z z<^D91jq-@5X$GHQlN!(SvEYaJ>t27zqhhhRozLfWs5nFe&3`ZqML?oZDD0s21e|*W z-bx2wE0@c6sZ{FNaU9CFZ7q|@NVuFd4C4X3N+OYn&1SPVj^j3UT{k_VkfOn0a3+f4 zX*!+$z*xIzOaf%YBf!pPv$wroPX>wQk&rb+RIOIWs;b^&v>On(@pzmffJzD~9%Wg+ z>~^~^=w0}L(0^!^N~OiJ>=l}}fVn>y)3z*&nNFvlrfI6BQt4XLv~N#UND(~Q1Gb>x z6{IJ)Xb`^|jYbO!pHa)=f&F&7{rjsP5k<`Qqu=if&1UmrI2?+|uhr}INv&4H)@6b9 zPN#FAD2j~zLSJY!yg&mS@CCS%Bu%jLqtS@uNhARWm^F_@l2Jq??~e@}hz0NdWN^O> mQLrcS95Ord))L8;5&Z!5a-;`E3zReLL~x)ue4iv?E~F?tS;XbI+q)*QHhwu~m=|j(fWPy{6M`TP!QPr$iH z;H`A`8jThdKBFe!f&F&7{rjsP5k<`Qqu=j~&1O>_4u=x*YxR14QmfUlbvaY&&{wRSVmNFo<8!0nXW=M?zHM>D=gPPsta)15O>-ERDFO$h^ZEkKJ zSMw=lKQthK5e$YuVN$77sa%?DhX!P`+4M3%hkmu11Ju{oTa}Ke)9D1VMCH7g6!S0K zuL)2sB#}t`aJ$_vold7q9)uv|Dz|Z7U0r<+exKk!T4+=HtqBl5z|W0FqZ>MCwOYR+ z!5~0XGGIkTMSq?lvLs1eUEOJ%V;nwyXgl^@1GvkQz1XNz``Xvk)G*jwRaKREA2_Ly zPNxOejd))xQ?$z)Q9Mx$RgHa5<}H&G0bmZG*J zU=RTs7`U>sQXnWKID<8x#1Inzgk^t!|10*m$(G4v5`P)w0r(s1D>xwALWuj7f8VLs1Du7XOjjaHTEVs0@Tnz?;H$tIM zxJW8-S@p`W%LH{o4I?8XC&tFc?#AQsJ^(TB)GTPZNF_j4pzC1d+0@jO&tkFgFToge z3!0Jp$baWr0C7LF+MrcXeNRu%g+L(i8mt%!0m06W&0*Q=^Y6%grJTwL^(L?JV30t6NeXlyqT{0Ug8WwD|| z=lhQoPr5FS1Qdxx2#WdwmT^9pOSy!sQSv_U-A4k}h$sNF z*WcgYhL<)D0BJJC7nggHz^H43?n(QVMpZzotE=w;`JH6YPGz`Yt88fu%IoP2|| z{D1#Ztet^@fso(tf6&p<(TRi~*s~}ADKs`VK195dg{=w*v2?w?y^j$%f*N|;*VorR zKR^GXtE-F5Y!L*nhe}2u>@(5f;o-o-!ou~YrluYoB7g(U%+Aivz?MG%Hy4VQP967n zJS+G%oyT?jLI4Jgw4`D+p_5 z1FEGw1g#7y|4Hu>P YZw!T|sbU}SlmGw#07*qoM6N<$f~u_XdjJ3c delta 1054 zcmV+(1mXL<2)qc8Eq`A~Q(+Xpo15F*=E|HVB8B0TU@t;}X3>Mb2U2r^1U+UX{b5p2 zPhkZSxL1A39-5Eli&$o$FNxrTizo>k;x=0T*>rzhzu(QfF6(ahGJEi02M*u2?|kQc zzwdX>cdqKP*{pDsM1PdP5K9@5T1LtYl^IfFK+SGY+n{Ezseh^YrPu3^ao_&_{$?Z+ zIj-hY%zkJ<03#R-f5N2G>0)PTvK<MV(G3kR>YTg`|*w z{(en>>LJNw@`uyueCcpFYUDu(LauTf=e4!9=iv7V{-gOerQezW;RF2KXf!&ZgH=^k z-*CVnKvXhdMSn#_jv=xnNkc=!Y20HRJ`2!J?7IeVmnD0lQK$B`udAzLusNH}CguY- z6*8HO0Ke~eRUR-l3Lw=y0PEYZpNhxhLjZgmerDDe012s7N{Gc`U&7(=S@` zb_5I{KobL3R#plGg#*rD%_lL$0|3IZx3~8ddz@s;WPdV=4Dtf}jrCPrkZmEz{Yvte zV{%CO4>bwCYXulK~**x21fBH;!Q18>cOmWxyZWCgkbMxISgP5CSq3;z;~ zLpP!Oa(^HBTnixX=c!hx4XSs!To?R)|7);f$Oi;FJ2r=vp`oEGy}i9Vn5UrI&;+!o zXayt&X>V_LfSpGGaSQciR$_=735X0KU8^9{v$VA2D~dv9)C33s3}|dO5&Q{Qsb{gG zL+ARB6i-SQM*@mQqXb3$2)(zyzW!O-TWC}T)PK~}bP3L0Fw)9bW;UC}mnH?2vjk?+ z&;$TkrBtE?2w;`3G7FI7u-!p%|CIdmN7Yvb2Qye{MNtaX)zu=HgIkBt`0nEX)`%zo zv)kX_-+`Al4geW4#TR$*B7srY0o|4MD~+mv*4EbE1IRnF!!+QaJ%D>PJUrYnIXU?T zYk#@_qgXow0|UW@g@p%QU0vNc@B@1m1t5jy=H`cpSG2HI0U?&IudnYh0!L9pZ{2Qp z=luNqi=LhyGP6k#Ob?ZeK-gzuBO@dJ#l^+zEiElBT*8kF&CJfu&cK$t05|80mQJ1U zcs#54Hl0U0ejxw@Mh{dBPB1dzo8N*mHGg)W&o_rZ+PbHwr|tO7r(YD1YEdHqr2^`N zvaykw3{(_!;4P$3{FdRfvAw*!%qA)qHdfl{6OkK~=2{q{AA>O!|8!WUj4z48xq`4} z!cZ;cA!ubt;hfykN!ejUw2`xvFwum%gt#TU{11joK3vK1$T3s~q?9K4TA3j^8y8CR Y-^sqCsSsfuZ~y=R07*qoM6N<$f+5QGo&W#< diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-regex-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-regex-on.png index 15a72cfa7a4f143cc835976bac5d7e29ed344448..1feb2d62492810f8e28302d6eb7ce6aee0e97dc9 100644 GIT binary patch delta 556 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 X78Kj58O$2O00000NkvXXu0mjf!o$jG delta 435 zcmV;k0ZjhK1I+`FB!84iL_t(I5v^0dOT$1E?!_Oe5tS|tQU{T`i3r&`*}<)JQzT@w zATEWj1vhcXUm^G>gg_@VDRdP&by7q@9E@bhFO1(yn-JSxv-E>6@9y5a@9w>OVNnzn z76D-qBBZe}q2;_AF~(L9Lkv)cfEUlb5d;XuYPGs=n&v$ON`E|&c%6zpNjT_$&1SQC z((Cnx{eGWb+ai%wd2&>g<+pSjXu2QKShhdn+@N5~5X(22!+qTyo z$B{hG<898ai+|Zk_SK@nVDQy$w{H!@cyL|!*Z2Jbj}ZBHT3RDY4NmG889YP{PBl$C zTLUmJsH&>Ne9{BwTSH z!5&GHbk*&4Nn~yxrKyc|Q!`Grh=m<+1AN1QNor|aGA!T8pbp;RD+ z7a>wt3uFw2?q-Y#Lc0*W@j|HR&dV;k*PAY+KcK5p7_35PVda`z?N`quJLB_oKDTq` z#V+>3!}-qH_r33Pp7;IU@2ss{E}v^AGc^+#a%mB&m64W0wSN@qs5POU-`ilAr-0iQ zo7dR(ZI}?aFzsf5zBtZTP{dWYHB>_&(~gK%pnG6oz_Yoz`4ZzUfCLShAwo(Y3X;w@#oXKHHdnbm6TE|p3oK|((`Fk-q0 zfs0Zv7Z9v(b$@j=JTfx!*km%bSICe9Q>M!=7b#ZvLXcs-SOBNb=exSNxcJ=T@dUV; zip3N4>%<%f=4X^3l`D5&p@JgT9m}bjQ~OQem?X+s2I|2@YETBoK?X-4LAw$RK0oVNe{JqJ^$){$snSVhISs)PbV&OjGsueu;r+$lS z5Rg&orORTmxR;lgqr=0)5BXe`qUxGD2Q2!ZK>dUSWx9rjhJ3MDY{l(%k4aKgCy(m1 zZj4xqnkH2Uv(0mJb2rAv$6wp+_WlM{5reZ9S(BP3BXrzr(&@B4pU-y`3fvrLW{fFq z$kHDeet&&^JrRjSp6u-Ge568Wfm*L6Bc$|O~mL%*?#NEgRoo;W3A4;4|<6NPnOYX`Xjdg|MImQglj{DDxj62bHpD z0|%(B>A}H4Kc1=W8y@TdBp@2Q^ml+n)MTskq=YyFq{<*gGQR;{=_s4YWU@<3OKGML zZD9e@06uOVPx{TkL;zP{mA*$affF2+M znajtZi!Ku`sl=w-)v*`*zf=tC1AcY`QO1IUW zGSM@FY1I`!X<6J%m9%I@KU!l$3W<&&7C=e-_csWfrG+c5SC1PeM2D3WGK~b)qlmof pwlg85JL)M5D0fp?i%_j}`~x8;;SMx1PaOaN002ovPDHLkV1knp)(Zdt delta 981 zcmV;`11kL82kQrrB!99=L_t(o3GG%(OB+!b)o3238e`R_EkUXzXb`%nLcv8Lh$uzS zmX?A@yRumXYmjU-lv2H2tCpSw2zLC z4z91SzjHdBkMQ0GNE0#~GBSZdp0fk(^YioLlarG#tyZfum&;+Q?lSO;0~s+y2wc>B zX@;@RwY9Zp1AhYpPfaFMgF=QJxMoN%52;e|L6Bj-FchsGkLSkH($Xup+wEsDWwWR9 zw-tLF*q>C5C{I@3v4Skl72K)YQ~pb!m?0t=8_+f}F)=tZGqdV&IJz}n(dwm#t^)dx zW%V5ahd>G_NvQs;cA=1=;!6PY<=)uX*bB4SOruib_ka7laqs|k^&Twf(|<+L2*{}U z(qge#Tq`Rp;r{;q$DG%NsCcHr0h9hWP&pxuXPSLJpJ!oVVb$ew4GBV2A&=rVCq}}y zAoLIpnFE2qt&x$DH+H+dt4gmF*dno~Xn!aadcM26`-vW^1xm8eOo|ApJ>w`IkMD)U;SJnVAK>I3xuSjMK?y9#tJ3IT+)6;itHXAQgSxMvIsZOd@B<*|QqhbFKhgK#$k!y*WEO z`=FzvqaE-6$<)-;YfRGUU)>@b;uqi}5Jev_w+sVJRDBvQ-E%L#^W zczAdSm#a1xW7zWKs zJ%7Gvgo&)4=d+}o=^^z5NnxCYx736rsX_?1x3|-=SnOLc7`%_Xx`ugr;1EZcGscCs zi~ttc)c6n0-;5sM4)6pH3PmE3_ZZ&>I*sVm?iE%UlnMo&A}RHkgm=?*fCL!|R#F^B z=qbZeQONXKVT{R6<4jUDFZfGKk|e@#N=}KChqRVJj1|TZ67(EfLTNPay~r)f+XTAdKn(-00000NkvXXu0mjf D-ABw( diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-selection-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-selection-off@2x.png index ec6f0fafab3d86c5d55c7f0a6e822d16c89d89e0..911c8b8310e8932da0cb10e8bc8879fcff2b26f5 100644 GIT binary patch delta 548 zcmV+<0^9xB1la_TKz}Vwq-`$~NIOaLnWR-gj}X|p7Scb;h3V+@eZ~P7bLKiT5iaJ! z>B^mZzJBLBcf2}b7@A!gg{3je?FfN!FL|zk_rTfcBN7=rBW#>FVUJ5f_53YjbhuH zfC>b6YIKGItWH7`idz?VIa8C`+I|&CU?@Tu9PafQ;O8WR`7DZho1Zle6-ex*2oK|U zJU-AwwpnkrT7OTX$ypmJkW`~3rdg+^gqE)Y(Pjn*MKGTEC+*lQP;{fTtUyd=%d!r> ztvpB6$^^uIP0xwCIc?0;P#;vrZkhoI!0ZM}Pf2)a`cri^bv{_s;qb;yuLR z7i5x1#mhZI6V#UaxmF8jUU?c7nE5<9e_C1Fq8@#1wDYEnuz&gTVuq z-=cEjfY{D`gz93H;1+BA4tYTYQG4TnxSvqG2t7nrFoJv|__9R8VBOe&xaVI-)~f#; mHV3jS^YuI5x#QId!_e%~DC`mpS#ls-M{-DrYf!2~ zNFI8g=aB!V099jP~5t>%bA+g*7mDF0z(nP;Bc?c06!-g%x6*5+x)C)s6b*bMR*v; ztP0rd-futHOG0i$PCA54Mh&D4gD1!0KKWWEifub9wWd&j~Tb6b3 zZRI(lRwf|!8*hbaHk%>E(jMx1y>8^}qyiF)Xi6$p;pp+#pD5hzV$n|1220000~!mHOj*j4t!yD_SnQ8rjksVf0ZVAsG|iw81i>Yo zr(t1#_OmQI`wCMn2jdwDErgv1;Qo_heGeJWG=vNFnh?kFm35p2G!G&3TX3PSD2fWj zp0hKfu2PD+)mdr+pX}!(Nv_MXR8;VvAf>F*G`)d{)Iz-dy9gcu#{kU{zXVHTZ}3Rq yj)3-CbF|K6(~~N|Ot#4g!9Uqp`fH0$CiDU4rH(^A_|zEy0000mkEDq4i@{=fjgDy_8hZl1cPV#MLXTSY2J2N2Kw%r-ScE=DQjsGXK zr=BK+pzQX617HFOV4YD>8{iF)sPPApdLj5z-}f&a$MHrs=(9S+gMl9xuzXux4yidb1H1tT~_wO{%Ku6kON6 zhV#;G^yN5?*54DB60;czr{-P^$<*5Zq|jSwQg+>OTuE)Z}@- zLb0dp&Y&x$q+f2;iohp(8HVAaC<;jh{|#A|t)eKpgGi=?c>50#JO|DInxp>x@VU`* zmcRo6?bqhWz<~w)WwJ{Mz2nZ$K+s%Y$RzIjYdq`&<@anq34VfJIuQ}K!>!8xyb?My?4(& z+_~?*r%k8R$ZfK<+a!isLP36SB+O8lA-@dx&ENUg0!`CcUw;JG5SjygEo^Ld#Mps_ z{%1)nn$#E_F>yHD1-5_9 z^w%UoRGmOAZLUfXxfuhPD?=2yJ|1(2uGH4St|UYmz#_Z%cm?Hhxp$Zw02ja_#uC_U zwOX${PK>S8uq23nnpY6UegQv#KCp=K)jSaR*k~1#1b^8J1%jXO?f}0Pmj4gj$F%eZ z>7|I01ljb0pqYMQFPi_u6!=7MNsxWX_7c?V^|vC!oP%CI$IXuArjP`2e!8kH?+UJn z;hZFhr|@_%7>saiaBBu`$QE}h`-*bnOMk8kyti6 zaJ9{DC-}vzFLufQ<9{=s*ZtRls}2SEr8LZtUxqFL81QxZIV)~y00000NkvXXu0mjf D*breH delta 731 zcmV<10wn$E1@i@vB!9X|L_t(o3GG)sZyG@mODT*c~Mw?6&4hz5{5Cf(qHA<$7IR5|xxRyvMght4`DijLua=F~+L?ZDv91fG* z)Yk9!jdr_zP^;CxmdoWI2y_7`q$EOAVl|)7zb_VxyOBs_d1@AdqiLF#rBdl*rBeBh z*XK})%Xn*29)IFetz@&=FHmLKvaFDwU`)p5902EX7}ana2d&2A@h!;ge&I!mybn=u{j68xf~`DVqdJ+b1bdH$*0E6$#sO22n}@x+6vyvvXELgU34ZBmBjG7m`A(!o5zX zbA__O6XLr;t!o5j)>SILM2HEftLyqubPfI-UNo4NE4IYtd*_N%MDMynms}n^0Y^*Xw)D zW^*4NKY=4udq*vk2ytRo;uNqBB<(hfuB0fsJ&k*X`{oaz1HXXZY=e*^ok)9#n~icF z0sPI26bW*J&q9bLhlH453Emu)>x7~Z28B?K!&G=sDfbn27DB_rf|xf~SQF}uM+iWF;O99)Dw2h`qjQu4G4Kj8YtwY(0_7Pa{Tq}c8B!3S{L_t(I5!I4CPQ)+}g}wP#w8=J33X(2p>y<*yL2?GJK%dLd zAPR0llaiVPP(&i+XUCv7m=q&cO2bIec>KP3a>m{*%c8ajXA2YK@ehphJipGe>`80= zw*GBN^K6*FTi10TqbNE|)AasV&2T)Uq9}9_1P^JN9^yEDZhzbM21b$whK=TVzDtrs zJC393y1s9k<`s~nf!X05RaNyuMyGKcKf12l6EFgjG%$RkFbt2r?<>k7HGY<5$uCvX zV8p{Fli2gVOB%ZB`yPyvG(c=bx24R7Sg+SLIAk_5L$+a4BP0zB8x6yt>}FFVBn=GT z8f{!*)5>_=k|y?r+eTLZ<;$NzT;BVuV>6>OHLYFed$Yk900000NkvXXu0mjflBkU$ delta 305 zcmV-10nYx&0?Y!CB!3f0L_t(I5!I4CZi6rwh8-{F3<<;{%=KFp?g}0(8^h>p6 z5b4Y2oJUHeT9#$3>pC#ZmH{$F%ynI?&&DL0%}ovkTBJe~SdN!-2+(U$-}g-YF*G$t zvt?lPU!#Q`7AlP%uiKXW;HJo^e}4FTmgj3{6|0FJwJ2T5(np$r00000NkvXXu0mjf D1Bs5? diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-off@2x.png index 160f44abefeb8d3341a960aa345840a76bd788c5..c643e1fa98b8de46da492fe8ca1576eafd20a793 100644 GIT binary patch delta 684 zcmV;d0#p6C1;hoAB!7!ZL_t(o3GJ85suMvFhI?`+7h>)Mabw_s;KI!;0>>v1MR%?O z?tO*4gOHsYU*v!8VLf35S4b=+yM(ul}a0yN7tY-Oij- z$($1mSS~=vGIEK~{}Cb2^NPpbR2u&qT}~28eVtAxpgZdWjeostHk-f6K9T0ReW&V< z0yP?qN7-!lBh~krOvd0M-EOyE-G$w@R(BAn)oMM?=kqtYT<$4VgNm|Vud^;dw5`>_ zM=DUi-)|GZ7XrE}l}Z+dB4bvo6?9AdNJXn-U#a&HE&RDqC|p&mRYOonlc7y{I~)#Q zO{ddqcHmmAj(`1#5%PWi4Z*ywR4O(yo6QFEXXEks-DouWMbF|FS*v3hSP_UKF)o+O zhM?>w#ztl6&EL8XxUJQ(o^2v!lz*ldNELuMI<%>=Z|+zmS*_0X zl>nl}pCMJK0NR)CU@%zWq+x@@&StZ45OcITUVv=^h$7>2x-f|&#Ty(z%VqK!(&`w5 zZ48LMJGE}N>v4p5658}BuuZz9&0?|OA#1g|n3H`Y21J)2z3Bbk0fbbL#z-pd6s=D3 z9|Nk@YJaqr=kA}F&e>abOsk7I-Zx@E=;H{S5}|+5?Kwh|3go7R6&yisv*i~#RmXa^ zB9N38793mf&^Y@zWD`KRV{nqv3g1KSxK`)-i2>E?bu*vO?HrsosQ}0Y*6Jkxp7Y-x zXWf@33Vq)qeOg__#r>mv*(9btT!$N>hg&&ir6seA&;=+ZQ(^ZdLSZOU9{vC*9ts#Y SoR41s0000RFSv{Zl-63U*4b*c`hQHmi+a8OhipgM7q0D! z+e(O%ze%N1U(@OIr))NB@Q^N-%O7FlaIVGul~AM6IL>4;chuhzA>$BAhXmZY7Ka## zgsRnQvrs5}Co(5w4Ov4+X6Exba5MZ!O^f4LiSJN8pT8wVPD-VcRR(0J-S>LE%h70b z&H+NJ#c>=_p?_wxd7aDUu0RILP|aZf&%t2uvET3irm^@%*5a59tVoDRd?^--RvBzU zAycw%MCKPwK}b9@Ee<)j#g44qZl97mXC%ErA=BGo33a>OI|4mW7s+UGOoVNW5IJs8 z#2`c0-4Y`EI2;b|$!80R!GYD{yfCpNBV|62LY_J_$A2+BddOg+#RXyFHAFY-y{pa! z@V3+G%yH9D;IQNIIC6t&ac&HZV+s+OBODb-p`nJ*q1xt^HnUm0jI=nL#QE_E(RZgp zJz|A;ALjHauv0vY)9I95)@pHHkj=y+R4$h(_w&tyN>bxOcYRu%8-vFlp-QD<>q1J( z0Qf?(T7MkJbgf4S4o(;*Wf0dC&)I5mjAt7o#0g=ghQyLjiwkok653TECbTPVDfUN3V(roB_>Kq%p)UX~9Ey%|W6Fn9tDoeCVrWsB1Q0000L_t(I5v@|aO2j}APA>8E;5$r!x%~g zj_>o*l%?RyQx-z$rhSOuj^VSe>o~lmJ-o? z2@$_)6-G?7>_a5Zp!)>CK0~J>?!*?CO+*`L*oHDo@(t%r1f3X&SPd6WM8PVSE_)RY a-unYEzs;o7KOGMM0000h;Sb~&Y*OU|EV4yx(+D<#rV&I~L^s(G&s;KL!Q@(O22SR2?wrgWu3A-<(uWQD z5Fw4;gof)AsW9YYU|QD~xcZV}87I!tj_N(pbi1DCT}4s!7=Op{2lC_pIPMn{)qk7% zzJG1o_MNWllRVGANf_K4d@G0gD;e2cAmd0w7%zL@FpLXaM=8s4l_bd%*e`${nT!)6^igS9nB+VR z!?~tuqlQ6%eR!S19dvQ%ec|&w(Fl_exX%6%!BE2e5gO37T8!RQCE=PDo=Bx7cbJT8 zT3Be=AtYY4jAJEU_6Z7?z!4Mh88EN&R&IUSEVPA&J%Bvh4}#A+Y2_e9XM_nMPB!uU dvNzEXy}wKt$%f_Y3h@8{002ovPDHLkV1mzWvqb;^ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-word-on@2x.png index 52f8b3b54ea7f8956a92efbd86f758162c7d723d..e7c6f3308ba5ca48908077f6ac51578283e2e39f 100644 GIT binary patch delta 848 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 P00000NkvXXu0mjfXDK83 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-on.png index 5df3e461d5e4e557ac50e3433c560f893fddf51a..a78140616afe520f2e4e7426dd99019bfb50a41f 100644 GIT binary patch delta 574 zcmV-E0>S;v1lI(RB!3=BL_t(I5v`LyXc|!*$Gy_j+Sp6Q6a_OWxVSmE_1uI~N*5&}8ah61?=Uc%JtAEqBKqsX`z> z`0{(d-|xN8z4v?XgvDavxCpy+5fCyiuL22z!1}r_j#cO<^nV*C(cn!JmK}`{m&@g= zR4Pxy;qX1D(|KbunT!U5!OL7O_Y8z%D{K|WQ!bZxW3kvH^RxP(*Xxz?`TQ6y?szqhhs?; zMMEl;I^{TSJDpCS)7s^71pt_mAvE>_jUZ+OK`;~wg`-ZVbDGIyinK<48habbXCtxPBs;s7+X5Xfo18wErVGB*~BZQ)AGbQ6L0QqORMxz=uWw+ZG>52J#{tUiU z0P3R0ByD)bNSkk=8;HIqCDb4@vStDmi^XFwu532jY=1l+KfzoG1OoG@-$5S`j{|Bx zNVNyzBZABf27??RmQ@6;o6Y7E0kHW2djAPQO>?zYxm-T>`~4ekw|j*|4FsxItCyJa zwAKst)f%NzDTk+-yU&=~Y|MFb6px79v0Hple9; zZ(*$o1uR5azy>jlKxF(d)PZDkX|5X@kZ#u>3g7!&_Vk90$-n>r002ovPDHLkV1oWY B{C)rc diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/icon-wrap-on@2x.png index 447dccfcd2af24ed5b200c3efc6422cdfd52dfbd..867d8105f750045749a36030da59a977bcebb21d 100644 GIT binary patch delta 1463 zcmV;o1xWhA3&{(RB!7@eL_t(o3GG)+NMlzN&NGRVNha~9K}Y;SC>7Cx3I)5+iZp}8 zk)lJPrHBZ1VY^V=xvJn!snaedE?PIbQ7DRH>r8NGi&bWnDGEAio!Zt`8!N^*wTbz& z-)G;g&*r@s<8(2L9{9L7=bd-o_s%`{dneM#$%)~4vb5)k41c-&Pbk@rn34q~yDmu* zvup&o7DtiwIOb6qG1TgXidDK`9M(N=AR@s-5?25#aFz3%oE%GTZmxNCb@dRxL%^Rv z5I6?n$%tKwkW9vm2iSn@zP`S<%FD|ev$L~rm`o-Ie1>p1e6YK_`*3=C`qPn-k%w>{ z03@fIt;-T(W`FGY`T0d|xBG5hUfyf?t!l_fY-MHTv-bA(j}fp79MPv53}?!OM@4!b=L9MM51*p6?*_?%Yt+~0ms;H=_E*_5?l)zTS zqR}W^=lvBG74O&A*H_?PZZp?VwaXCVb~&o5s+#anNq;$u0%)YSAI#thB@Rnxqn8vVDE#cX#von^7uSt$$;4}Ud3KfjD?`;kaQ znwpyOu~xdey51CPq1Il(oJF4w@B}8W?8UEzPRTkk9Ay!u=z#1DpyZ4nQ8s2tz!7P`5N|C@CQ< z$}AF$aaKo1 zhpVit>^7A+Jw4rNG#W|nLM8}73Zi3UWA||r$@67YO_dNL>9^s#RM^|wdk3fErt)sF z1xlaZ9H-4NF){JQ+S=M<#07w`R(t{>xn{UE&Yqs0PeAH*@s?VP==npTP;h*F+%q{j zIe&w=UjPzf7ochhgb>BLvA4Cg-BKfi%!h-)AnQQWaMUCF`}+Z}*ZULnXdw^?{D9ym zz;D12kWvk?b{wUprMHz^a|A=$zxH@MYw(j0`x|irInsE-L=QyaV+*neFf+dbJY`|6 z?-;I~)(S_Zof68($S7438X6k9x45`S(SPId7Ah-FAeEO!qRaq~Ka2M%KoWjH^hYGE zX=+e9FFUQgM(jUF6^YGrNxCG%nAvRplHq_{FLCg zu8CADgXlA%7(>jEoFx+;&Ntoxn0^Z3(~^>s7eoxldV&-94gOhtj*F}z64F%S zga!WPP}?#VC5x6@RScBiguT?z&`<-hd@(aKGm9iBlv`U{+jDbs57yV$DSrvy0UH2M zmu3@>Yr2*|h|%)*%z`ONObQ$m6% zX%6BOOqUR&L5dF-f=E&1oG?CSf}es@u&ywFW=l%wnV*p20%|&x;u8OD_+R7Rx+#^S RH)sF=002ovPDHLkV1m(A!S?_F delta 1455 zcmV;g1yK6Q3&9JJB!7rWL_t(o3GG%*NMlzNPHJM4SQE8HjXzZwtffYZbR%dhSg}?R z9SSW~aN|Y_3U1tWP8ESB4{V$(iW?X7Dq&#v`!tIj&`hQ;?zm}iRt&H z@6xBa&!;I2v*>}3d*6BI-uK;e&OPrKL!pr2Ws+B3CNkvmKYt;;h?w*Y^xhkdMtV60 z;9eYs*UvCNRS-n3UMX3PSG2>v=LJNJ(2&HdfDN#6o}8R)Nl8gDFE1}2;5h(12mHVh zaHdBrDnbewGd>UpB(=A!lfU|<2NeSpL+ z^;Wcm=$ZKR^ndg>ySux;Nli_CAJ0;OM}kXBOW!s&Hr|544sb}F)ELTZ6H>gx5IKj2 zhPtw|vp>?>T&-VNSmpOs9vi#XX3`y*Vh+iWo4CPV}lweCnrG&bLE`A zprD|!va<4Bt_8S3p>rDbY5lMtAF~-*by8Q(3 z0rc22fTR-vTXAu5CX)MOBz`?ZD&$uP=B(KNDu2aGWt0+9$cRdu(A3mamY<)0Gbt&_ z6&oA-Modgh*h?4ol0FLWoI+5y+%WnJ;9;a9B?L=)CJr|#v#qV|mec7hQ?FK^TCKlq zJ~A>gvAVkY05#=b^`3*#xsuo5#V9|P=bEa-ny(SA%~E+kZ2Tl*m%=oOOT??HfG({0e`@{1Dg z1d)sZdWb-xo9uQwPs}Pz_cIrs<>chNE^Ihr2}zJF2|Weq9qNt{rAr$#*n~w!h=1`7 zqTAb2f2^jahOL9h8HUJSR8&-^w%Om`--4b8A_4Y1%$F+>!J4qhLdK*FtgNiu&&bHg zQ|t_=yr1L$wLdjA<+fNX=8B4nO0=z?l8}&%jSb!;9!N83g{{^ot3&)PWFv$)1t)(;(yF zT0ryHpvO=aG8QeKZhL-yeijLBt*fj14E+y17a<@Qnwy*ZsjshZ7%!Rh|A~++WK2pX zEkl(JyoIpkR8>`dkei!(BOxInktA3sH#ax8rl+Uxt*x!GB>V!b18hY!n}2w`yHp|w z(OUVRSuiE33?LI=w`T{~j*v9N!XhXt@h0XZ^BjD6F`^P#$hZ-239Bya2sdX4SyvnY ziINci*wnKMlav==4buEi;hnBXJ;YV;5AQ>KldJ-imk{~$0R?UHZglG~H zmI^UZ5J^VdOco$|gW#O06)1K6!TPZ15)y_lY80n^QQy=3&fmPAy*qCPi6j63002ov JPDHLkV1iAKweA1_ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-horizontal.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-horizontal.png index ff2ca6ef7a76733335c7a82d920f589bfda7d368..be64d2775e0ab229ee2de3693ee0fb6da5a08cea 100644 GIT binary patch delta 208 zcmV;>05AXZ0qg;gB!9C>L_t(26{V8B3WgvQhBGB}a&m2I&PxpM@T0*C2)ckK!N|V% z7&hjhAj*;F@%4w@_uaKLL9lbK*ILg)h!Mh}A`I7c?Za$V{G delta 215 zcmV;|04V?L0rLTnB!9X|L_t(26{V6f4ul{OML|}Pii+0C+Rl4$BjXKMk6_{fEDQSZ7(bnog%>9q-mlFn^!V!*;vy zmpXuj44XZIcw?_IPHpHIV>01a2ZA8@#A1)2YjgqwBR9uMtD0*YE|<#{Huz*pzg7Sn ze<;j1fZQlEqyG?IeF?d+SS&(x!xKQ-CIYl!QTyz0I7hrDNpk)aivXPP@niG=ug51x zv`m8er8N`=*BWoY3gSim{I?9$H*Pi8J8&z(rp~_uUt{&L?f>VcfdBvi07*qoM6N<$ Eg2F`CWdHyG delta 453 zcmV;$0XqKZ1K$IXB!8w!L_t(Y4eeE3YQjJe&Ywa+vQXlqg?f(msb|PRJb`!u50W$V zsXc>FVnJVh$sz@tx;dPkjy6gWxj7_b|>!fcyw+#yjzl!J7^1-&1PN{ zMPuLhM^#ms?=bCeet0fC&&$fP+*oT@)9JKo;@xHm=JR<7@P8vN5_dijP=@PmGMVJ! zTm>-lIF6qgb-&+t!Z37bkqN>6$s%&+a5y-5p4XPy*lxEkk_j!ra5x-;!I0P(b2hQ4 zWdIUTmSq%SPFzc3bQ1mK34d&{H!9nkAJ z_d`UL*L8^}(tyTsR87yi|TqFCp7cGlViCWCcd_r{oxsvro%#2C9_7{I(R zDNM9WOCXk6w3~oeO0}G2kj`16`6+nhoMn*BulnC$pqKc%9YA;~<;h{b9tzhG0%rmc zd`jufVFveochp$C_vf~4g2s6q$1+WmV^BW*W;d*`O+-QzC0qai002ovPDHLkV1iL5 BV4?s3 delta 199 zcmV;&0672d0qy~iFMotE424@gwVj;0Iyt-b_Z5F%m($tlz)``eR8TLk5V%_o_Y0)y zd-;ZvR9@F5?!*I7-}jj@rYnk~sj8}^2ui7W7>3a~cY;Y^UDut~x}_=zLh9oW(r2UFS@v~Z1?3)NoV@oVgYxb-djle@N=o?UJ!}8~002ovPDHLkV1j;& BUtItI diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-vertical@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/overlay-scrollbar-vertical@2x.png index 3d3ff15c7804de01fb73be7a697edf4a87177771..e860a90e08de8063fa2f983d5d7f66ca032d2d95 100644 GIT binary patch delta 446 zcmV;v0YU!J1K0zQB!8btL_t(Y4egdqZo)7Sh3zE#1fh5XOOTp#&|~C0h!b!ScI>-q z&%lyO>Ga&?c>_Jx13RN z50H(Df{(KH(Tmf=0*j*f;$EKTH=ztj8wy&1_R)*e!veD`+ahAh===R%w@cU1fYLP8 zI%Ws?bZz*4)#%Z0U(r`0_LiohouteFd~a&>XFwXSRyQjjkze32Wmj|mia?#=n7gW~ owtzL>-W8D0Hezeo2IBgD1C6?#l!T~{JOBUy07*qoM6N<$f@JL8(f|Me delta 439 zcmV;o0Z9JX1JMJJB!8GmL_t(Y4egdoj>0euhWnzZ0yS*91Zg-2bBvw`aRLs)30TZ( z<_s(uX;^mEs8YqFW&B6u$dqa%3Ok4t*-7lrj#J5X&*!uA$l*N_23F7nE*1+fNs?(0 z1Y_U#Rj=0zNIIV9o%;QLURBj@v)OFt^LgF$#oCouUM`nW7=MN{*L4*jn6x&M*N_w} z-|zQdX_}TzsLc?WjYgwcUDqm#B4;=pI*<}X**Skx4F-c5!9HkSWNK>|$MKY|rI3+Q z%48;^1I&S7gTU+cdMa(8TOc`%vB6nEZyNvuh(XXv)5Zc35r=HtG?Z&$6U%+*7D&3O zCo;HU*f(Gw34i3hyW|UaSNV+dtA?){cBsIUe>NwS3_at9VSJ>$tf9ui;c$@Wt-7!= zKGMhnsh$4dpe)OOv&0P;NKMW7v;wu(Tk0!0jPpFVPHYSif)z!k}@P);X7un7P8^XKj7&!3N; zJ$rTmQ2rv&kjGdJ0NL#A?d_|ptJ}%R$;tTZ*Dr3Ul!TCwkc*g@n7fROOgT{9Qe>CF z!@$hU%tBLBvl}Qm7l;km*w~&iGcz*)>3cxF&cA>E=7Hov>aYf)k&#go)_19f52{ zdwcs3YisKnKzVkcf!0`Ez{SNS477m(7lo+U0hro ztE;P*TUlAzRaREM+q!jY8ZaP#g4AIR17OrO0TqPV+1aJ@@$szzhQ)NCt%5-B%mTU~ z-PF`H8p!_xbWsyZ+Ck3FlP6Dh11eYxG>9E&6DyF-0Fno~3}o|fppxL++}vF_4FG{D zQ>G{a6_){1p%>5q384H7U~1e36zj~$$asK}^hXPn;a~tjfB^v4ovt?qe!STL0000< KMNUMnLSTYxC;8d{ literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/panel-close-pressed@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/panel-close-pressed@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..19cc98e3377e92f505141e8755b0e3a8c1a0f538 GIT binary patch literal 1252 zcmVBfKy@vWmwt*_qQSTblfrdrdw2?@0eDHKYVhC*=_R0tuY z);Nd~E2v>i9i8d-4dwY2xOm8}h0Zfu^m|cLruF)9@Jb~ZCwea&jMx-Axq6Kf-fhlkH2k;t>{?d@L|78ZV4U0vN3iu{i8 zoih^<;2))>rCICi>u==c<$Z=e2TYL`4;kw<7T(<4ygN5H_uby!-d`Ag`X&;m5)iVN zl$Ms}S5#C4GcqzR+U<4)k;ctz{wH@OcuGo&l9ZIBgu`KFcX#*6{QSIcad9z(DNC>0 zj{m9xx5witsHv&BlbM+r2S@u2(knDU@|CfV1USdxa3~<`uC1+|rVo)n`pb~nj(yIo zs;asP;Bz*cO^HUMfq{X6kFnnYMk&DJO-7Ef{=vb)t4Bviw@H|5tE;Pn^d%}tw*m!q zb#*sVQ&TVU$NKvEo#El(FZlP+&`>`B;v|xjlV5?OuODLpNF7^VUT#Moe9!q%!%biK zvrspI;^N}-OgItMFMbpxUA6!eZi+o zH-Yl<^6QAsK>)~jsimdGk8ayZ5&&St$H4Kqg0w?Qazbn5YkhKOSXN78w@M(YEK?ACo3proAKTUqP;GLroK{Dwdv@Elz&C)cQ| zYKHkyEnccdPsJqS2;=KvN&B&a09QWW+7Oe4El=ETUzNED!Ja-Li#eUMNh1UBVD zHpgMoKMrLhwqkd0@%w(TKUMHr3Qe1W&6qM@cDelN>rYh z@cA$i1%8qd;DAw9R#tBH_V#|I1;+tn?dRLVw@*k1JsZtJn8A_cFY;Ycp>LNi6DI@bQ|IZG9(Xu!8U?4x&n4Nc2HpXL^u~kB?tKR1c6) z1w766_xCsO+a(iB`yhd0@qEEsVFSP}aY6BYSc&agIrEcDkoXY}O9(2#B(ThKZOL&Q zgqJ|34$A%U){oMenHd{iIiCZ-hv+}ho|3W=RH*QR`Gc(sG&VNAd?&AB*>IU;W4}KI z_;r{E(Yk~Z8vtE|{fwXYTU%TIe5cQH<6kp_fI+|@U=T0}{O<|;Ex-U|&i~L8%9)M; O0000}M{P)ZqL; z5@utYRz(jkZ{~iSd+wR{h8_-w)K8X*`r-Nm0L-L=rs+9s7MO;-!idUUSObE;d<%eL zi__^`al73+JkPsWmNoWzy;i&3u2rkm6553tyVWxDiy^rOdTv12ZO;miIY4<5kY$o z-Ka{XB4sj}7C<}zxCekIo6UA4Nva{PCV7e@m`o4nlBcKvy5>N4 zD;Nx>3V$$cltkP$`OadkW zlYmJe$>>Cj#p1xt19QS`9D>C(i^9UNHFVa!4Hq@<*5tc@HU9lhAw+k3vXwe`pR{QPgr%gei( zB2V%A&Y1~l&Wp0Lvh2;x&DRSH3%|EotxlLk(_hhOR9JX@ef{zD^z@_c?d=2nwER92 zZzQ0}exaequAkaNQhQCK0ZE0fa_^cM6%gzQaBuzc6WC}b8~Yy7Z(@T zFlFgo>3G{N@RgUBdunTIA7o}`7DIB5EvBTTNXf~`BzVd=P6C`mq9E+6sj2ya7+U=p z--fJq$evMMU417lEzM=O+bONu-roLABoa{>C7lW9Bt%7^j*K~%G4~$VIGs)xFeZql z?I6Pnc-q?9uBWG`7xM)KEI3Anh@Xs&jZI;=)Ivm!1eFj9;92hO?p8JRth2MTrNLlu0!pkB zgIK_H8zxX$S?Nbkos=0VIM>(L*8`x`L=sF3Ohg`cj@x-iZ*~fLocH$jD!>ScI^mU@n|sBu0?eBr zKx07_6cp6=_xJZ8n&UUNKU`2N2BJz{US2@#3Sq=FtNzfVljKu5p)=y1My}(d+ z;u=L!tdoUsQ7<$YMktIoCmK}d*bfEoRdBw zQlJI#r(pt!;1?ko`+g`Cx{K(>6P*tZ4k|-KL$7VorlzJ_D=RAxc{pi!aZJMmki!`) zcuBnd{rx8+BO{OL6z?|Se0X>`zSX!p@T@06=I2#ay^k%sN(^EF^GCx38X6k5k?#qr z0B7d_rqC&Vy2LyBfs&t}AK;-wU%>g5qSe*a{r5>f%gw)LCIORxNx&pv68PT}cqPC96JP2kf4(bV00000NkvXX Hu0mjf0C_!x literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background-fix.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background-fix.png index 816fe8e0644c00f68fdb632eadb5d3bb22f06aa0..ceb6c747b2f2118c1e560b56f0e3e38558249c8f 100644 GIT binary patch delta 128 zcmV-`0Du3X0i6MmBza^>L_t(2744HT3IH((1XnR&YnL|ve_1{e?@VQD(p+(ZU^A<$ zNMj7|y<@F~N-#6#oRBC~74BZ)K}0(xsMgw%K(G>>V~Ug5Y}I>i6fFM&;OhPJ5fB10000c;N diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background-fix@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background-fix@2x.png index d7b5ec07b07fc29b1903a12d98805d88ebce2fcb..e91a68c9765489f5ce927098463ca716c1e267e7 100644 GIT binary patch delta 172 zcmV;d08{_Z0muQ6B!7=dL_t(Y4egh~4Zt7_0~3@5`0-``$yfp^M{>miB%fG9Y5TEr z6c9S+JbP~$W5_wjhdh+33dUN?tMP-G34**9{a6i#tu;Xa5Qq@XQJnycqXV!}Cx91F z$dD0xMgS+zt}!1O`UymvlY9jz0!5$*6oH&0@Y?rl3W`AegeB<&wD%r!8MxN|{g}22 a7~%up34_}MDtu7@0000Rvlpg+HQb*;T8oJ?gnlNQzs21fh3Rwk^l+BJb}EAS1g}zK@!MK7*0TIt!ICQ hwln{ihMfYMc>$#&hRo;@$4&qM002ovPDHLkV1iP2N;3ce diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background.png index eb38a68cd6d4635076d09e1000eb972e759ff0b5..398d4440476ea98ee1c9fa4ffeebfe062438d8c1 100644 GIT binary patch delta 528 zcmV+r0`L8=1gQj&B!BQpL_t(Y4ZW33P69y?Mi~^vzpV*7HlDz{cp8^p#*^qmS7=;l z%}fca&fD$wO{>+qDvE+f zYg@fuuXVfK+cZtD<&;SS#h(yhpAXMi{1$ZBY&L6_g!jC1Dt|u`9uY%w0CNaIl)%Qk z(P(T0!dLo{mX0F?agGy=W7x#-C5AM+lX;M6wAtQGFfPleV-z$VkE5H{SuFF7f_7HX z&Ol}5Z~f^0`~UG1DD#cpQJ&`mRrTcuSZA(IGSaaRV|$|@IlT1y{r8wd)mpFDdsBi> zu{|-+R24mRI)9z}EX$siA)TVq*u!4=+~FJBoYD7ug1uEBDsaIy5~H-KlAUD;$bLOI0RbA`sedI#B0$`6X~f6kX}bD8GDkod z4jhX?sK~0aK)w%tXr-dX9qA!2ErO6WBDjg~K15#^2FPFl3{vQRNXFR`h?P0afd0rl zW`1SNfx=ioa9RPm#aU%;%7?5BGJE>>l5@dBhzk&a&2j5v_$%5vzyj_J^b7l|1GEb7 SYX1NL00{s|MNUMnLSTYpNB!CW delta 534 zcmV+x0_pv!1g`{;B!BivL_t(Y4ZW33PQyS9g_{B`zgwj4vf%{Wh10O)GMoe!u&F>S zKq$K|BDD)j>3?|N5J!_RDW$4v>2)R^+t2nSZj@zN1Bs$&Ps^!r9>?)br_;GwE|(mw zEzM@L(d+eYlO(y8Qz{G<=RU%553bR(L z)v#*K_^%Zrd<9~Z+EFE%UiImmLU@&+OIjqw4 z`~5d1_!P1U5r1XM>Y>~1-lu8$OqL->Uxy90^0~uTXQhu7+sMX@#s{P=;ZQgcV&PJ_ z5YVp@M$W77C?vwCKntge??zFT=s<@|(_`UGfbd9wi0STznN|}wvh)BEgA{@ff%~Ns zGb5_bI@p<0j0Ow|j_{0ah=?=3a4NuuXJXV2RkHIlw14}<2@*(6r>&D}Vy;~}v7uVr zRb53kg9t=hjD(7;Doe~K!xrEAiqy+2|3ATz@~yfMiH@CA$sjNiLKYPjFGKFgKcr<% zo7mfGm6)lmJ{fxwEs@~ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/quick-panel-background@2x.png index e6cd417d6a428d79cb4fcea3ebcd2e3bd5eec243..712178de297dd04ff54c6468267378f9f3774a95 100644 GIT binary patch delta 1184 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 o(t=*7JldKZ{`1GjmdKI;Vst00$W(8~^|S delta 79 zcmV-V0I>gaZjdBOHAzH4R2Ug!jEs!@&4312zJC46f+ohm^8f#TbTI}d3=yCJg(_*G lofc-&#P3g@Jo$qjrU3X$DYCPhk+=W=002ovPDHLkV1kJxBC7xZ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/sidebar-row-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/sidebar-row-selected@2x.png index 4be24fdd8b1855ee0936baeb12eddee4149d3037..959fe7548ba3240fd88284fa3f70cb5187296d26 100644 GIT binary patch delta 113 zcmV-%0FM8R0geHXBx+SjL_t(I5$%#O3IH$&18wn>zTY=ETPSpu8-xz6t4GwtVP+|y zCSW$A5rJ5)n2A;ttcXGVqpK{jj~MJXykGI+F@JRRj`C*K_$e@`?p_*wnpklG<$fsJ T)h78-00000NkvXXu0mjfPy{e_ delta 111 zcmV-#0FeKV0gM5VBxqAfL_t(I5o3&ujQq_&1YjaU4Oj`w*RNk$h*3@PACV zuYlt?54aadl5~sSIFC2{&hS@%Aa=4^t!A7<;yo}N4xbIf7=L@72ZO-?6y=*fj{Wnx zEK5j|1Qv@0q-pxHTrQsk_#^<6KbofbIvS0jC<-7D7bgHsJXW(ljH;?IpU+8zk0PeT z#d{!dT^F|7E#!F)`~4mcheIn5aScDtX)P|&iA=nnU8#Quf;6G!?TIex#0 nE=7w`oP^S|=9k!6wq0000ZyO=e~wrEfw6uZ#~U6);CsL{%_mh=*ME-VfUfHxNk91UvH#xJ zbqz_9Kp2Lw-|wHJD0*bUJpoMquvjc!UDpK)I2;bZlVbrxG*%O2S?&%L1Obs)A&D2l z;(NfeEDMUFfU+#1X&PwTc9f5>2$QgF+orr8EhK!6?-WJp5`Vicv(9ES0;c}zgeL%1 zRRNXjl0w(Z3w%#tlcwoP(=^^c`aa7t!hB=EJpnwqzuj(E^Z6V^QDpv6HT44KAWWYD z_ftRy8biFJwP6^5<2V`-N}T68>~=e9#^1+~G2$MgTeMv>`$%QxXy?GzpbKaS` zcgA#meJ$f`=5Yp3>=Obe3K(++#{Lno4l96YH2MS);{oR1M}G;S*iiIjbvW}AWzS?X z?WR(xkHa`;8WaWrJke-0*0JfmVK&su9N_wDHk&s=#CAWQ`VtU{MBZcZ8aHse-4YIm z#qalv!{HEY-f!RkK88b*N~I$Cd|on{j1-GS)rYNgI{i%TwAVce0QenDer&N=Bo>Rw z!oq?;1?}r~*?*_1kB*My;NU>6uCD0gCy=n(&DpL5;G|yy>}#vlD$C2u;`Mr!jV>-O zq+YKJ+18^`l{TAArlzKZ2UjQ*B$-Ug<>jS-lvhy3n|8lDbPV7Yloy;BkH^L1@kqH` zmb0@nsaC5B5oB18g{Ms)=_`Gv@5DeOCSp@MnoCC%z<=qlV|>Zy^U3`Dyquq(D-3GZ z7@|occ=E(RET^ZZOHFKVwE#K@z~CcJ2!%oli?v!!JKs=k`c5Sf3kaa^#5Rxs#(8nM zT#B67HnLF(#L_eco7!RBqJTgk;FzACp5qIIFFHQwqhQ2BOn67k5o0!+t*E$K2vBdb zrhG<)0DodpBNyHY%ouO1g#a>x`2QPB-Jx{scKgjlBR3enn}?a*-{0>|KojI1>J^3( zzCB05h=m3ch^@t16wn;`jCv)P%P9#^%WQ*oh=qncU{`5qE?qN0d;on1Tk*xkMZ>Fr z;qUP9kdS{M9sZjYX$a`101m%H*5hwr-4Jj^VCU zD#^*oiSlXObZ#5cU+d*)PX(w8Kmr#(N4cxsGf4dner^Ih|7rBJfdr^4@kp%V)E}a+ z*G6Ar%gol-5iXFiNL+8!UeS XBA3B%d#M{h00000NkvXXu0mjf*_M`j delta 861 zcmV-j1ET!o2J{AyB!4?eL_t(&1?^Z}YZ^fmy?!Q$kQfcA20@a% z(}$GO{+yPkd1%t6f1?nJPzqA~0ZEXGpu~hEh>03w&zbB7S68DCExYQ4*_pfZ;XUWx z**l}7)oRH&i*ua86Z?dKNd$~J17p7h+=B+d%F4m|9==IhT=i7k~CmU4khhy zIQ9_C5|E1h*u0swXogP*%xE{VtE z5{tz;=UQ6N<$rRL&1R+9Y|_qeAYt9iU{eB?mX_WkBlTwPrm2p|boDRc^*L>p-(WKjzr%XoU2h@>lI$u#%XC31_H-n4xboSd8pPJqOn#c}xdRMf_TS$_nu`xD9*&(r)4`2GI2DfVgd3_uJN zcz;VZIv%DGz%lUtE+)O?tuGplsz`v|G2NOfKX~+fKA%@#f9PKj1t~LYdlJCG$Ot>A zQf%`I%p)bEftL-dk-;>_)J?DB*sHF_v>URE(BHUic0cu>9^ptxE&}U*xti$V1pFMGaq9K|*W$M(w=TDtFb>PH_4pC!6!-Gc?b1Zl-dFUlD-t^FuxayO1 nVq?Rjo*09Mg|-Ff?kT(-cw^+5RLCQ95JBge(6XOt4r!ECtAo$u%Ag|SgFW(UaUoA0s|0uy85}S Ib4q9e0Oy|(L;wH) delta 55 zcmc~VoS-6N;pyTSq9K|*W#Yu2=N;4%5|fjaCtAo$X>OV+be3VIG~=bqMTN!;K;Y@> K=d#Wzp$PyWXcG7U diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-horizontal@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-horizontal@2x.png index fc9f04416e3eaaba3848eb95a8a5a1c8d3f45e4f..00c1a3be4e74309bad4f081097dc5b9cc226e6cc 100644 GIT binary patch delta 115 zcmV-(0F3{S0gwTZBy3knL_t(Y4egXM4geqs#Kgce`2RCCab$-Mo*RpoD-D*JX8~^m znJ>~K0?FnYn;FvBso?)mtMQoOvU&{le^yH_$<;;xlM4&h0cn$|eykl=B5gB<`T~an V0r*5RXea;x002ovPDHLkV1k?xE(-tv delta 114 zcmV-&0FD2U0gnNYBx_blL_t(Y4egY%4FE6*!wN!N!T)czQpfJp1K|Ly1PiOGy?}9m zd6AKs2%Af6W=P_o!1JnB;%|n_>Tgxgvs!XVuC@j+xiGLENSjRW;(<4IXZ*sdK8E}O U`w2s*-~a#s07*qoM6N<$f_RTJ`2YX_ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-vertical.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-vertical.png index 1c958be9c6dc48304282fd68669543bffeb57e2e..dbf4dd0b40d28fda3930089269b3db66f8b32ef3 100644 GIT binary patch delta 74 zcmd1GoS@=r;OXKRq9K|*W$M(w=TDtFb>PH_4pC!6!-Gc?b1cp}FeWT=&_BE)O~^Ul e}aeXoB$x<`? f;>MhFf<=PizANLl(4~s=7=Xaj)z4*}Q$iB}euW$# diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-vertical@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/standard-scrollbar-vertical@2x.png index f560c3f077de919a47ba79db0d188a2ac48af5aa..d604dac38f308eb3ebf3751b592cf92e2549f87d 100644 GIT binary patch delta 128 zcmZ3$IG1sPN@a?ti(`lf@7qg`Tn7|*TmqG!NbLM?xRvWJ4{P{kyVvd;ik8*Nta=yz;&Fb#rTbymPx$)@+9Pr`=W`3|QTmdKI;Vst0Iw`NfB*mh delta 131 zcmV-}0DS+Q0iXepBz$K{L_t(Y4eioF3cw%?1VEERUXlNQvo-;t?8VdCgDYqZ=n8|D zna_AfL{u4O)^qK5wa()i%=q^1-ud3QepvMIF?3OjnkJynMWe-FQKJa07d4F*gGG%Z lv|iLSS_~F7iqQJ|s3&Ly0oM`x(is2%002ovPDHLkV1l_UJNp0t diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close-inactive.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close-inactive.png index 865956a08cb943d16d8b9a66763cac2e0b063af5..c45c727c3002b6689f0a4dcbc711fb0f2a0554b0 100644 GIT binary patch delta 283 zcmV+$0p$Li1C;}iBmrEJB}fQ}h)BT3jT_UEbsvAA`u_d<<1=T@w3wQjHgj@v ziUHMK{rU5!X<=dEcbo=*B<9SSBLh@?7-*XU5Hmn6xX#SXY@e8z_!?O=h>Z~jsi~=7 zj~zSK#?H>p0JNC_NdMolV@GpRQqoU!%^*HT7y$ii2lRIf8ygz~5dQ}{o6*C=qx0p< zm+yapF4_xp5$-Tpy?Qmzj~_p7ad2=*0u^8V^XE@23k%B{pe9h5JOsK(Gc+_5B@9r! z0F?X!)Z7fzd>N?8F(M-3JQEX>J&=C~sJRneGuTU@Ktji>R;^+~76bA*k@?7MMi7S* hfZ_#m3>tU?0P|R>)}{Q&NB{r;07*qoM6LruV1kRfe2oAA delta 290 zcmV+-0p0$U1DpepBmrZQB}fRcuyDYpO`Fn@bsv8q{~}O<()aJ*AD=yYw#CH6q?wbG zQw%76_1CXoriF!t-*FlMl9)4Rj?BM*{|*CfQvmTn0I2yoD=VvgVq)TJhzNqj2!qtr z)UQX59BBh;U;x_80HptK-@d&$DJkhELK}jG5e6B_$p9wERbFU^b#mc9`f??YKDe}qJ#m8 z7l4vqI5;?(nV6U^1F>U7M8x?&fBx7rGc(`e=H~81*9`Uu&=yc2q2N`kRDK000UA07*qoM6N<$f=z9M)&Kwi diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close-inactive@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close-inactive@2x.png index 0e711454fdbf73cd671e87aa7b3ba5a60d0fd656..34ccdf5a8b1ff93d77e0442218bc94dedb184751 100644 GIT binary patch delta 778 zcmV+l1NHoq2B8L!B!B2hL_t(o3GGx(YZ_4$o$*^zBxcc%1WQ1Q{h*6%li%P%t<9nf zMF~|ql0_QqB4VLR2wjK}sf3Vb(~Thn`VYD;p)Q4Xk*wVn0*N}>a~_$ZA*1*qgJXk*&GSFG*5`d%o1kGl%@%j1r zubjCaB%y(?tgI*t3ky4`RO%IMNWzQRPzKMVCOWR-1!|LyN^C6P)$Z>2#!{qod7o zxx7p7xm-?}nVE^(Y&O<`1Y45m!uDAvllem%J<%n=gIil$X}jHC#jo7$b_EH}%mnoJ z7`XIqwNHEQV!s~&km}7}V!ZbJ!4SS+ygdnOP&1Q3h(Xt>JF$e#l>oppUkI7{6aK!PE zYEuLNARZZy$McYYmki6~)`zIo{vRfEE`~39u zG`6_7D5`yLZ!eALtH_pkCou7`Y6JO2G#WL0h<|jiAQFlEg2NbwN^nLF=l{VO9zedF zot?eIUY|A)fW80pz9KvrO3;$QeuRU|&~x~=uLt28 zK2KmA(BJU+$vq>Y`ve+zFc^G>fXmbMmDn2i)MpiI{}l(xLP`LE?h$l%cQ+mg1U}Pp zeVK#?o=&Gz@HyJv-VVWsB<{lMitZESa=DK#m+OtiVlj=!V^gQoIVhLQ$B29mx~nXU?@mtL4{8En|u7`_xn@%d|o4QJA(*Ar-DKBY6>depRI;g zjDg%LTNGA70g7TA3kcC66GWrY9|#zOs&oX`hKvTz>qR1wPq_7Ke4Ee*z61xh zJ~+=yGe8GQtJQh|a|C@E3WfgJ=gnp_3GfnlJO}t!o--skAZEOtZ8RDWZ^P$$3_}?( tbdwu8No(MSF=slqAh5{5A_H^Jz#j$fb>r}#r#Jur002ovPDHLkV1j6-YYqSa diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close.png index 865956a08cb943d16d8b9a66763cac2e0b063af5..c45c727c3002b6689f0a4dcbc711fb0f2a0554b0 100644 GIT binary patch delta 283 zcmV+$0p$Li1C;}iBmrEJB}fQ}h)BT3jT_UEbsvAA`u_d<<1=T@w3wQjHgj@v ziUHMK{rU5!X<=dEcbo=*B<9SSBLh@?7-*XU5Hmn6xX#SXY@e8z_!?O=h>Z~jsi~=7 zj~zSK#?H>p0JNC_NdMolV@GpRQqoU!%^*HT7y$ii2lRIf8ygz~5dQ}{o6*C=qx0p< zm+yapF4_xp5$-Tpy?Qmzj~_p7ad2=*0u^8V^XE@23k%B{pe9h5JOsK(Gc+_5B@9r! z0F?X!)Z7fzd>N?8F(M-3JQEX>J&=C~sJRneGuTU@Ktji>R;^+~76bA*k@?7MMi7S* hfZ_#m3>tU?0P|R>)}{Q&NB{r;07*qoM6LruV1kRfe2oAA delta 290 zcmV+-0p0$U1DpepBmrZQB}fRcuyDYpO`Fn@bsv8q{~}O<()aJ*AD=yYw#CH6q?wbG zQw%76_1CXoriF!t-*FlMl9)4Rj?BM*{|*CfQvmTn0I2yoD=VvgVq)TJhzNqj2!qtr z)UQX59BBh;U;x_80HptK-@d&$DJkhELK}jG5e6B_$p9wERbFU^b#mc9`f??YKDe}qJ#m8 z7l4vqI5;?(nV6U^1F>U7M8x?&fBx7rGc(`e=H~81*9`Uu&=yc2q2N`kRDK000UA07*qoM6N<$f=z9M)&Kwi diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-close@2x.png index 0e711454fdbf73cd671e87aa7b3ba5a60d0fd656..34ccdf5a8b1ff93d77e0442218bc94dedb184751 100644 GIT binary patch delta 778 zcmV+l1NHoq2B8L!B!B2hL_t(o3GGx(YZ_4$o$*^zBxcc%1WQ1Q{h*6%li%P%t<9nf zMF~|ql0_QqB4VLR2wjK}sf3Vb(~Thn`VYD;p)Q4Xk*wVn0*N}>a~_$ZA*1*qgJXk*&GSFG*5`d%o1kGl%@%j1r zubjCaB%y(?tgI*t3ky4`RO%IMNWzQRPzKMVCOWR-1!|LyN^C6P)$Z>2#!{qod7o zxx7p7xm-?}nVE^(Y&O<`1Y45m!uDAvllem%J<%n=gIil$X}jHC#jo7$b_EH}%mnoJ z7`XIqwNHEQV!s~&km}7}V!ZbJ!4SS+ygdnOP&1Q3h(Xt>JF$e#l>oppUkI7{6aK!PE zYEuLNARZZy$McYYmki6~)`zIo{vRfEE`~39u zG`6_7D5`yLZ!eALtH_pkCou7`Y6JO2G#WL0h<|jiAQFlEg2NbwN^nLF=l{VO9zedF zot?eIUY|A)fW80pz9KvrO3;$QeuRU|&~x~=uLt28 zK2KmA(BJU+$vq>Y`ve+zFc^G>fXmbMmDn2i)MpiI{}l(xLP`LE?h$l%cQ+mg1U}Pp zeVK#?o=&Gz@HyJv-VVWsB<{lMitZESa=DK#m+OtiVlj=!V^gQoIVhLQ$B29mx~nXU?@mtL4{8En|u7`_xn@%d|o4QJA(*Ar-DKBY6>depRI;g zjDg%LTNGA70g7TA3kcC66GWrY9|#zOs&oX`hKvTz>qR1wPq_7Ke4Ee*z61xh zJ~+=yGe8GQtJQh|a|C@E3WfgJ=gnp_3GfnlJO}t!o--skAZEOtZ8RDWZ^P$$3_}?( tbdwu8No(MSF=slqAh5{5A_H^Jz#j$fb>r}#r#Jur002ovPDHLkV1j6-YYqSa diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-inactive.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tab-inactive.png index 3ff477308860c9f5a552b38d041e70771f0415fc..00ccf2dbe633eb9858c1ec149ee0ec640f9981a1 100644 GIT binary patch delta 179 zcmV;k08Ibn0nY)DB!8AkL_t(I5$#ez3cw%?t7GU%KjQzN;zzuB>gYgdJEvpF24g*F zwIMdK1`&ZG#)u(=Js&MOXGkd-!mS+ay@yf?zdGd3IUs@>g1}k}wWA8qJ7IV%{BS4# z2>{;rK+FMs<7f`_SI?Dj<#mmjD?zhsy)BPE(1DOZ9*lg@bu2Lly7-8Rb0xG|9^IKM hx_czT#2)ahwgGgvPa-vn8WaEk002ovPDHLkV1kjCO;i8? delta 200 zcmV;(05|{70ptOYB!8<(L_t(I5$%#e3dA4`MPnIsr5Ex3NAV)=Tv>iWNd5@QOjo5Y z+9dvbA0e*yE;u>oo>IEv)u+}fr4$FO(puA{uQ-GtFc`-ec<*Hp4FUh5(dW>~ zFY#+2@_-qbMZzAe7KwMo%wVUPHTM!Q-cD`^fX9ez!uMtdEJ5DM?%Q6BYy!6NklskUAMxjI)m^%B5LRR@Sm zwe=Fn)w$BCmjGs}IzU{it(QQq&Xtb6iYWnwF7~EVI>KnWl&)fRsWiWfjf0Q9h_ yh*f@Z0>ulEBPB>hk32PkMg>6VU$*!Ntbh-@Jy=qO#Gz>b0000Px~@yL z^E_{Reww#!o9f4L-0h?Zb2#Zmd^hYz#+3%fV#8Moq)Rm_d`exU{7@h zK;o`Qx1bbyONSc3iY)=uosD`8wFNo@Is=qIsSl8)Lk-}|W(lC~Y;=DMs=HTck#Q#= a10Q5?Qdr|~a}Bfr0000!zU delta 111 zcmV-#0FeKR0gM5VBxqAfL_t&-8MTnH4!}?ZMG=3)|9_R;AklVww+87@o15bhY-Sf7 zr6O{)M9Rz`0~9>N!!d{v>?1qzi*#Q*2<>io;XEueflznHAR;h$OSP(ixB+?91_gEZ zCE%H=g0Kh$JTUzWp20xdq=3E;v_v0pMgHr2hLd-io73zYWmN@#!v02428#%&fwpO5 t49u_=yeDFF&L#sbK>?YGHL$Dq0$1c3QLG?giU0rr07*qoLWm;-K;-#MS5t+ikebWGcR sff>4?pNK`okb#zYsQ>@~ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..fd585a56df4d9a49e5dfe1f54e6d8a78e0237b38 GIT binary patch literal 332 zcmV-S0ki&zP)3M; z|NlP&rGSYP0~jGd5lFEj>tjLY;9~O@V-8U5JD7Tch5$7S0Ts&x z1_qiK85!lVv$N~px^-(I%m5IL(-5F$E}*=kgM)*rqoZSvu&}Tb3kwV5@87?X^@G`1 z4FMX!tF5hV8Wa?iDkUWq%*MvX%EZLP02Bvn;^yW?F$k+63l}cr`}_CrJfO|;Ak82Q z1ONX0gE8SWLKZH>zz`P~_mqQ!!vtu^WMCNl0tP4pGDcDgW@0r2Boi4K`38vde*OBT z158V6$ub0HTU1ok?aqp{fa2iRL#+6T!Gu(jA< zU?*0V0kNqFWxcA#Fr*9wth(P23ZnvP-Y87+2+=*$L*~MbvrvLB$&L99Vk?kft#H{6V zIa@3iUq>R5`-WjC{5;I(b8bm(YY+f%&3YwdD%uF!{N}v+rC56 zBPOPR061F(Axse8D*{pIsVb|0d9dG>!zeRCoCn0aW)78ZxBI!%=^R14jv@TUYG6KW zC?U9RWt^9JN36-vZnr-slga#KGC9UK;wOcI!Js1M#RK>~3lfPmoYW!@1U8Bm>H&1Ulr!fpfT16sBu2xrI%-F!AM&lXg z=iY2KJLM3wRia%Y&TaJ#LFx)cyMap(ODYmHOnJiq^goUPJdoU)r68u{{{ P00000NkvXXu0mjfpA0Zq literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left.png new file mode 100644 index 0000000000000000000000000000000000000000..58e1d80b2187607ec3ae79c380e7703911915d7d GIT binary patch literal 334 zcmV-U0kQsxP)3M; z|NlP&rGSYP0~jGd5lFEj>tjLY;9~QlW*E_iz!c`?<=xK7$q4~^!v<*BabWB)0A2SFCJ&}TDV7AxICbjOkeM@Qav|$s gqk literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-left@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..2f70fb660bf9b2a02ca252342424f04b403cc611 GIT binary patch literal 688 zcmV;h0#E&kP)V^=pxG*uT9x2!)gm;cz+F&_C=$IPh?^Gfa^tcu&?jiyqEFB+ z?&|I@EdoIziSVuef8fX@&dl}YUhbj?j?c__o;m0D{Lh(DozLgWWf_VIiirp02hP%4%3%;mYb2?zl8@LURJ1OfrA zTCF}#BoeO&gTZb1e!0cm83+KbA;|&EE1_6MHXjxd8 zQA?##u23kvjYJ~%bX})QnM@}3?DexT2msjV1h@)Q^ZESsVzKxJ{ZKXxL&H$8N8xvB zHk)}d*lQbsAOTBY!L(#Dd8J$~Kaa=b&(ISlRUm*E%R(r>u@(q|ClHXPhr?mLR;xWp zr_-+hU@@7fKnNLBWal6f8~_ip+3Zm$6gr>+0wL?mv-HZc-+8~5BE4R3A4&T-f*8)L6mBtgIA?+sgdn3+T8vibGaCbVLukCjG5U!_C4tnP$eBYzdsOKM!N(s+<^!xqDXf*o626xMKAs}wao&IwxjWfwvuK$B9tMnHO WQJ{r8<6y`Y?T61O`a}}AsV7*10DGe2ncNPEfo59 z$-b&V{ztzZ2fMC%q>C_LX=0k$<}~pjuavKIZZI(Dow@oWx9YmZere&gTa$Yp?rL)8 z_{pYYrWWMoqzopr0C!tN*#H0l literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list-hover@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..eff9636dcd09c66d9e43e6bd793f280f648d64d5 GIT binary patch literal 307 zcmV-30nGl1P){r8<6y`Y?T61jh-%!AsV8|fByfsXI|RCdY8e? z%`LgaFvae|rOhX51f&m7XliOQ*3pqTlu%p$?+@d}o&-Lj#)VN14h@YBiXOKVecZkn zEbJ=R($+q|Vbi9h8*D;9{{A-aPeKt}xqCpko+3UHx3vIVCg!09|TB!2kdN literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-list@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c11f5bcda46955daf17bd0648090bf14f382788a GIT binary patch literal 289 zcmV++0p9+JP)*fcmT?r$IVvG!2_S$$aX^F+ zFYcMU{K@!om;dJehmmm{XDKCN#KdtNgmH#=vW{&{(=^n!E#Vpy`@Yk*ZI73*K!#x; zrBo6l*4lhPAa7I`D|56ZW{uz;uB!2yI!#j+)mDeM_B#?1Jr13M; z|NlP&rGN?D07gbeMIdHG7stoPFeElMHk6f>)c|OY6h4i}axBOk5L;JQw^L0`E&ANK za}$9EodF6x0eblzx;kV&h9R7soD6(?e0t8#&T|Y54EF8czkdqQusc9a_kf0dL)Hvq zGolCV%$YO)b8v7lfW-g(`^WI>*RS6%Uc6YlX3d(}SFc_@4HW+j(+s0ATmlRa1~xV} zm^=eJJ3A{kH+MuyNr@E`6O$ECg0LYV13(7B0MO>&fByWL!NS7Q6d4)$1}2V7W4HvU z7;XsAD{FuM{#_Xr6?Gd~6FM8i5U4AT|NZ;7G&D5yD7pr$d{6))<8|xSg#!8D07n+b o$Hs^qtTqu4#7MCO)MJ$f0MoyU3^5_7y8r+H07*qoM6N<$f?G_6ssI20 literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right-hover@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..cc34076823da4dd632cae71b34d9b74aa4102eae GIT binary patch literal 723 zcmV;^0xbQBP)hgBUIY1Q~=N zvMd{*=6?Yz1Ob2r8Uc(SDsK}=2oes5O>VdQ3IMJ`Z-V9ewqQ)6z_D1&?)7^2JDpCk zR4Tn4kH>Dn?7*jsB0mxN>fMt?U6ae@uKE4`p3!KOhQr}ev)O!FE|=dg4;f)ShW`(} zhn{CI%m^JU7K>yunIxOdwi}H`p9O=#5A}NeNv&2p!uBpQG{E{5KttMZDw-LIL?S)H z7z_r9*_%$M(quB3_51yI)oS&5qtW<|!#Rx3pkH7!+XO}klF#QCS7WtWC0-AOFtgc= zFcNBJ*zI;-S1Og4tyb$Nwh3_xn`7GQ5lxUlAkbR{VQ|8%0-=6R+wJy?VzKyUFc?f> zLhZq(NBea|*uD(FE1@zYin34p**5 z9YDzr;ZRH7cs%|HdWh$@VY_xl)&^l#M8bUzU)0T_eqbwhPzBQjS-lpO@8T=>smpHq zj6%Mf&1Uz{vlTtl1mTq^FiId^hXSzdpp(&P^uXis6cdTWM6uJeYJw;bTq#UHKNAMm zFhCnW#`-Xm$^6lT6@u99_TO)=WQG77XwX-Hx|dF;zv+4Ni)@7;bmH5yBd8o=&;w-P zPAZl9cu~+zo(n;IKHr%0f?2@O`UHTzWHMRZhj-;6A#dge-7kZbgJ3 zPN(w>|3z+~9r-)5RT2KyZ1rSSa9z_+<@;B^{s&Q~^B1?)9$k$h{^$Sz002ovPDHLk FV1fo$Pn!S$ literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right.png new file mode 100644 index 0000000000000000000000000000000000000000..16f47a10a9c7e922e10ba2cf91132e50b56f8462 GIT binary patch literal 342 zcmV-c0jd6pP)3M; z|NlP&rGN?D07gbeMIdHG7stoPFvQQ#FBGW30BDXBK8?t7jOgAeE-wE6`}gll*RNln zxM$CvGeE^pfNI_$YeHwUpbKzta4_)l^6HtJo6k{KSKoK~^yw);SKR@sy9YGv8@hTB zAHxt978V9JHZ}%UR#rxKc6MiXcXvBuW8<~EcI}!CH0(6cpwH+AVHg6`3^D+&4rm}N zH#c`gYHF(0pFe-BfD(ia0l5T(K>!p<%*@QdAo@K6=z^y5^71!e2{Z!3B_IPpE@5I~ zV)*y(-&!E9EGjCxjiwKUi(v>b96&BP4s?2HPEO8I6xFymKxZ-_<7v~Tg#!8Ds7DsZ o$Hqt}xGcjbj*(*VDaIuS08kc$7iz5>{r~^~07*qoM6N<$g8eRu)c^nh literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/tabset-right@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9c25eb2c91d383a87af5a635ce3dfd086857209f GIT binary patch literal 707 zcmV;!0zCbRP)Mic29@jt5`rWW2_+B+cmd#t-U7>2yI_PM$z;-(&*%3unamxh z)2RR=2$-AD%i9K$M7JiF%UubDLIa1x!KTycLATp|R&CNE7R0>=YDFjtr$437|ij(WY`t6Hu0URBiv_K^;-Z@|x(jlx?)hba=I zlp3zIKCK(79p?3VFU4Z9-BPLaE}PBXHgjt9URbO?pKryvkpY4uq38fAl1il>r_<>s z)_3vPazun6LMy zjg9RhP@&SdZ{HptKYqN)+SG2r>kw z5NOHOpFe*(CMG7nhKa#wMr0Sj1lO%wCj%5e$i&2?1hN^39YaGyF*JkJV+0~dFeoVK z3D5=hK)eN?W{_H}E&xdo3$VI?Sj`}H!@vLlI(=7(y(?S100000NkvXXu0mjf`vrAn literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list-hover@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9825e0ab96034966107c1ae2d254e65e7c95b320 GIT binary patch literal 558 zcmV+}0@3}6P)nc(gN6Y$-|4N9tC$ny7drr* zL{|VBosqOyEM}t7Xqz+h-y$^fQmIq`<~g6wCxD@dQ_O~8cm>sJ^(hz(-aDO6VZB}p z1Jj37t{N-}F5Dtek4u?aKWqC85P9Gi-*fR`W0j*Z+79HL|E|Q$z%WAa} zMx#-qR;zW`y;7+ZB9TbO?RGO2R1quuy~<{@pUR@nkFEgLwcG8S*XwPJb_Rsbmwxp6k^pS8T%6ARfl)GWhj8iZnwJ?L2?EiVerHZiolw@TrQ<_I&F%a zb*2Om*z5IjKA*1%Ld6Lf!e!-Jw3E;0b19WdnI>nwX#oWH`~9sG+WVp2xlHn8GI_wV1Y1oDf4vGyB94{|g%Ha7CG zu&`YG_wS$5j~_oCU$}6g$ zL4rWVPk@^3f!^7IPcuj@Ru_OIhy_?(K&)nvx?x}d03Ehh!r_8%*Z=?k07*qoM6N<$ Ef}oUsHvj+t literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Dark/text-field-list@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..f16b2c0f664a777c027fd05809ac9f925ce21e0c GIT binary patch literal 537 zcmV+!0_OdRP)1=5DanGqKz^e!DeYWi+)b~ptk-2!8XpL zb3PnM!Fdr#Fc*gF9q+m4+y^5Z$5D=H>M_a59Sevj5KrL$Oh5>6?)S|IlCxh!1o;x8 z@FjpbJZt2C>=$8-oNKr&o)vOF`7Vh0uD~TPl}a}#nA^rXwO#UU&I#g+odl1-QveE` zk+f2&)QZL8D?Q`iB3$x%z1~I6OSxRWLk>lp!uy)<3VOYsme1!O5{ZOj+qSY?E}sp< zSfKJ64;#of8jWtDP^hV@s+gwftX8W>UDu!RpRaie7>~!d*ylUC5h$$iUg#-eKA$gG zz1eI^@?3tt5L}#3m}jp;J!PZj=?ypg;!}JnxO)1R;M+~YV1VcosENJJP4TDG=LEU?AthK73Qi^~?9}hSI#5t!5 zYTAgp7H)o1K-#rA^P2W>bk0iGeAK8bU0D3_UDxb&=jFi%O+9-Y*aykFUh>fcdu>+t yt+aC9f-AS(39VOqKpUEJIXT3C+K@wUL)-!0rCTfj delta 196 zcmV;#06YKd0qX&fFMos}3`Hprc3y;|Ip>k1aS?YcU}$3##|ud$M0_Trh%tss z@tG`0M|OWoX3J)ES9shnM~^_-<`% yhVz~}Zsno2-5p!+fDw>MKPMM_8z6$n6E9!fZzlY)`c?G+0000piDZxi zIsmc9LLP#r=$WQzwy83jX_^oyMHMF$8H5n3R5eq=WOkI$c7KHGy0$?Gy@CbXL`u7d zmSu@}InJGjQn)38e6hCv5k{WnwBgOd)6zJzAjut%)n;OB)$2`m`K59|N@>%+&3?SBFMkuL*Bj}+_x0000)WVUzGelUX_oTkf}52q6#chwSGFAOeiPW6-6>$^@quxP ziK?Q8z>j@(xy|-j_rJFV?<`bmj(OJ>aR0mijygwKpLo@`EEdNr=iTqP9=gKK=maCH z!<$_Ztq=UPFPGjm3~}GA!_Blc?y1lvo+&}SE2n5C8gZ+d^w$U6-nNHhp;enmY3sJ! z&k9vslI4e|RdFV6X>n@4!l}5g{!ZSlb&T~Qiv_x7t@yywx$qE^%l7FKNSt(W=SM|> zXSw|arfcgoY~MR)o9$$Q_Y-v7V;0O8@4d2d-XZnO-&1Dl%~8qx?Uby3)9X^1ka1c} zOYF2{caNwfjm5v!BWKM$y-``kaLSR3#(IaYKRu8ke1*||>iXBSF1AHZa|8QB!p^B* x*5X)p#xmyJ$5t`f>)T`{JWTjwas7|n!ToMh`m9>UPxKjpz|+;wWt~$(697NooKOG& diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light 3.sublime-theme b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light 3.sublime-theme new file mode 100644 index 0000000..3fe7ff0 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light 3.sublime-theme @@ -0,0 +1,1160 @@ +[ + +// +// TABS (REGULAR) +// + + // Tab set + { + "class": "tabset_control", + "layer0.texture": "Theme - Soda/Soda Light/tabset-background.png", + "layer0.inner_margin": [1, 7], + "layer0.opacity": 1.0, + "content_margin": [-4, 0, -4, 3], + "tab_overlap": 5, + "tab_width": 180, + "tab_min_width": 45, + "tab_height": 25, + "mouse_wheel_switch": false + }, + { + "class": "tabset_control", + "settings": ["enable_tab_scrolling"], + "content_margin": [3, 0, 3, 3] + }, + { + "class": "tabset_control", + "settings": ["mouse_wheel_switches_tabs"], + "mouse_wheel_switch": true + }, + // Tab element + { + "class": "tab_control", + "content_margin": [12, 3, 12, 3], + "max_margin_trim": 0, + "hit_test_level": 0.0, + "layer0.texture": "Theme - Soda/Soda Light/tab-inactive.png", + "layer0.inner_margin": [5, 5], + "layer0.opacity": 1.0 + }, + // Tab close state + { + "class": "tab_control", + "settings": ["show_tab_close_buttons"], + "content_margin": [12, 3, 7, 3] + }, + // Tab hover state + { + "class": "tab_control", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/tab-hover.png" + }, + // Tab active state + { + "class": "tab_control", + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Light/tab-active.png" + }, + // Tab dirty state (close button hidden) + { + "class": "tab_control", + "settings": ["!show_tab_close_buttons"], + "attributes": ["dirty"], + "content_margin": [12, 3, 7, 3] + }, + +// +// TABS (CLASSIC) +// + + // Tab set + { + "class": "tabset_control", + "settings": ["soda_classic_tabs"], + "content_margin": [3, 4, 3, 3], + "tab_overlap": 24, + "tab_height": 28 + }, + { + "class": "tabset_control", + "settings": ["soda_classic_tabs", "enable_tab_scrolling"], + "content_margin": [6, 4, 6, 3] + }, + // Tab element + { + "class": "tab_control", + "settings": ["soda_classic_tabs"], + "content_margin": [22, 6, 22, 4], + "hit_test_level": 0.5, + "layer0.texture": "Theme - Soda/Soda Light/classic/tab-inactive.png", + "layer0.inner_margin": [18, 4] + }, + // Tab close state + { + "class": "tab_control", + "settings": ["soda_classic_tabs", "show_tab_close_buttons"], + "content_margin": [22, 6, 15, 4] + }, + // Tab hover state + { + "class": "tab_control", + "settings": ["soda_classic_tabs"], + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/classic/tab-hover.png" + }, + // Tab active state + { + "class": "tab_control", + "settings": ["soda_classic_tabs"], + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Light/classic/tab-active.png" + }, + // Tab dirty state (close button hidden) + { + "class": "tab_control", + "settings": ["soda_classic_tabs", "!show_tab_close_buttons"], + "attributes": ["dirty"], + "content_margin": [22, 6, 15, 4] + }, + +// +// TAB BUTTONS +// + + // Tab close button + { + "class": "tab_close_button", + "content_margin": [0, 0], + "layer0.texture": "Theme - Soda/Soda Light/tab-close-inactive.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "tab_close_button", + "settings": ["show_tab_close_buttons"], + "content_margin": [8, 8] + }, + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Light/tab-close.png", + "layer0.opacity": 0.85 + }, + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/tab-close.png", + "layer0.opacity": 1.0 + }, + // Tab dirty button + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["dirty"]}], + "layer0.texture": "Theme - Soda/Soda Light/tab-dirty-inactive.png" + }, + { + "class": "tab_close_button", + "settings": ["!show_tab_close_buttons"], + "parents": [{"class": "tab_control", "attributes": ["dirty"]}], + "content_margin": [8, 8] + }, + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["dirty", "hover"]}], + "layer0.opacity": 1.0 + }, + { + "class": "tab_close_button", + "parents": [{"class": "tab_control", "attributes": ["dirty", "selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/tab-dirty.png" + }, + // Tab highlight button + { + "class": "tab_close_button", + "settings": ["highlight_modified_tabs"], + "parents": [{"class": "tab_control", "attributes": ["dirty"]}], + "layer0.texture": "Theme - Soda/Soda Light/tab-highlight-inactive.png" + }, + { + "class": "tab_close_button", + "settings": ["highlight_modified_tabs"], + "parents": [{"class": "tab_control", "attributes": ["dirty", "selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/tab-highlight.png" + }, + // Tab close button hover + { + "class": "tab_close_button", + "settings": ["show_tab_close_buttons"], + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/tab-close-hover.png" + }, + // Tab close button pressed + { + "class": "tab_close_button", + "settings": ["show_tab_close_buttons"], + "attributes": ["pressed"], + "layer0.texture": "Theme - Soda/Soda Light/tab-close-pressed.png" + }, + +// +// TAB LABELS +// + + { + "class": "tab_label", + "fade": true, + "fg": [50, 50, 50], + "shadow_color": [235, 235, 235], + "shadow_offset": [0, 1], + "font.italic": false + }, + { + "class": "tab_label", + "parents": [{"class": "tab_control", "attributes": ["hover"]}], + "fg": [25, 25, 25], + "shadow_color": [245, 245, 245] + }, + { + "class": "tab_label", + "parents": [{"class": "tab_control", "attributes": ["selected"]}], + "fg": [0, 0, 0], + "shadow_color": [255, 255, 255] + }, + { + "class": "tab_label", + "attributes": ["transient"], + "font.italic": true + }, + +// +// TAB SCROLLING +// + + // Tab dropdown + { + "class": "show_tabs_dropdown_button", + "content_margin": [9, 7, 8, 6], + "layer0.texture": "Theme - Soda/Soda Light/tabset-list.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "show_tabs_dropdown_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/tabset-list-hover.png" + }, + // Tab scroll left + { + "class": "scroll_tabs_left_button", + "content_margin": [9, 7, 8, 6], + "layer0.texture": "Theme - Soda/Soda Light/tabset-left.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "scroll_tabs_left_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/tabset-left-hover.png" + }, + // Tab scroll right + { + "class": "scroll_tabs_right_button", + "content_margin": [9, 7, 8, 6], + "layer0.texture": "Theme - Soda/Soda Light/tabset-right.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "scroll_tabs_right_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/tabset-right-hover.png" + }, + +// +// FOLD BUTTONS +// + + { + "class": "fold_button_control", + "layer0.texture": "Theme - Soda/Soda Light/fold-closed.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0, + "content_margin": [8, 8] + }, + { + "class": "fold_button_control", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/fold-closed-hover.png" + }, + { + "class": "fold_button_control", + "attributes": ["expanded"], + "layer0.texture": "Theme - Soda/Soda Light/fold-open.png" + }, + { + "class": "fold_button_control", + "attributes": ["expanded", "hover"], + "layer0.texture": "Theme - Soda/Soda Light/fold-open-hover.png" + }, + +// +// STANDARD SCROLLBARS +// + + // Standard vertical scroll bar + { + "class": "scroll_bar_control", + "layer0.texture": "Theme - Soda/Soda Light/standard-scrollbar-vertical.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [2, 6], + "blur": false + }, + // Standard horizontal scroll bar + { + "class": "scroll_bar_control", + "attributes": ["horizontal"], + "layer0.texture": "Theme - Soda/Soda Light/standard-scrollbar-horizontal.png", + "layer0.inner_margin": [6, 2], + "blur": false + }, + // Standard scroll bar corner + { + "class": "scroll_corner_control", + "layer0.texture": "Theme - Soda/Soda Light/standard-scrollbar-corner.png", + "layer0.inner_margin": [2, 2], + "layer0.opacity": 1.0 + }, + // Standard vertical scroll puck + { + "class": "puck_control", + "layer0.texture": "Theme - Soda/Soda Light/standard-puck-vertical.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [0, 10], + "content_margin": [8, 12], + "blur": false + }, + // Standard horizontal scroll puck + { + "class": "puck_control", + "attributes": ["horizontal"], + "layer0.texture": "Theme - Soda/Soda Light/standard-puck-horizontal.png", + "layer0.inner_margin": [10, 0], + "content_margin": [12, 8], + "blur": false + }, + +// +// OVERLAY SCROLLBARS +// + + // Overlay toggle scroll bar + { + "class": "scroll_area_control", + "settings": ["overlay_scroll_bars"], + "overlay": true + }, + { + "class": "scroll_area_control", + "settings": ["!overlay_scroll_bars"], + "overlay": false + }, + // Overlay vertical scroll bar + { + "class": "scroll_bar_control", + "settings": ["overlay_scroll_bars"], + "layer0.texture": "Theme - Soda/Soda Light/overlay-scrollbar-vertical.png", + "layer0.inner_margin": [0, 5], + "blur": true + }, + // Overlay horizontal scroll bar + { + "class": "scroll_bar_control", + "settings": ["overlay_scroll_bars"], + "attributes": ["horizontal"], + "layer0.texture": "Theme - Soda/Soda Light/overlay-scrollbar-horizontal.png", + "layer0.inner_margin": [5, 0], + "blur": true + }, + // Overlay vertical puck + { + "class": "puck_control", + "settings": ["overlay_scroll_bars"], + "layer0.texture": "Theme - Soda/Soda Light/overlay-puck-vertical.png", + "layer0.inner_margin": [0, 5], + "content_margin": [5, 20], + "blur": true + }, + // Overlay horizontal puck + { + "class": "puck_control", + "settings": ["overlay_scroll_bars"], + "attributes": ["horizontal"], + "layer0.texture": "Theme - Soda/Soda Light/overlay-puck-horizontal.png", + "layer0.inner_margin": [5, 0], + "content_margin": [20, 5], + "blur": true + }, + // Overlay light puck (for dark content) + { + "class": "puck_control", + "settings": ["overlay_scroll_bars"], + "attributes": ["dark"], + "layer0.texture": "Theme - Soda/Soda Light/overlay-dark-puck-vertical.png" + }, + // Overlay light horizontal puck (for dark content) + { + "class": "puck_control", + "settings": ["overlay_scroll_bars"], + "attributes": ["horizontal", "dark"], + "layer0.texture": "Theme - Soda/Soda Light/overlay-dark-puck-horizontal.png" + }, + +// +// EMPTY WINDOW BACKGROUND +// + + { + "class": "sheet_container_control", + "layer0.tint": [255, 255, 255], + "layer0.opacity": 1.0 + }, + +// +// GRID LAYOUT +// + + { + "class": "grid_layout_control", + "border_size": 1, + "border_color": [189, 189, 189] + }, + +// +// MINI MAP +// + + { + "class": "minimap_control", + "settings": ["always_show_minimap_viewport"], + "viewport_color": [0, 0, 0, 35], + "viewport_opacity": 1.0 + }, + { + "class": "minimap_control", + "settings": ["!always_show_minimap_viewport"], + "viewport_color": [0, 0, 0, 35], + "viewport_opacity": { "target": 0.0, "speed": 10.0, "interpolation": "smoothstep" } + }, + { + "class": "minimap_control", + "attributes": ["hover"], + "settings": ["!always_show_minimap_viewport"], + "viewport_opacity": 1.0 + }, + +// +// DIALOG +// + + { + "class": "dialog", + "layer0.tint": [238, 238, 238], + "layer0.opacity": 1.0 + }, + +// +// PROGRESS BAR +// + + { + "class": "progress_bar_control", + "layer0.tint": [193, 193, 193], + "layer0.opacity": 1.0 + }, + { + "class": "progress_gauge_control", + "layer0.tint": [70, 160, 225], + "layer0.opacity": 1.0, + "content_margin": [0, 6] + }, + +// +// LABELS +// + + // General labels + { + "class": "label_control", + "color": [0, 0, 0] + }, + // Text field labels + { + "class": "label_control", + "parents": [{"class": "panel_control"}], + "shadow_color": [250, 250, 250], + "shadow_offset": [0, 1] + }, + // Button labels + { + "class": "label_control", + "parents": [{"class": "button_control"}], + "shadow_color": [245, 245, 245], + "shadow_offset": [0, 1] + }, + +// +// TOOLTIP +// + + // Tooltip container + { + "class": "tool_tip_control", + "layer0.texture": "Theme - Soda/Soda Light/tooltip.png", + "layer0.inner_margin": [1, 1], + "layer0.opacity": 0.95, + "content_margin": [3, 3] + }, + // Tooltip content + { + "class": "tool_tip_label_control", + "color": [0, 0, 0] + }, + +// +// STATUS BAR +// + + // Status bar container + { + "class": "status_bar", + "layer0.texture": "Theme - Soda/Soda Light/status-bar-background.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [2, 2], + "content_margin": [8, 4, 8, 4] + }, + // Status bar button + { + "class": "status_button", + "min_size": [100, 0] + }, + // Status bar label + { + "class": "label_control", + "parents": [{"class": "status_bar"}], + "color": [47, 47, 47], + "shadow_color": [220, 220, 220], + "shadow_offset": [0, 1] + }, + +// +// SIDEBAR +// + + // Sidebar container + { + "class": "sidebar_container", + "layer0.texture": "Theme - Soda/Soda Light/sidebar-bg.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [1, 1, 2, 1], + "content_margin": [0, 0, 1, 0] + }, + // Sidebar tree + { + "class": "sidebar_tree", + "row_padding": [8, 3], + "indent": 15, + "indent_offset": 15, + "indent_top_level": false + }, + // Sidebar rows + { + "class": "tree_row", + "layer0.texture": "Theme - Soda/Soda Light/sidebar-row-selected.png", + "layer0.opacity": 0.0, + "layer0.inner_margin": [1, 1] + }, + // Sidebar row selected + { + "class": "tree_row", + "attributes": ["selected"], + "layer0.opacity": 1.0 + }, + // Sidebar heading + { + "class": "sidebar_heading", + "color": [110, 126, 141], + "font.bold": true, + "shadow_color": [249, 250, 252], + "shadow_offset": [0, 1] + }, + // Sidebar heading selected + { + "class": "sidebar_heading", + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "color": [255, 255, 255], + "shadow_color": [34, 94, 145] + }, + // Sidebar entry + { + "class": "sidebar_label", + "color": [0, 0, 0], + "shadow_color": [241, 244, 247], + "shadow_offset": [0, 1], + "font.bold": false, + "font.italic": false + }, + // Sidebar folder entry + { + "class": "sidebar_label", + "parents": [{"class": "tree_row", "attributes": ["expandable"]}], + "color": [70, 86, 102] + }, + { + "class": "sidebar_label", + "parents": [{"class": "tree_row", "attributes": ["expandable", "hover"]}], + "color": [45, 56, 68] + }, + { + "class": "sidebar_label", + "parents": [{"class": "tree_row", "attributes": ["expandable"]}], + "settings": ["bold_folder_labels"], + "color": [110, 126, 141], + "font.bold": true + }, + { + "class": "sidebar_label", + "parents": [{"class": "tree_row", "attributes": ["expandable", "hover"]}], + "settings": ["bold_folder_labels"], + "color": [81, 92, 103] + }, + // Sidebar entry selected + { + "class": "sidebar_label", + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "color": [255, 255, 255], + "shadow_color": [34, 94, 145], + "shadow_offset": [0, 1] + }, + // Sidebar entry transient + { + "class": "sidebar_label", + "attributes": ["transient"], + "font.italic": true + }, + +// +// SIDEBAR - OPEN FILE ICONS +// + + // Sidebar file close + { + "class": "close_button", + "layer0.texture": "Theme - Soda/Soda Light/file-close.png", + "layer0.opacity": 0.0, + "layer0.inner_margin": 0, + "content_margin": [8, 8] + }, + { + "class": "close_button", + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.opacity": 1.0 + }, + // Sidebar file dirty + { + "class": "close_button", + "attributes": ["dirty"], + "layer0.texture": "Theme - Soda/Soda Light/file-dirty.png", + "layer0.opacity": 1.0 + }, + { + "class": "close_button", + "attributes": ["dirty"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/file-dirty-selected.png" + }, + { + "class": "close_button", + "attributes": ["dirty"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Light/file-close.png" + }, + // Sidebar file close hover + { + "class": "close_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/file-close-hover.png" + }, + { + "class": "close_button", + "parents": [{"class": "tree_row", "attributes": ["hover", "selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/file-close-selected.png" + }, + +// +// SIDEBAR - GENERAL FILE ICONS +// + + // Sidebar group closed + { + "class": "disclosure_button_control", + "content_margin": [8, 8], + "layer0.texture": "Theme - Soda/Soda Light/group-closed.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": 0 + }, + { + "class": "disclosure_button_control", + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Light/group-closed-hover.png" + }, + { + "class": "disclosure_button_control", + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/group-closed-selected.png" + }, + // Sidebar folder closed + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "layer0.texture": "Theme - Soda/Soda Light/folder-closed.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Light/folder-closed-hover.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/folder-closed-selected.png" + }, + // Sidebar group open + { + "class": "disclosure_button_control", + "attributes": ["expanded"], + "layer0.texture": "Theme - Soda/Soda Light/group-open.png" + }, + { + "class": "disclosure_button_control", + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Light/group-open-hover.png" + }, + { + "class": "disclosure_button_control", + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/group-open-selected.png" + }, + // Sidebar folder open + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "layer0.texture": "Theme - Soda/Soda Light/folder-open.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Light/folder-open-hover.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/folder-open-selected.png" + }, + +// +// STANDARD TEXT BUTTONS +// + + // Default button state + { + "class": "button_control", + "content_margin": [6, 5, 6, 6], + "min_size": [75, 0], + "layer0.texture": "Theme - Soda/Soda Light/btn-large.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [6, 6] + }, + // Pressed button state + { + "class": "button_control", + "attributes": ["pressed"], + "layer0.texture": "Theme - Soda/Soda Light/btn-large-on.png" + }, + +// +// TEXT INPUT FIELD +// + + // Text input field item + { + "class": "text_line_control", + "layer0.texture": "Theme - Soda/Soda Light/text-field.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [4, 5, 4, 3], + "content_margin": [3, 3, 6, 3] + }, + + // Text input dropdown + { + "class": "dropdown_button_control", + "content_margin": [8, 8], + "layer0.texture": "Theme - Soda/Soda Light/text-field-list.png", + "layer0.opacity": 1.0, + "layer0.inner_margin": [4, 4] + }, + { + "class": "dropdown_button_control", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/text-field-list-hover.png" + }, + +// +// PANEL BACKGROUNDS +// + + // Bottom panel background + { + "class": "panel_control", + "layer0.texture": "Theme - Soda/Soda Light/panel-background.png", + "layer0.inner_margin": [2, 2, 2, 2], + "layer0.opacity": 1.0, + "content_margin": [2, 3, 2, 1] + }, + // Quick panel background + { + "class": "overlay_control", + "settings": ["!soda_retina_fix"], + "layer0.texture": "Theme - Soda/Soda Light/quick-panel-background.png", + "layer0.inner_margin": [12, 6, 12, 15], + "layer0.opacity": 1.0, + "layer1.texture": "Theme - Soda/Soda Light/quick-panel-sections.png", + "layer1.inner_margin": [12, 40, 12, 19], + "layer1.opacity": 1.0, + "content_margin": [11, 8, 11, 17] + }, + // Quick panel background (Retina fix) + { + "class": "overlay_control", + "settings": ["soda_retina_fix"], + "layer0.tint": [238, 238, 238], + "layer0.opacity": 1.0, + "content_margin": [6, 8, 6, 6] + }, + +// +// QUICK PANEL +// + + { + "class": "quick_panel", + "row_padding": [5, 2], + "layer0.tint": [252, 252, 252], + "layer0.opacity": 1.0 + }, + { + "class": "quick_panel_row", + "layer0.texture": "Theme - Soda/Soda Light/quick-panel-row.png", + "layer0.inner_margin": [2, 2, 2, 2], + "layer0.opacity": 1.0 + }, + { + "class": "quick_panel_row", + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Light/quick-panel-row-selected.png" + }, + { + "class": "quick_panel_label", + "fg": [50, 50, 50, 255], + "match_fg": [0, 0, 0, 255], + "selected_fg": [25, 25, 25, 255], + "selected_match_fg": [0, 0, 0, 255] + }, + { + "class": "quick_panel_path_label", + "fg": [150, 150, 150, 255], + "match_fg": [90, 90, 90, 255], + "selected_fg": [120, 120, 120, 255], + "selected_match_fg": [90, 90, 90, 255] + }, + { + "class": "quick_panel_score_label", + "fg": [72, 139, 211, 255], + "selected_fg": [72, 139, 211, 255] + }, + +// +// MINI QUICK PANEL +// + + { + "class": "mini_quick_panel_row", + "layer0.texture": "Theme - Soda/Soda Light/quick-panel-row.png", + "layer0.inner_margin": [2, 2, 2, 2], + "layer0.opacity": 1.0 + }, + { + "class": "mini_quick_panel_row", + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Light/quick-panel-row-selected.png" + }, + +// +// CODE COMPLETION DROPDOWN +// + + { + "class": "popup_control", + "content_margin": [2, 2], + "layer0.tint": [255, 255, 255], + "layer0.opacity": 1.0 + }, + { + "class": "auto_complete", + "row_padding": [4, 2] + }, + { + "class": "auto_complete_label", + "fg": [51, 51, 51], + "match_fg": [0, 0, 0], + "selected_fg": [205, 226, 243], + "selected_match_fg": [255, 255, 255] + }, + { + "class": "table_row", + "layer0.texture": "Theme - Soda/Soda Light/autocomplete-row-selected.png", + "layer0.opacity": 0.0, + "layer0.inner_margin": [3, 1] + }, + { + "class": "table_row", + "attributes": ["selected"], + "layer0.opacity": 1.0 + }, + +// +// BOTTOM PANEL BUTTONS +// + + // Button group middle + { + "class": "icon_button_control", + "layer0.texture": "Theme - Soda/Soda Light/btn-group-middle.png", + "layer0.inner_margin": [6, 6], + "layer0.opacity": 1.0, + "content_margin": [3, 3] + }, + { + "class": "icon_button_control", + "attributes": ["selected"], + "layer0.texture": "Theme - Soda/Soda Light/btn-group-middle-on.png" + }, + // Button group left + { + "class": "icon_button_control", + "attributes": ["left"], + "layer0.texture": "Theme - Soda/Soda Light/btn-group-left.png", + "content_margin": [4, 3, 3, 3] + }, + { + "class": "icon_button_control", + "attributes": ["left", "selected"], + "layer0.texture": "Theme - Soda/Soda Light/btn-group-left-on.png" + }, + // Button group right + { + "class": "icon_button_control", + "attributes": ["right"], + "layer0.texture": "Theme - Soda/Soda Light/btn-group-right.png", + "content_margin": [3, 3, 4, 3] + }, + { + "class": "icon_button_control", + "attributes": ["right", "selected"], + "layer0.texture": "Theme - Soda/Soda Light/btn-group-right-on.png" + }, + // Button single + { + "class": "icon_button_control", + "attributes": ["left", "right"], + "layer0.texture": "Theme - Soda/Soda Light/btn-small.png", + "content_margin": [4, 3] + }, + { + "class": "icon_button_control", + "attributes": ["left", "right", "selected"], + "layer0.texture": "Theme - Soda/Soda Light/btn-small-on.png" + }, + // Panel close button + { + "class": "panel_close_button", + "layer0.texture": "Theme - Soda/Soda Light/panel-close.png", + "layer0.opacity": 0.85, + "content_margin": [8, 12] + }, + { + "class": "panel_close_button", + "attributes": ["hover"], + "layer0.texture": "Theme - Soda/Soda Light/panel-close.png", + "layer0.opacity": 1.0 + }, + { + "class": "panel_close_button", + "attributes": ["pressed"], + "layer0.texture": "Theme - Soda/Soda Light/panel-close-pressed.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 1 +// + + // Regex search button + { + "class": "icon_regex", + "layer0.texture": "Theme - Soda/Soda Light/icon-regex-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_regex", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-regex-on.png" + }, + // Case sensitive search button + { + "class": "icon_case", + "layer0.texture": "Theme - Soda/Soda Light/icon-case-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_case", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-case-on.png" + }, + // Match whole word search button + { + "class": "icon_whole_word", + "layer0.texture": "Theme - Soda/Soda Light/icon-word-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_whole_word", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-word-on.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 1 (EXTENDED: FIND IN FILES) +// + + // Show search context button + { + "class": "icon_context", + "layer0.texture": "Theme - Soda/Soda Light/icon-context-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_context", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-context-on.png" + }, + // Use search buffer + { + "class": "icon_use_buffer", + "layer0.texture": "Theme - Soda/Soda Light/icon-buffer-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_use_buffer", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-buffer-on.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 2 +// + + // Reverse search direction button + { + "class": "icon_reverse", + "layer0.texture": "Theme - Soda/Soda Light/icon-reverse-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_reverse", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-reverse-on.png" + }, + // Search wrap button + { + "class": "icon_wrap", + "layer0.texture": "Theme - Soda/Soda Light/icon-wrap-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_wrap", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-wrap-on.png" + }, + // Search in selection button + { + "class": "icon_in_selection", + "layer0.texture": "Theme - Soda/Soda Light/icon-selection-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_in_selection", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-selection-on.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 3 +// + + // Preserve case button + { + "class": "icon_preserve_case", + "layer0.texture": "Theme - Soda/Soda Light/icon-preserve-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_preserve_case", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-preserve-on.png" + }, + +// +// BOTTOM PANEL ICONS - GROUP 4 +// + + // Highlight results button + { + "class": "icon_highlight", + "layer0.texture": "Theme - Soda/Soda Light/icon-highlight-off.png", + "layer0.opacity": 1.0, + "content_margin": [9, 9] + }, + { + "class": "icon_highlight", + "parents": [{"class": "icon_button_control", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/icon-highlight-on.png" + } + +] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light.sublime-theme b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light.sublime-theme index 5c1e2ee..936a573 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light.sublime-theme +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light.sublime-theme @@ -487,6 +487,13 @@ "shadow_color": [249, 250, 252], "shadow_offset": [0, 1] }, + // Sidebar heading selected + { + "class": "sidebar_heading", + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "color": [255, 255, 255], + "shadow_color": [34, 94, 145] + }, // Sidebar entry { "class": "sidebar_label", @@ -597,6 +604,24 @@ "parents": [{"class": "tree_row", "attributes": ["selected"]}], "layer0.texture": "Theme - Soda/Soda Light/group-closed-selected.png" }, + // Sidebar folder closed + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "layer0.texture": "Theme - Soda/Soda Light/folder-closed.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Light/folder-closed-hover.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/folder-closed-selected.png" + }, // Sidebar group open { "class": "disclosure_button_control", @@ -615,6 +640,27 @@ "parents": [{"class": "tree_row", "attributes": ["selected"]}], "layer0.texture": "Theme - Soda/Soda Light/group-open-selected.png" }, + // Sidebar folder open + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "layer0.texture": "Theme - Soda/Soda Light/folder-open.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["hover"]}], + "layer0.texture": "Theme - Soda/Soda Light/folder-open-hover.png" + }, + { + "class": "disclosure_button_control", + "settings": ["soda_folder_icons"], + "attributes": ["expanded"], + "parents": [{"class": "tree_row", "attributes": ["selected"]}], + "layer0.texture": "Theme - Soda/Soda Light/folder-open-selected.png" + }, // // STANDARD TEXT BUTTONS diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/Widget - Soda Light 3.sublime-settings b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/Widget - Soda Light 3.sublime-settings new file mode 100644 index 0000000..5cf1379 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/Widget - Soda Light 3.sublime-settings @@ -0,0 +1,4 @@ +{ + "color_scheme": "Packages/Theme - Soda/Soda Light/Widget - Soda Light.stTheme", + "draw_shadows": false +} diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/Widget - Soda Light.stTheme b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/Widget - Soda Light.stTheme index 3c84a2f..7b59a19 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/Widget - Soda Light.stTheme +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/Widget - Soda Light.stTheme @@ -21,8 +21,65 @@ #000000 invisibles #E0E0E0 + lineHighlight + #2D89D825 selection #b5d5ff + selectionBorder + #b5d5ff00 + inactiveSelection + #EDEDED + + + + name + Comment + scope + comment + settings + + fontStyle + italic + foreground + #0066FF + + + + name + Keyword + scope + keyword, storage + settings + + foreground + #4271AE + + + + scope + constant + settings + + foreground + #2C473E + + + + scope + string + settings + + foreground + #1D577D + + + + scope + constant.character.escape + settings + + foreground + #e47b18 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left-on.png index 9b09ad74992c08c3ea58aaa7dccbac6494c6baf7..c39d6c2e2e1e174f88396faa23470fdcb9846a30 100644 GIT binary patch delta 298 zcmV+_0oDG-0>uK5B!3J^L_t(Y4egdq3WG2Zg(ooqqwS&~(pB%!1Gp)8fL^C3>8|JT zCT{CO#6|7AKmsn@$fTPJZG95Wu7>Itc(Ar3*b{Cb&on34o*!%q@ifAJ^6d5J3z+jBM-S zz4up;5dPj~TkowPshptiYFwa7S9HRk`48`g8atxou80LkCskFw=XpNirZfl;g};@) w?_XWl-N$h}0wXT%#zVOAne|{@S&k)IU!T1sP58zTfB*mh07*qoM6N<$f@U3t=l}o! delta 299 zcmV+`0o4A*0>%Q6B!3M_L_t(Y4egdq3WG2Zg(ooqqwS&~(pB%!1Gp)8fgYzP>8ibs zS8-VvA}(s@1rl)KrcAo(z{4MOzPx#0RLinZ>M{VdZF?$;;%!-E$ zlIJ-wgp^WZoM9NqL&~yj*aB(U04PYoB$$O(|k^uC=qP-L3 z5F`yjUk?E-NE#BI;N&Mx00B(8qLTo?QM%AGW`c{PkN`*u!Q4Xl|8Z?i01?FC!^pNC z{`UR~62kws+17h2NGd1jyBZg$(iNTXXa3E*p~j9VxhrD9(MeTRuX&yixG4<+MB#6x x@B2sBb=Pqm_auNzyYUcid}Q5NXO?4$)(4-(Bu(0wp3ndQ002ovPDHLkV1jJCh`ay* diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-left-on@2x.png index 942fb8cbe4fbf7611e609e3391370ec8d7f57bef..2f1e8e9d3ad9d0939bbdd40af08ea034535f8209 100644 GIT binary patch delta 383 zcmV-_0f7F)1;hoAB!7!ZL_t(&1?`%>&JsZshIf~L0+pgBmeNZ9S=kao=LLMF#vAYo zybSNf!doDQl9Em;5(s$S)n_uxu3y3y%M5Xn!(oS+-S?UI3=~6pKA)3&XxTkLh@pK6 zGM~>MvMl@9YPH^_Y5K}ybU7x(>(no;#bWWL)9HL|x7(?KBx44Y7)Y+093q4?IgsUY z`Oxe2essIt)L;^WNdzN+kRf)vUB0%iv<%`?zu!+S5`&2b49xeqRJJh?nL{LI}z)t5C~J)uZN0%U0j|x|0S0l>u^-oB<~Rlas^&UmP=w z9%MKi9_)m-Cpsv}>I=1@(K#}iOnz;*+ZU5R0wQCxTCHB|*i8fYwP!+zK;!W^m*xI2 zU{`M~zHK&}@p`>}ZU^zB!{N{|hw?9g5Fva`K8X4sEk0ZP`A)Mpv9wDN>7H49_?w?u d9#{n5egiQ+RO{`>s%roM002ovPDHLkV1h))wblRt delta 382 zcmV-^0fGL+1;Yi9B!7xYL_t(&1?`$WuG&Bpg>C*prb>8;lrq%xFI8HE=(2#6iL!yL z;O!!dNs%p}h?0^nR7fDmJx;#S7&}kGg_%Ja>2mQfW1nx%9VmwMd_E`l&|3EZA%@N+ z$b3G3Y_(dS+U@qcG)-SyWS3(?yiWbnS}Yb{yWQ?nr_)IdBx5n4#6WW01F z%g27d|D)IIr3RB2Od=QogbcCU?eevCrDYJG2ZKRskr+%gAjjje2$E&ld$}+i4s#H> z;+IxFI>49xeqRJJh*#!HLI}z)t5C~J)uZN0%U0j|dXokLl>v5>oB<~Rvy;RDUmUX) zJ;-P@I@k$sUvyBC)fZ|*qjO|3nf%&rw-1v)0wQ9&TCLvb*i8fYwP!+zK;!W^m*xI2 zU{^DXZ=20#yk4(g+ChAFI2^j>Q2qrFB80EW2T}h=i!T;`zSHbYEbS6Ry5-D=zxjpb cp+)fRH!(s~>*rHnRsaA107*qoM6N<$fA6Hxxu#E!e}8P}T*jp$^hZ0O=^;pD zMTM{%ZGErz;3PT(fLIXdhMwF*U{Nc6Ete($2Mtb;h?8g#2m_)GfD>pw^n`Cd_EqDl zTrnVjI?*g!Z~{?$h!agnZ~{Oq$deOmt@?xoCsHnqm1e0#&yO(o!maCSzSk^b$8o$3!|*ZB^KsiY{I#ftft_VpPVC^5^D{d*s1jK0 Y8;7yuXA%Ym?TT)Xvs*?W(G}T5Hb= zeB&ZSVnCd8by=35MNvo`V~VGyX?`KP`g5M=k|YVw{g2bKY=7^45=D_L2E zZV*i(s6Dhv^3o72K5YD^U5Loi-F7hnSm@m(yFp+KA{&4t@LcZ+AbRsLPu0J6?E!h1 zgxR)-Ntlr5CIJnx^wFZ9ur&z)D2Umr;I|xMP2i_#vf))^fTI6y#9(Z;2v$d8c+aOD(v zfYec5BRy|GnTD4jqM$+o2?+@b#6?TazvLf{*+rq-v(8BCGjnFJzi-aYS_XT^7smTq8!-xeHfrwwN zR=>!>_nOV7>GgUBXC7Bp8qNipS11%7(P_0>nUesw`*eDbN3jSYpSi}vZbTw7z?nGz z-J;owtgcAJi+>564L52QA)7rTOhAB^jGWUL$s4N(V6z}_zNuD(6IqiWuN7f}kz(2- zd94V6Il*yOq7|V4V^##n`}#y!0UC0wJ(BmTig2-Jseg)Smmr@N;ckavXng@>&}ix%J0NrLx*=Hg~hx%yhe5^|ur^mUjz8P?50H>HMD0 z=XZ<6B9f4gs*U{9fPHI-FVq$(;-Mqw!(Xr0KWav!(bIT5{-6!^8Xk&x%jNRETrU5d zPN#1tlgYc)YIU>SZu58mT=R|6UK5D`LaO4)L4T6&SAoYP47Zq_#~RHo2D;zx@5Sk} jTpvZ`R^Z=g79iUn*>%<0zKpuj@m#JhR5b6SRo{m4G;<%ddf)AzC~UjX_6uj zkUDve^c3`zX?O;TNJvyjAPPhzD7ctCzuA8@Ya8J-%khrnJu_#wo+JRD8w>^?Yqi?fTz@Y2LGlf`?qH$4a78fN zx#4j5qg*ckl)CEKPnuK!hZ6mM|DoAz{*g1|wOTFHZnsUTR5G$MN%yJAjYgx81uFs( zzh19@gM`SKZntY>ONEyj;wvCH7bsq#P} ztq2p0+6AI~nHS;2TM-(vgKh1RyjFw>M(qORBX24Z4(81oqZJ`s6gt}GI4zNUR)mQl z2#oKa|KKyH1v-g?j4=^(u#++A1GMDG`1(XR!&zGqa(`c35iS`AYK`Q)D54cS$`upg z0C#IKr#Z$%n8=z4C;9blt!pd7e$IfFNG`h~E*Lwn<=p;T)yZl_m|%>8UCKwt-Kw2R zM6(3htO#e49c&{e<=p;T*2!i?m|%>;UCKwty>y#gsYPBI@PB`w72%CE+iYMTorTSeCyZ3b9Z9m3n tE-}#ke&3U?s6UBFPr%>ERn{y(wnw|1-p;e-l>7hy002ovPDHLkV1jSEZDard diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-middle.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-middle.png index 016d334508ae2fbf74aab276589b9832dce7533e..6118c7a24953ad49e9ef0cfd67fad1e237f32daa 100644 GIT binary patch delta 143 zcmV;A0C4}m0lxu|IDd5Gy7zITHWADTd_o)Wx0!K1Dk8TnY&7RQFmwaf*4nT^I7%sU zsVV}#KrjD2rND8|5cJ-cqOE5r_$+~cd}d%(-D=GO!!4Q$e4{c5iztFK7C8&5z`>~& x{U`&^ff&3+X9(~XrIZN!15<0QPrQK33zE>Y$!5liCjbBd00>D%PDHLkV1g`?K41U< delta 143 zcmV;A0C4}m0lxu|IDf>2>)ywW+C(rX@Cj|e-)6@7sEFLQu+g0Jz|aj?TWiAx;V7lZ zrK$+{0=@kAlmf>+L(qF)ingAi;Ijn&@tJ{Db*nWC47X@1@QunKETRa`SmZ3I0tcsB x^rH+s2V(FRogu(mlu{z>4@|AKKJfxBFRbsg%q!i!Y5)KL00>D%PDHLkV1i@`Mi>A9 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-middle@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-group-middle@2x.png index fa640c487a471d7a3f5a0626520289231583de52..80a20c3cca71eb3e1005ac6c8c38bc0845f389f3 100644 GIT binary patch delta 325 zcmV-L0lNO=0^kCWB!4GKL_t(&1?`$k3WG2dMNJ#({x{^rc~|39Z4^!h3QmOZPEjZ~ zQA+PGjnt;D>)z$1J#u0G(XubZ(=ZIHS-{Ioe&e3zdEOIPS62|@IPTu(l#)6GeTDP_ zGTjF)eS+_{59|jgm|L(4un>kPhHEQ?keUFBxDXxTt4%;JfPaQO6~c+usIO2JfOE^T zFjS%cTL`;}gn4v?uRj5lfYF_YAAGg@079M$;Y4fHSEveL0Je7W?&oP2F^1PgY`c{> z3E;%tNL#%hzS;_5RILz-xDXxTt4*K^U;svU9)9qBejCM+)<^={MT}woMT`<6VICdf z`#%+m?)Z`4DLw)1%^bwRqtdAJnm~C{AC==JFCGu;cj7cn)Av$p@Qb00000NkvXXu0mjfCN7q> delta 329 zcmV-P0k;0&0^|aaB!4SOL_t(&1?`&63W7isMxBy;{}Hz`$Vn&!Sev`4!!e`win#LF-Yt69L!On&EH=6OC6SXWmN<2WAPx1|(y2>J@? z1+;V@wDbwS`*q+rIKkY4A;3Zyo*1sJ5JDmW6tR#U;j2wRFMoiBTouBJ+NiHk2*9~z zSs1F&|0#ssM8Z5f!q=YwO2F*S!wi|No3gJX;)K@43FaT>i`Rn^>H!+6CCbr#5 zyb0jMuaVYzJ$$tl!lFUZB!8qyL_t(&1?`zXYg%Cx#_!cvML&SxPq=@^O-vZ#PN8nAfy_ZKxDh!uGnt3FBgl&H4LX-uU8odt=9z*R^$#MYc`uH z&+}K}`3-!hPN!4(#~VB^g0xyKKx6^|y`bmRZnvvFfq39W0f5D-AmM<(>y*+9`b?n1 zqVm9_VotCCfqz*Mpe}u)&#FC@2Obs41_bnOe`{XMh5|uy8;wR4;X%+jfPvaB(u&~F z`$Ah0fH3r|AeN4Y-V`3|F!Y#Mi3lJt&5BSJgaA|?cvS+_{c3BmGF}1T3vC6ToDU15 zAoihG7v)-P!V{n z!?1I7*dtYh^I?;(Aod|Cz3It{uo4kKkXZP%!DMSi2*d-giWz}d!qBIJM5_p49)Lus zNc}gKO}_qRw+~6_P2usMtrfl^tOV6t6|5h4#J_}~44#J@c61KICSPadxwTl^C(yHN zEzZZi)_>v)>JudPQ9r^$SkF4_&olC<{QmzJqY6kmE5q-g6|rUFp+_Y^-ET+`Bp4ua zd!_e<;Xnuh?RL96C{{=id~ihc`~6?H7zTt7JS)uOvlc@3<#;^4qTjUHY`Ax+FpotD z+#O1klgZ@Ua5(%#(@8Gfxp%98kgK+X+~V#BgFeBZ(P;Ej^tTxPNYlBz=?h;3Qe5x8 rmH0rOaaB!8tzL_t(&1?`#7YFbec#_!dmq7NXrsC!qEg)h(-aPPjmLRY>- z_g(ZITDnni8@ero&@8eqbkRZ=wg~Fg+xc$t4bxk<7q~92maiJtp~n^CyvV89lAi)5U z+bg{<3&4zoI3iDWm zz}=xlIhjnp42Q!@noe@*&b?a&gj}^9BY5kDuexfIu@mgDNC^{0wo027@k zihP!3kD@3pEGg_v-}g6B0*kSuC&zXh$MKXTN%sL5K@IR}qm&c?0000TB!9?BL_t(&1?`$UZreZ*hIb|Eks=TTNQHnvm7p`E%0<+q2#_)d z$pLbSB!vzjAVpHqT9 z-AXGfp(R=>+RMhXbce!mDi~w*OVuNgUn~~sM1>qt(v_%QoqtZJZ@b;@!)!MDDM!xI z156`M(?59by`Q$*?boBx=<9qwe=Cwv7RV~+i=qgQ)KU(|RJHT@><)*6TP~Mwv)Mq~ zd9_--6lZ?PxX6T3mEgO?v|25fWtp2!r(w!^z0Ugm{u|kNC%v$gS`LejE~yn@I-Sld z8Ndl|3M8nP4}Z9WQV^&Y6^P4N8;qQ+H9}{z8BcvYb@+^ewSyp~s1dR1vB1rwRviKc z0FKRMYC$|*i69)1nA=)o3Xp0x=|~F01vd)ow~LfwNMvF#HX$h#xw{a5aW5|HoMY^n#R=17(lqes7df$TA-DkbIdwYU-(!f zS}##}M(4^S5`q!?B1pOu2Q)WmWv9-kms-{wffK3b|Bf&>A0x6pZgu1_1U=+CQ?d8W zH}$LaB!4gC;ZEw7nPR`s4M3Rzfmk*=!HE4PYJ_eOM4o$wv}!aOy!BH?M>t7iOjjP` zd_!d;W-h}Mh7`oaPv&-36&uh;uqfs`2m80&wiWgF=vFAKQscI)JY0o+k3 z$K&yD(HGJylQ$oHcZ?r6^Lumn@jv5uXB8ocT)aT b48enc7EN9aN@QZ*00000NkvXXu0mjf%KTnA delta 743 zcmV)2LA<+B!9+9L_t(&1>Ksvj@m#JhR0woDI$cBkU%R6B*H7C%8SsXh?Fu9 zk_X63B&n!DDUvdmBFYN{aaEdS3vPDKKk;uJEVSBfvU7H%;f%+F`M>$k*j{@h=UiE; zm6B#cQxq*al8q<2Bfz#6j5cbL)f_0APN#ZcfE>A`>m*6$r+?Gw%X+bu`G~PE)qiTNFn90FQ{@n9^G!YbFF!C-(~a?9m1ZZ?~*Wb7M&SV}F2Nk{E;I-a#w ztG$p0oQO<;cz^Zs0e9d^xm>==KwQkV!N|d(#tgvAld~8v9Z^idp@Sf$s1XrW`tk*C zCbfzXG=R}$YC$}~au7C(JJ=sugic+l&!i)qbVk6K15gw_;yus^g2ow`z=Sw{%K zXcmO7 zZL^EL=!#iKv>T=!fp`t`_#6RYh(*sj^>KaZeWo12saT8GZBrm{8ye)y z5zwIM&VPSB;eKAyY9h7h2B%iud-{o&em?q1^VH&?5TkYg?fri5Wy+f?H2mMZxg(+8 zG!`coAgk5NJJM)0{$wC&OaR8>7em=bIvoxN53$*7@SH2{^?E-=pHHuNGMR*;_0Bo~ zF&qwmw_2@F01=ncJhkq8lm{9pZ*Y@(y}p-px?t(|`|EbQeJyL{qsZ}VB-(DbUmr-@ zy?z#FE;u3o#(~$;Cs_(_T>t2Wde}e_$Mt*HAy1xm;hibL@D`W5? Z{sKzxZ3HFD0zv=)002ovPDHLkV1jnHXv6>j diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large-on.png index 1bc3770e2c13def966aa967a3f45bca97b93a044..527647eb7ca900da7d780011f1bbe265458bbf76 100644 GIT binary patch delta 409 zcmV;K0cQTl1G58=B!7BIL_t(Y4c(T%PK7WS#mj$;frJ|pSXg`j7ZQd$=x*ZZ6EHgZ z9*(;B2F9mwW#AQ@2?>V8MJS%r+%mXQ8T^vcQtmy!o_>(8*0L-yqwI6Pr|L_0Tuf%b^@-gVtQG(TL3+F6#p0Cb(Esef=BhYmNf01(qOMSb7j z_`d%Pv0DYQZTnahg$Tn?fi_W2f1c-A>5b!fg(;gLffaaB6p0`R1fWJ7NhyWrd31J0 zh5~V`=!T%J+eT3aLDWK1c~Bs_&`wN&v?hCoy6Q<&5DHUE>+*heq#v?38QReb;tEVU z;$pQcj@PX-4S)F!+Rf1l(kozZGIr0=>gfNy49v@4ym`ClX#ekMm#uSMmv!ASog)@M zRM>Bt;MU0`Ne0w*RYN#wA_ze#7dRsH(3AX*WK~sfP1D@9Z5!hUjtLOX9y1JlmSy=0 zz1iYH0&em+jvp|-#AL|1EpbZ07c|;o7#{OH?|uNcYZ7!wGEnYV00000NkvXXu0mjf Ddc3=) delta 430 zcmV;f0a5<51IPoAB!7=dL_t(Y4c(SKPQySDh1dT^$X5KL$W0n9Kw(!D&?8ZDf|PU| zgc2G~K*1%bBatIeI~GQkXt3AJTQV-vdX$2Zyk3vv_wAeAcx{d2s4UB}l~R0AS1dFs z4a68-K-TN^*u1|$9o!0tox7*#3=7&q7_Hwy=&VRD(D$jG}I1U`nVxdZX z-$w+w_B`*2#BK}-PFAax3d7KVW>NMJ55r*ex~{vVlns!g5qMD)sUQdxK}8%<*%Y~f zvlB232&IA>faaDBq6mPXMTeOO1A+^7A_ZbioDrtwq!a|g&|+O&PmcH|&XU27CJ;s- z=?H^mSLjcrlYfRBf{Y{nlS;#&2W`YuIv#<^3N}ZdqobAXxQl`7D7_dk)vP@&20)}i z&|}a)EuHJS*w-ED(qZ%SegDVI6*W3d(@)xV%^rfJL;%t>jY^WFAw7-7psT8S-E20u z+wC@{AN;*DX42@@GmwF=vMgUnYOwGjQP*`5$MHS&&nGDvA~z@YQ}9h1?`_*Y6h%@0 Y0(}f|Q?|3oK>z>%07*qoM6N<$f+r}-@Bjb+ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-large-on@2x.png index 921950f14c0b3884e93d3bc990702ee8f7a52f02..a4e4d38658592bf363a3076f8fe07ac8b5e9d877 100644 GIT binary patch delta 1044 zcmV+v1nc|42&f2J5N+W7=~xB?6O3_1Z9!JViStBB^pC)`~mjX zUMNURwz2jf5S!hfA+fihu+|ux4HnRla0x61fn_hg@38MOoO9O2hMi;ll3{M&IlRw& za|V)c65VdsxR!vS&q7mu(evl0L__{nfQ0%END{hPTri@H8h=g@)WSAPr&s!5X$+Vi z3?Wj>%gc`og~F>;D)kWkyO=A`#}|Y0pU|@1Znr-lA0Jn1wb~c-TijTrvJpauFzHIA z@-~ynyhD4`(SU^E2HmaG)6>f8>S_(%ZSZuotx_v1D@zj-6Q3}Vn4Fw6)6>%?olYA> zYWSOP$OD2+r+?Ei=jZ3<;NZX<9UbvEw;GMcqiVJK740^+9HElqf+=1ek~ zv;{+$_QME7BNX&neSk?L5HB$N2E_u=Cy~u&m#{C*?Ch+`<#IMMgd&hHPy|wxsC8y$ z#sJ7b`x7#-C_>PjxI^JM5X{)vnB8s!qWJ_wJ%nLlq<^Rl-~BzxBiL><-wp!lQ%x8af<Kq8< zI{5-39e)C0-)*xW0^(p?Ofc@^M*_*wS1|X)b781A1Qc}e5z1X~w;c)OBfua{f^mZo z86i0ideNxIgIn&8K%*9Wo!_qocWXTtijXGaQVU9YLqKw1?XyXr^h#?8NE30W1tU0W zjs%jUuTXz=D7dt~0%^hBT3RC`q{%qcf|Aw{kbfLl`)saGS8E8!XNV|m0@GY1P|!)v zzSRE@1z#d@9UZ~l6w56~P=Tlxd47oj)-MMWtx zLUQyOPM<_jtAM($gU=vAZ9$-m1W?c`CjF=&C_+r3w2_`jDd!?*AKCQ3oU|ef(dy~! zGJirg=Z~yilMu|g_-vu^)NDb>`>i z*B2HRO7(jE{_gIsVe#DvQ!bZ(;2Od@w10NUqC=sJ%M2eD7Z;x*t>4(#*eK#+!!3+u zkZtzo<^@#aB1oIx3@(N=5P*0 zpsg`F)Y=D<@Mg3501rF^SQ$RIF`m<$4>ffCf{s0C`-X*I15Dl3^&1jHSVR`5#0|y( O0000FH@xC=?9fOe&Qc z{>ryh0l}u*?SGn!iwkpda$-(TPx+hM$H&JHx3{;yoNArygpfg+Gx z>U27$SS%8VdLNU4Lk=Y%zQ|&1J2N>sX}24J7(M|}4}W1eXaKe0ZE=?}nX{)HFOZyI zGMCGlOeS;9A?AGv*C7ExTduFqI%Kcq0%16KAZU#R?sfl%Z*?Mwf*{;IIJ&JaTnBG- zIdruE5chN&H;5oK6HJ<%6ZlHM3ROgMUh7_YUuJ0|Sd>;d6aeT42;eJ}Tp9_;R~S8L z>2nQWZhycbF2*GUBRJ}g1X7`|VD5<*);a*>()tSILb%uebjVkbur9&4CPX@C+!qX$^o>phO(zsD~d3q(Wbz zMmiMY;FKP?JL};`I;3PAW5vL8KBW!BLjyV>MkxW{$wSK_V@P<`v10v$u*yX+wAP@ z%xYw7A0Hoj(9V`Vzg1$=*pEEnx(f>nJAcc|%lCJ8ckk6|HN)Y15N2g%<#(x6+JV+C zS#&7$@MVS%Yinyy&?;~2?d_H8_4*yGWzcQ*=bIN$jW%S5@0r=z*~Ze+(qXw=u46ZS zM#?r}uYGw5iDz)@1t9D??e#JADV1Vp{`h?tPiSlwliLF~f9{{}cg~lCEwdZV9tZ)AW6{T0KDX(}k#>On)ZPcs#xt4u{HhIn_P@ z3D)cNEg&yYyEAs-Wj+`Tl;?T0(aNA!7=}#z8X#@b&-2`aZHnK^X3!mIpHb6*2#{eD zL`{I43J4Q68Aj_c`Ld!@fT&4WSrI2r1rpubiX@AsUh>an998uD(ad67h!Mesup}YONEHl-snTLmknowN4hTgZ^4)6I5%R z*1G&lnpVSeOqBK!B1u=QlTB?Pa<&$P$s8R~OLvtlk_K_y>3%KUX_!d>sG)002ovPDHLkV1hJa B-UR>v delta 455 zcmV;&0XY8U1K|UZB!8$$L_t(Y4aJqeO2beX#cysC5RAnxg|tIU=90m&gYM$sd-w*v zK;J+d@(d2Xf~!;M*hL70IEjS}ZT@&p@C&K!(&nlkT=Msx-*-*|pH-HnvMft0rDWmM zSQSvp!6TWiQL+@(!ABHbk}W`+wnRG`boNhsw5jt9=1- z7DdrDAdgVHsp7)LbTAkw$8i{=kwq=f^Vs+$K)PzbC<>?3=_r0Llf`uMJU^jk1tLI} zNzk{ZRfFJ2ahWcHM7MS%H%!8)Nf_&IK_oXFNf*buh3sd3z6ms+lj78HVu-b zwj7y)h%4%e=6@DQvXvLx9(O!B_t! z$GYJ@>M=fQN;+H7$xCe@vUe{CP6|v!Ez?!HkWGgk38BV%y;hiW8TZps(d~9W<2XL= z_xpTMTR#-Q_a!zAJe4Gga$Wa>j0!ZFOr95u#qE4PcR-OV$9wByO1)lh6$HT(#Bz%r xq-pBG$vw_5V7nTu9=B!5LoL_t(&1>KsxPZU8I#^;XX!;tV1I1l6ki2=&9)EHuGM`cH2 zVYIL?v9Y%H577UB!jF>DL+q4QGfD=69l!e&Du+<2P?th z5*R}Ct*)-#9UmWm+~41S8~qB#h8Yu3*@ODV@$vDO-QC^SD=RDCpsmA+MXESM=n$r~ zwzl?SXlUpq+IeT=qW7=UZnvK=FE78r1zqs;G_9DKrAnppkuXzJQ?6RAy1~Ig*Vor~ z(La7Eah*=b9e*Akx_Z6twzjtTo1I3ZaR-_Dj&_$*j!?z1v9Tx2*!1+YTUc1&G>j|8 zf+|=Fu2d?y#l^)~*XHJC2}|S=I?qtMoN6qEfq{V~HfCmKf`y?z7Ts*#d=|{4dvvqf z-RwFt$zg_g zdY%)M)p_NBbW<%%CQv$%eH*Cp5Ug~`T21F>C@a`r7($yGRi)=#AO(bM(=gQPfh0UD zNMoT{YGsjS+XKmY)qv7^{|g{yM#TfkktVi&wSV)ldP1Pl<75D*2Y zFU$+V{;q(mHV>pgJ1AcW_M*BxkS>tbBYFrhX${gwt*VOgKynC(ecRS}DAs~lodSaI z?|*ZV?m&o8(k3A3){PdB`mwb^wtXrnfR41uQj!M(IbJ99iaf*Qk)JL00_(r*wz8w& z?RLBIvcNkK9`Cb6d>g{K7UAUXn4Y7fBgYpdgixhFgd#n()JUx?NB|r0no%mEk&%(s z!NEZphp=vLZZ7Bzfe?ghHk+{>9Uc9h0DnaUB3W;CcJ>#J$;&%CJ8=+>_#MwM^-2pd zK@?6ie01Tqx3?WWyx<$42-<%@Ts-YcAdR?w0PuN!e*WhA`uYt*v4&o7vm+eu*URPd zCjybhPdRJ`Z)>&M{fUW*_QuA>WUJL0#s?1l+$cf<;|74QU5+r#_K|?*zz<(~L_2r{G=ae1-26-Z4AWct$Yh=Pg|D0JVmg v3B>5gJP0P}+J}%P>{raH_t>3(T48?xfjBu!4WZ*c00000NkvXXu0mjfeJ3YS delta 1116 zcmV-i1f%=@2=NGzB!4(bL_t(&1?8H%PZUuU#%CYALI{E7DFzY(q1;kqh_wZsC5?sA z!otMH+S)%r{{srplG236RxOM%A)%4*SYm>>B(S?Pe&1!k>)By2*f1Bp$=!4BIcIjh zALq_6nY)D~Ng~?{XqdCJ$)3%^`IKnL?*u4lZa|9A&2GVnl7BaxAgG07_PE_WSQ(C} z4MT|1>gwv9!NI{trBdk@{Nw2T%n7I+#c{mTYPH_iYPDC(%gZ03tqGkGsa%CHAxwFF zef@b~U*8Li^RC8a|9;JSz5Z-zY3Vf%Xn`lzvMQ~ttSnAUOuQrHJe}xyk?jbdVgz!IGMQX^|kdDERgdtktMY@ zBsgiKCTeW!bk(8HUqhU*DM@4J4CsUUq)lTh?_7nHY?DG93r1EN&jFiOG5Gs$ZGOH za%Lqd7MfLMWU0>s$$8y?(t7`2AXPB0Wv3?udw>3Pbvl+@I?>KK&KoGL$7-_LGMC(+ z4$g|@sbKsl@}yDVr90kw2Yxm2+xJ>$)`1 zRSL~IS6Vo81Y~0#NME9$9?|6w$)>eQGc|f3Ib|8NW6ncr)TVBA2?(*@?~LtVKz@5r zo_}L$6O61h2}pBiErcC+Bn2?Z(&mBi9wElga*=zO-14(6>p&a7s7k`YYGuPt9tZB4Q7Mvvfq@^#$H$}GS)Q4h zaWF1B&<{fG@9#UE_+7>KHc&z!lEr}dihmvDQT&*}C5?}dlia`33WbZytkGyVn1h1@ zz5z<6r>8%Yff~yoO}GZJqr8Fb{MpUT%^OT)54{m)VPWB0rBc~|)|f1Q$zeUNRjbu| zBO@a}x3;!M4i69e;p^g!A|x z!39@n-xz#66ev{1Ei=B|1zZa{U*dNPYpf16o)e7qSw-S~Bvuixpli#V`MEa$)ght= i{U4z54erigtf*gi{7y8KKBlDr0000V9i+y*>{8yBKNGC%pTUNhBnaA!&j9!+ZCef7E*HO?>$>OaZ{WVKqI#c_P^ z`~Jf7yrZdL3{U#p@AuD5)7+M2`Km$l`TQXWg3H-#rU;}Q$A6i`0;ub{lg(yxog|4z zW>*?S9}7yPq9{_X>xzK-zE^c!tL=7sfvGi!(gXxkNCc2X0fd`r+cvU6irz;{0s;>V zO!Gtf9dshR3rh+FAKO!dKxavUY)=ES=~6Iw0)`raxcO%wCgYSW49=dTMBJ2uaRB&f z5E?8g2z+c$lYhvLDFuTkzqhl~9&b-VIyV9UZQ48ztW%JckVx7(#?NWc#N85EXZmSyh- zv|KKqs;au7)q6}$oeV$zwhn2!G)=#XqIiUlgC^A}X(dfMC+XEo=hUjn;DvNYdiw^6 WI9{5Iuc0si00003Is?_;=eF&m07JthX+P_T6rS9Z{BV(arXPY`j7GfsjBKMNs@ct z_g9|h9nA$}c+zLrb{V2?(Z;2q1|92shLBeQbjiy^odz1RfZe z=7;n<=tOuImJ|p+wxlgN%K1+zU3$fiqy;5oTECUa8? z#J=z|iO`gOqaPqz5hLvm7yD!GcIDf^mW5XshP_F&SS&u;w$0FxfF1rbC@jA`&)*HG zEX$|5uCHnJ9#c~%!;inMLz*GWvag~j9^vDlNp(uPB_y4b^y;N^ZZ%}^Lb@ZpeFKR= VUYhuY2+{xm002ovPDHLkV1h-szy<&S diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-small-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/btn-small-on@2x.png index 0896a6c370ffe3bc1b5d3f88a11d5ea860d52b26..eb0cef5dd20196b901a17dbaa279078e63ff8f50 100644 GIT binary patch delta 917 zcmV;G18V%m2gL`FK7Z`e!4!uKbrQD_x;Z(y2#SKMe}RL$gNuKGV+&o2xGR-X3N95K zA_ztinopl6_IW+I$;C|O+#Wcbn|n{;`MuA%Z%W_B!{IPGPmw!MFl5Omff|j*RxX!& zxUjHrD~{tU$V#$jUY|Kl$Tso|@@=ozd%3r__rdCk*`#PTn}5#=g~IcEJ|6=p0$2o~ z(PLHh{_iTCto?pp+3R+@Pj+{AUrA2@+TPyYDwRs_ip63Ks0dIIp$IZ>7>Om!U@#cL zz?}#%QK!@Cot~cF+}YXr$i6UgfO)jIxELcNK)D5yLuW8t^gI^)0a%`n0aYrM+jON| zE{}ldk{vlVdw&Q4QJ*FV^^UX!hX8XKE=5Yh*>Riq*b#)m%A`Zo#A|51HUR`TFlohz z#RSl_CN+Dh)R=^lwy8i_aY#B-9kLC!N%UUo zOb!u-jej=108Gr==nF_=Qf@1X=Gi?e2u~1%tsBxx|W1~&4 zK-6Uopk_sQhj_!n*d{f5s%csSP_iNO?@$sb3x5vTvD5UVI+O(U3i)z#G>&~y*^iP|g>Ki@|_M*jU>0v{X)kHcRe-$-K;i1%yAyJz(x rYB(~-wTJix5iR6mboy4t_Zca`vf}-H+U*O>G;NX9tTMJ!_IEz$5DY#T{ zh#(k6$hXfE`@EjqH>Gdm;cyt8r^uZr7_#J(K-=5fTe)2B z!NS7A%{Y#)AS=n9d41+IAzR2V$hW;-@5RBv!3V1+W|LxPXMg8up-_01&*x(RMF5Kc zGtleORPD|_8;_woMz{!8fzK)buUTcuLzU9nh<0Tlr%A{0U94I{CH84Ly^ z7`PJwChBxLy>`2OV{dQoBm2V00p{W2;$n=90Ob})4xPbp(eqgF`(Sx822`n3Zqb!; zxjX`*OLpYg?0+EyM17hd)Z5Y)90JT`xD+V~XUA>cV@D7ME0YdU6R)B5+5`~Xz@!x; zl8c@Wc?`Zs48rhT0$5o(gG02BNoRDeCU2Gc2qT@i{<9TBXaJ%cN;_)aV5CP{&*0L5 z2*YMp&tY|1t-&GQv`Hwh!Euw;llz+5}cYmRxNr`>ll!llGNUSNbYd|965n`1){#0 zMvjfL@LV}0D9;Y@Pg$BIR>jyZClEEN$z!EfWhxLK2#VEf%(N*8#6o!mqQ;pQEMdWP)H=0}zf;2588s zDGNXZpiZ1;^?A(*Kub$YUF`2A-V}@~?|BUA!!c?{`U23(%F6dp;C;KjxQ}l8s)2+KkTO#>U2xv<8Q2wb~&Zy549suJTlj?r0Zb^Ss^C z0)GpJXRFBJ=H}+-wY9ZF0fm3Oz>PZc)#>T!g_Dz$ORZL`f|KR~R${bK=m+Lm34_J% zV*~3hFE9UGUtj;ay1M!Un(iV$QJV$g=X=OU$iKf!;Dh7farkrOYiUdZ@jjipb5<{+ rh9h%adx&2U(L}ySzCj)%L;MEIRW(^KVp{`h?tPiSlwliLF~f9{{}cg~lCEwdZV9tZ)AW6{T0KDX(}k#>On)ZPcs#xt4u{HhIn_P@ z3D)cNEg&yYyEAs-Wj+`Tl;?T0(aNA!7=}#z8X#@b&-2`aZHnK^X3!mIpHb6*2#{eD zL`{I43J4Q68Aj_c`Ld!@fT&4WSrI2r1rpubiX@AsUh>an998uD(ad67h!Mesup}YONEHl-snTLmknowN4hTgZ^4)6I5%R z*1G&lnpVSeOqBK!B1u=QlTB?Pa<&$P$s8R~OLvtlk_K_y>3%KUX_!d>sG)002ovPDHLkV1hJa B-UR>v delta 455 zcmV;&0XY8U1K|UZB!8$$L_t(Y4aJqeO2beX#cysC5RAnxg|tIU=90m&gYM$sd-w*v zK;J+d@(d2Xf~!;M*hL70IEjS}ZT@&p@C&K!(&nlkT=Msx-*-*|pH-HnvMft0rDWmM zSQSvp!6TWiQL+@(!ABHbk}W`+wnRG`boNhsw5jt9=1- z7DdrDAdgVHsp7)LbTAkw$8i{=kwq=f^Vs+$K)PzbC<>?3=_r0Llf`uMJU^jk1tLI} zNzk{ZRfFJ2ahWcHM7MS%H%!8)Nf_&IK_oXFNf*buh3sd3z6ms+lj78HVu-b zwj7y)h%4%e=6@DQvXvLx9(O!B_t! z$GYJ@>M=fQN;+H7$xCe@vUe{CP6|v!Ez?!HkWGgk38BV%y;hiW8TZps(d~9W<2XL= z_xpTMTR#-Q_a!zAJe4Gga$Wa>j0!ZFOr95u#qE4PcR-OV$9wByO1)lh6$HT(#Bz%r xq-pBG$vw_5V7nTu9=B!5LoL_t(&1>KsxPZU8I#^;XX!;tV1I1l6ki2=&9)EHuGM`cH2 zVYIL?v9Y%H577UB!jF>DL+q4QGfD=69l!e&Du+<2P?th z5*R}Ct*)-#9UmWm+~41S8~qB#h8Yu3*@ODV@$vDO-QC^SD=RDCpsmA+MXESM=n$r~ zwzl?SXlUpq+IeT=qW7=UZnvK=FE78r1zqs;G_9DKrAnppkuXzJQ?6RAy1~Ig*Vor~ z(La7Eah*=b9e*Akx_Z6twzjtTo1I3ZaR-_Dj&_$*j!?z1v9Tx2*!1+YTUc1&G>j|8 zf+|=Fu2d?y#l^)~*XHJC2}|S=I?qtMoN6qEfq{V~HfCmKf`y?z7Ts*#d=|{4dvvqf z-RwFt$zg_g zdY%)M)p_NBbW<%%CQv$%eH*Cp5Ug~`T21F>C@a`r7($yGRi)=#AO(bM(=gQPfh0UD zNMoT{YGsjS+XKmY)qv7^{|g{yM#TfkktVi&wSV)ldP1Pl<75D*2Y zFU$+V{;q(mHV>pgJ1AcW_M*BxkS>tbBYFrhX${gwt*VOgKynC(ecRS}DAs~lodSaI z?|*ZV?m&o8(k3A3){PdB`mwb^wtXrnfR41uQj!M(IbJ99iaf*Qk)JL00_(r*wz8w& z?RLBIvcNkK9`Cb6d>g{K7UAUXn4Y7fBgYpdgixhFgd#n()JUx?NB|r0no%mEk&%(s z!NEZphp=vLZZ7Bzfe?ghHk+{>9Uc9h0DnaUB3W;CcJ>#J$;&%CJ8=+>_#MwM^-2pd zK@?6ie01Tqx3?WWyx<$42-<%@Ts-YcAdR?w0PuN!e*WhA`uYt*v4&o7vm+eu*URPd zCjybhPdRJ`Z)>&M{fUW*_QuA>WUJL0#s?1l+$cf<;|74QU5+r#_K|?*zz<(~L_2r{G=ae1-26-Z4AWct$Yh=Pg|D0JVmg v3B>5gJP0P}+J}%P>{raH_t>3(T48?xfjBu!4WZ*c00000NkvXXu0mjfeJ3YS delta 1116 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 P00000NkvXXu0mjflsLJi diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-active@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-active@2x.png index 7f01778ff137e9893991d44d8527fb9d47a10a37..27f4935f340f1d193068c821fdc8c0b7ea7bbc44 100644 GIT binary patch delta 2132 zcmV-a2&?z+5aAGzB!4JLL_t(|0qt8&Y*bYgzB3)#nGS6cDA3ZD(Ek<)Kb4JAjVXH)lup}eJN5g{+%p{q z+rF8(?@b4KC-48wIp6u-x#ygB-+Q5jg$1+L+%XV#Cu&U+8Gl$K)Uh9|5$c%Gh+}zq zi937tY-N3Y{d@S+8lB3{RPu^4<0=DW_Wlwxp*kpwNR;&()sh}_k_dY z?_qAYW0U_#rBX*vpFaJYW4WHhm4wjI(b4B|A%6tI`1bAFO>b|n*|~G4si>&%1UmPm z`T2Qs`}S>f{eSv(bLY+-9-aiLFN4&H+;jcj4xG@jv9aAS_9qxt*VWZ!1_lP4+Bh*W zVQ$>GL7D%c2Rt)0H1xYu1uF;(l+clpky?1+ufWva(a~WJA3iMbK(OIdX!r?*LI&i? zM1kYugKxd zEf7Lc9!1b4zos?&_wP4#b#-Dmx+8dW`=d;fN*&b224Y8uK{kAz1wtrl%{O7k?uLd2 zbMW9nF@K5PE&@?AWnGl+Dc}123R}59(*@(`*ciNdWmU^Nmmr zegN+PG4I*K)pRnFl>lcNONn8_h7Hs_XtDW%!Z$(@JKqQ5(5_v(q`+q8<;2()W8@T3ucJ z8i3UDetP4^jTu6N^vrBsT3HYaF%g@^m@i)|p6?5x(E2Vlx3#qifcd%fpoMIdXr>qx z6JQ8N*KnK@ih$2slS51ZTK_S-=aw`D9Vzzh>-W~Z0v3l z!-6W6me8ZgiI@}{lG6!pyKGJgwRa&L2+7>H2U*Ck^a~bL9K>h6w8>%?f?dp<5{eNW=|W9SO@eK1wtw`o zGuLYc6R*0(sF+=lNg2)vy>jJB4dlH-q1vUG2!i*+wDaDN;k79Evejqy{xR^w<^w|~~!P$Zp>Vkl}nCZkYy zS2v%98+NPtl>ejhxt`~Bo7jkvSc#d~tqr9o)Yfs5-8;L|^<8qdWbbNuCPXnRX8iGl zi<-3PE|2Fl11OMNSl2Z+G|FAkY!WeQl_jHGJjBSmCt@ac+CW=8Uy&tFEPt9%0Fi($ z55j<&K)rUoQlF<-b(~afS%NE1yNe=}?65Y0e`)k~7IN@y*5PEtoN2LL2OWNsqOyAWjSJ^62g# zP@@L)nVA{MntJ8SWywrW?Af-cZL%}ZSwVy%8TCTXOVp(gzIgYsc-_luP4Ar47TRWQ zq=|X8f(VU7BA-J`nC~h@qftpjC;YsMk2M zWcvF0xG*!*=`JH1Wja$E)F#;YdLj4sc@v8G@&!1n;2=mm9+xZ27z6-iJ<$ewDQ&U? zJYkenv^B4m7oo_0&w)63X4H4!@$pV&EZ0l1{}^r7<6f4Y3B?`6I@D;R5E|llMK!fG zhOuXQdb+GVGrMQe7Ju5rLZ3F$R@zM4y^zUsJQIrU@G+G4^3_EB=s>TiS7qgJp4vnk z^>uQP{E1iWo(ScaZxHw;h3?z8PezgwiG-P+fpnPbKiZYMBgzgQR z^?X+JTz@(~CnggTH*yuH<_4RPUd5CEM6TqvFeySD!F>~s=0OR7Hvx*1bmC-*!}zWf5xZesceHY zgo63M>;XPsuYb^!nC%##dDW(;rp?2L5B1O!v0T4?y@|%6Lh7|^*W`=mEW>$6gV<-x zsZ*z(;xhZ|GI?K`JGXA#DzDOz%-Y&oA(Mexy8+L6QW_nI-v!`(QgN)kd-n>dckkXc z_wL=R0uP)rE#sjej48%cR#wW@&@=d$051YaM%R5$;!QGI6*aL~%&e@D8Z|dLg>HHUftXNkUwew0000< KMNUMnLSTZ5!zr%- delta 2146 zcmV-o2%Y!g5bqF>B!4zZL_t(|0qt8$Y*b|w{%1NZw9^Ly=t_fuL|BmkU6|O%wA1O-?>qg^%ybIF zJpOxU#`d4w$K1L1obP=1{O6qizjp!)3kznoxnm&cPSmO-GJmj2sAE4^CDbvYA;FMb)J9q9h6%`d(flfbZ zetzEEy?fW(ynlJq+`D&=KTm2mf@c;0eNfC3JXrxE@~kJ217ix3`Q8-4A@X$17h5MXmWJ?C9IN zb*njW;D3OaM5DcGDx12tZQI71)dLLgc%?5@uIGeW&-j!|nbp}C>(ppD;CMyZ8c(~r z7Ry{DsLwc%aI9*XIL`^)uwlaqXlKQ{nK=mta^cy;HlMbahU_ z`mn@Nl9AZ({S063b#3V)~GX9;IU*x`}~Uh>6%NM(#Upo)8Lz?-FHeYpZ~jy0b{$ss5^U zsbW%$0?^^gIiU#n9GVp_RObAJ}47>LOxr-K%2&ayIH;G9sDC2vt7$L(x1 zgfWcuU@;d>PQ;|xkep8PyUXU3P&*3YL`Y)XE(yUU;sRtTmX3}Nk%Ak1!u80>DWMxT zZX5v8CVIe@EnCDRV{;h(6)72|>m?TU65_L7+GH^oaGRE1>XcB-=tvi8YHAWK#eXc7 zG#RCDHI^zy#k`c3{J%LP^y<~Cb%60Ip=g(4xsvWB#r`gsQZN#;#ZKr2H)n)KA`#Y} z;pXOMQ&(3fi6}OYE>>b6Vqz>MMq(vqU_VwclU9H;LeX{Jrp#Ttc8Taxik<1bLa+g) z)}+8Lm5Bf^aOH&1%a<>=1IIyP(SKR_x!7EZ`DU`O5F49@ChR_EZII)Iteg;vF2rOM zV9|`bz4(ZOGfO3s-P3V!28tsmVzq;hfVH6zF6G*S3AJ^cWOu#ebJ5cEPW4k_WK+!8 zJ?Ww*EtpU|r|Cy+*UY+(GxH4i(Yd0%{3ax2vm!=fC1zr$4YVbjYcriwFn^%{A^~0I z8qvk)=5Xgrw5zA%Qmi^ps`A{BL@3`?f`X$|(%I1oym&Ci(MXwm6f3dYcJ#`X zD-HT3pKU>e+H#|Y^;~UTtvuq8kw}?%s7%}-K@odPa|`*aiAJN65Aq?C*Azl%05eN` z%#kRS7?;Y$I~AwK`;N}V34ar7Q-F5m)AAC-67qOcGoy9!jkHOt7oA`-4FIb70 z*lB~EFzL3o<-=$G9*XY%0X1qskIpV;O}+EwvX!a?*i&s$+vH%5tGqlE$*2c)lCfMw26&2(pK7xG5;q{3FSZEAn;2H-Me=$HzqP2F%roC zC*GOrrL>JrZAIP~vK+|qOsgk)C|~`zCMOnSXIayh89Zy!x>`KQ#i;(;wQJ_i zojbf>9LurOr%(Up|R=lh_7Q$(){^He6Oh!y9^rOB$(hgn(~h z|LDVq4}Zm9$$#UiTqFpEH~wRP;PdqgJ&D;)k`Q8LWF}&!Ca3hL6|oK1hE4U_Y9aN; zjT`ch=M=+PXG!dX*FMGLssApM_oaDo^XAR+Dh~9z1xk2Ip}}w1kI(Fy?DhRaGTV16%bBKFp$LYSF95SK5{_wYs|6 zR8}TSQYL4PuGm1h%%LU-#g9TLe`ImX=PjHolsQfwTy4St9ga(7Ri(iMN`e)<8YzVS Y2MR@L&B-wkr2qf`07*qoM6N<$f;nw5zW@LL diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-hover.png index 9c80775b948481740a75befb62234b2401ef6df1..1a3a02868f838f7ce83913ce8e3617faea77daa2 100644 GIT binary patch delta 807 zcmV+?1K9kc2E7K5BYy(~Nkluk75xC96Uj0qbV7DP>ErAxy= zl<30DB%_HL&8)|T(U=$?z=azhfygbaeEpn`Y=1VpDi(`BxAik^JN4CS z^~7v8|Hx!A2jOs7!f!+Q+Pcs6bd!PjvFp+1=?sd?!fd{E|)uWyWP@w zJXW=_TCGyA*OOYU)<2WUl+EY!e^dh$ZYrliq2tme4(@w8oj!EATpJ-7$APdfV;GA| zrX@X1KXrrFYJatpKp=1&i9}=o7)x3{gk?-96gsZg>rXV%Ep^fjimCe$jYg&EbgBt( zc@T_YEHvIPF=+a!3l!OW==1rGJRXlUnQ$}fI~cP_3%WbnMGApu5 zt-XL^r-xzo*zfnt*%ZclTV|CS)S{+VSj`zlJv>h)lYa-Si~Q&X6#Yf~LhIacP)yZn zFc{QSjy9RI0#J*Z=&PwzY04mYEcu~_cslU_cei{-znNCtioQu+%e@?BX5xYPuXnN%4c!E-(T&&*K?=SDR10%yFJDLJ|ffKN~O|YnqGtM ll_0$rLKpbY~_x*mJ z-}C%_Gh>?1=hAf;=Ia1SM$1(|tJUfqi^cKBHNmxCvwzv_yi_XvFzRPAy7aYL z?Z|94f6r#Kdyz;)!m%m?0Ht29Cw05sDZCyRi^aO?jcZOC0&O;%w_y4+pU>~R-EL_- z9&6fItyZbu?@R4=`>)Al$`uNQ-EK6y?=fb3-}d|sX;AjioyzK6#eitm48a@u`d$N=wK{0mRhHxI>Mk> zs^d^7q^r_bWt$yrIIgrmWpM6OYEcu~cSW))1PY;J^uLWj zDq;EPMXNg52xz5JaYE=Rn*2tVT?Xay2Lb`^_a{paP2RN`wb^VXWYdM2Z#<*aq&7V? zDRFTvD1Yubk&9@ZnBMDhQPE>vtR`ry)rv#lfyd+7=&~!9pW5_TdadNu1Vwe8rPFCM zo3t#x>3?d|gI+5sabYDWH#qjtb5{FkG};;{wdsNJLaV4&g5qWOC7$bkZuo6ylpc5^ zyFt%YSycta%vj~;>~q`9ro4^lvFxdD>8%E<3V&+1+dn0f$q+vwSee@%^q?0!gJ@Ns z)%-4j;-jsMnZ8aW64G!u+}hrhvnl@%1_Ozn7-x7_efB@>#m5~}j#<6Jl7GzQa#lWz z8NAEkbUGJXt<&j@F@X=r^w)B^{D%w|pt}Sz;&o~1jfm_b<{!5jV+u+Pi2MKm002ov KP6b4+LSTaQ&WMr# diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-hover@2x.png index 82e931f804dd732d1f75b31f7d666895510f9972..4d1a1541637274121b50c2f9723a6b98fa747946 100644 GIT binary patch delta 1832 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 JLUX4v9%@1D6Y1I zjYgx4>iJGUu{nES@)TRjPSP%cmx}5MJ+{HBMD!H@C;`k|tB53^Y*|p$q&6T=wLy{g z^Ia8@gnyK+EGl|vfg&Y0A@I=e_wO|A%b?Vz$J}cz@5a<)rWe@n99;N1d#40jZK+9Z zdeCbvC2p(*oiFegEFvpUNvkm1ep8zsSg$WP71df$419$r_b3-UXT7h3+VsFvRfL}F zd`zG4F|z)9J!1hkrMKzq1QVAEprn2x}d_VW_o;_DSq{H0ZJ_YEJ#_zIy5p*o&k^hEy)yhS`)5YE3WQqOc_jl9PB zF`v)h;0(9Dw5^Ki{&u_Fv)u(tUso%GuUH$)ml0TrIcQ|DL P015yANkvXXu0mjf8HRR^ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-inactive@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/classic/tab-inactive@2x.png index 5e57ee892f46ba9a22efa2d609ab75e267bea140..d2885b6b7e953cfdc09784af710cb3ddee18de83 100644 GIT binary patch delta 1869 zcmV-T2eSC{4%`lqB!44GL_t(|0qt8$Yh* z2yvoD7mDCYbm1f97l^nR3GPBb3`rp1P9=gX(M1v=f^iiR$a68`+5P|Wkqdo zZ}ap&`0W*l`hTOhuIuBB3pz71a}{KN2C<5kmKN3B-EEcQ!oq@DU0tQkztKR?_VxAs zZq>mV#xg4C#Kc5-QBl!vz!Yt6ZdN@#J?iM_NF5v;=q+gXgFaXl6cp$!5{an!`FXXr zwnplI?d|P7GdMVS?+hKRoeq{`0o_B)&v9wAwzgJvb$@lK-Q8Vv|NecozrU}7ib*KE zmNsaMHfftaP&$lH?q{TAOi=WjyJ-hdR0#VYW8fq;2}3FBbUzk{I-X$x~~E~tslj|rYrBNAm=a;oF1 z&^CS0S7T$NxcLOBUmyprzMTuYwY7Bz;G>Mry1Ke}blOVsC)trGK2!RSmB)CG~p z*_5>B6soeal2tkkrpHyc@|#mZkz(%?B?V=XzVk_G>f(2aQj_)^@iit5$ias*K`{Vy z0dFrcH#IfI`%VWOlA4q*h8RjpN?7-X!It#8peLOPib!}Dph6_VTSJb#++0LqGMn^C zQGbb{qM}0UWUvL*Ea`Pl1RWnAFNN1ZdX{O6q+U`qj!sfk7QuquL4#5MZu7&5ptye? z0LXIoolG+lk^v%FG`Tt_eK)a~{+thKh|zj+A}DVA-Xc=Y0a?y7IyHa<41!58%6VJg z)&-rKn(9QPU1v1N{4bjXB_?7cMq;%kq<`44E+`DYPUB@|WqJUTbaR!?soG8Kc8HAy z1J#h;vG`$CP;(c;2O;`4lixBRaR0oEVBsw=3mW5&?K>x{g5oaZ25`1=dQx6q9*@ve zqsVnV85oJpOrcs0W-YLd537P=ey39?QA`^kZQ4i_m39TQEg#kdot>R!$J9q)l7CW6 zG1pP&*SN$^t6jLLGh%V^SV;b^9lEo{qq|ojVxp+TXy&A)cu9EMTQBIb zH9-O34T9i92u2No`;DGV)fa3D4@~sTg!t1fRs_X-?kNDdPB4=0?5nc12sXirhJ4O& zNE@;hLCt(FBmkLG+1P(d1_(Z?XMZq*f}J^O+JbT(2cUX%=Yw2=mIz3@xg8pi(cZkj zC+;B|bJDa0#iD{<@V>&SQZ`SQylo?b3T|>RT)b6%9j7fQS~h;GNzVe5k(%wBWRl#9 z3Kw4rdMc+SDAy{%-YwdcbkD}Al81BrP)2)6dz`q1r}_Uqg^t(qx09Bjn19PLI!pKh zEOG9c?U5p~1h-guL=mnb!yLf(3*9tRc_sc{od zo@S@cI}Wz=Me08#N{N}+$$?zR$#6?m!-whx78DN^-lHYy&_&FLtY3fw3M)}6T!dQ? z_<;mP3&!B=Wjah;z#y%D(SN>WAQdj;W;l|fuM$X5ypQ=5Ucv;xWrGAHpnQ4HlFEXg zT#Tq;!_hZF-%G;La#5tjAMljN_3>Pa&DFz_Hog&L$pu_W&Y23Ar*oY?3g10<_V@Sy z=?9|k2t~_%1_yb(2d0>8-HR?J1tSM?At#Aa{7kOCiZ4N@r>CDs|9`088FA4-Vu$X_ zYnE1~sN^JXYXNhRoc+K}S(CPlrhh1?|j$k~kgb<~2IB;)-^%Fp5d zJUcr(*I@J=TsVX+M1#?2RR5uhj>K6q!Z#@BWk$8Ju|ZlV*v9b?<*UWT z#a~87MtB@Aj|dt+liLZTK+=>CWbuD~QBF|H0gw~avY`J0ct~3gP;yVw00000NkvXX Hu0mjf5^|35 delta 1892 zcmV-q2b=iZ4)YF>B!4TWuTS!8P z5WCgrLJ?evE*xThfryKi;4TEzkOTs*1kJ*g=pu=rU|fWdK*mMH%!A)|y{EdU?n+g? zcVAT{)`eGZKIc2%J?Gr>-n-?yxVTW+W{&~CJyBUnWFSkZWq&`&5^9;yT+8y35;rw9 zmGATU-o)}UZoGx%siXuvy7&N_zhFH08p~IMgM-J9PNjCsm##qxWOn)e{%>J!(6Y(D zpP!!(fz&0-auX7l7D7Sh9gz7Q5C$qMD^+uIv#PDFRXCOq=;(_;pxW5jP^+t}YG-GM zr~kpRmq6+d(SO%^y`OSIXJ=<`!q}f+SZQNpqw4PNwrbS$h$zf9UmX-#cBFM4p#a3`FhFC%~cBv3u=9R zou>bFczF2q@bK_n93HHlb{1m})q~8>aBD|ZRh8=M>VHxP2M20@e_tIP9Vw?|5(@97 z49cQR%BBvqcB7NqDQPJq6nV237L71Z27^Hb&(Yp_c6MgXP|BjmQ#N&&oIGN5Thnuy zpp+0gKR=%f<31;lwzf8vlar%p{K?6Q_AYvut%Wivn>whA4Z6P|2EF6)Pf7^g+S+;r zcHAs4FMn4}O--6So1PiX5oNQ3sH>u)f~P|m{a=s1&WnA|2{qpNA;GgWqM-~+mO5TF z%BBwLs;jFLH6J1L^JT}YwdaIxZ*SiR_zrq!O-)VMJ8gw{EIHz(9Gr}owjlIJydqNM zdP?Zn*q9$BjrPuHG6jHFYZ5J_GBQkikCrRw;N2+ms;ERlu(4&yF^Mt*(7(y3RBz1C6`jS344zC8WWFs(L5s*13(u5_Y!n{ zeZ9_|UJgXD$e~Vl6fqPP74fpW23xFKTpjm}PG~S{IO2x^z?KP zM%?BAAoX85y;Han48%li#7L}i-qzYWp??7OD!~;O7i)~EE|tV1qt^_1Vu{3tf$D)i zVDZN)q2?)sFG9kPnKB?)#OzoUv5@HM>S`?m+xKlhIawtXPa$`Kw29>jJ(S=j)Eu*5 z@kYs`B_$AKrV$>R;b8~Y{ntcSukblZF z?-atMlO^nl*jJFv3JMCswP!lL@kzQ#D03+>nwm6#kAy?6Sd6&Y z8ljN+8s*AM2nj77%d{rgq$Wk!>&nCiPAi0>p6djd+XNID?oaZNOgJWCZ@!E&Jw0n| zaLuU|LQOsA<4a^VhEyh%OVKPv;(s%kVS=5S)HR{3U*^jj=FDkuR8O zgN>ThHKCYP&bk2yrZ6U0 z1-sZH=bfxw5z4trP})z$5<2YlSYeiF$B8u(JGT4rHvhl3(E3dBKduNxEq_PvEaC?+ zPS)b+DJ4nrP*Q(~;3alblNPxhoZJw~k&&M{!rm$6NTzw>Wq8B^q$V{s`J8sIV?q%c zFF@%H<{uXG1T8tqDVTKJN6f@d8%!>}VQg~*$MFOzJYBp)jq+BBz$7O*LXxJN#1S~J zl(y{c?eSAHZTsE{Bu5^K9)Ao$**z+hVqQYW35ayxBiO|j;JIgPOd6UaLh(K3V`%ad z0DG2z1XR)pGT;WmPg@MBeq&?O=#riij-Km4Nc;g!Ih@j+mzSrfb6>^$8OavO?N zrJPcZ#+oWPFfj0E&1lSz_f44_TorGGd~Ta#)@5jrz7 z^Bj`6{$?bT2E3Ss%z+{u3vFSOueI15rp@|bfDV9 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close-selected.png index 12032b2acfb70144b1177499dceda333d9245662..cace18cf35b0ccc6aa6f2c906c0c3ec7cab70c48 100644 GIT binary patch delta 338 zcmV-Y0j>V=0__5jB!4tXL_t(I5w%mhPJ~bp^%@MDSedZ6CU%xKv=&fV&|6qb=@&?0 zOKHbP@GJZQ$x19Xx{y#2Kmy@<&OQvNkxe!`$>hvAb7uyIi;z+t?PJIL6tng)4TKOU zrfHrLIprs(_HEoj-;q$mFnlP>a_;-SK~5y6PChu_CyX^syMM^CENa_U=6Svdg5Zuw zPM!IF&0sncu&HHP_wTT#Y2?<)sWU&;V1h%}j^jMnb=|=wk(@ewGS)zKUH2wU(~qjE zB;LIvl2b>6|97D4`gxKh^P(tZ9LG!BwqJ@>fnPN zh|S`luMOY)6id;Sq9`+Let~!ipMhV3Gk!%)0bJQ%4R|}bNM53P<0_Eez(3KT1Fq~> kgKc70Jj}na>vW#`0&5jP0t?KIO#lD@07*qoM6N<$f)|XT+W-In delta 342 zcmV-c0jd7&0`UTnB!4(bL_t(I5w%mXPJ~bp6oVed%7o*Lv9q+HwSdZk-ojc+zd#CG zN;^J+U*Qi7XRx^F385ka0gmU)=^0QXmt1a>$?VL&-A5LXgb+KM#O@}|?A^`)Ns{&q z!#E=ImY&8ld3Nxoz9`RaLcsAXC!^l%WA5$8j!_BzY~% zQWQnelBwxq!2dhYG;I>c@f1rFQ54NB%X%PF(`S9?3C6DL-i2ZKtn2zMZoDK@(}(Wu zK>Au7{LjEUKTkwWZ@>il6nc*Q2|e%NW&AaOU*`e9kEjGHU?1ReYoNe@ o7QBpK16GA~c{~5Yy0`P(2jP4{LCo;B5dZ)H07*qoM6N<$f~R+?G5`Po diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-close-selected@2x.png index 672ed1f3f285d47b0c7987804ae3bdfe87d60348..63dcb2abfac8b563342cb11bd6c2e16e0b940360 100644 GIT binary patch delta 848 zcmV-W1F!ty2ImHlB!4bRL_t(o3GJ56OH^SL#+#`zb;2*)1gT)S2^3_A7J)S2I$GAo zg-aKaAWoXp#$~&_R6>Ke{J(+8lgrj?X=}^AhzS2a@+}Zf-s(7K_J+hlelq+$VGi z6akR?u)%?==N_Tss`!m2?_FD4dsrwGPAZj(5#0U#{Z|U!FOV4Yqr(OVExmM^we2cSoo;mF90$+-_DND`1ts!>`f1%k$61*IGIcy>9`Gv78e)S6?j&7 z;$apYHaKwck&RC1w%^*88#w{4;EpggF){J{;D6xYYa)?&uV-2Xuq`T z&H;J20&{@gX5{|Vo)_5Zb7U4jDR495@a3n4?*hK)Yp%K{fR|2zIlxsgh!`{^D+&x5 zWJx&uDPiYl1-UGcyd+L@1t^GL1T%qR7=iA^z+R!);gM97;N-IZ9S{huT2$N8)h?iY a0)GI#^LudJSOp*e0000)V1hHyN+S1SIjPFzLkl|xsl7_J9O`gu3 zbKi6BnS1BlX^Z1Hp{s1+tDKy|WKiSLMiSuZ23A&9 zl9fuOYFU=!!QR~5d=IkO9=n4UAACheBMIUgl#ha(#rwD<@T z@O=-0Yyt1UE%8vioS&cn%mjKup7Ioop+ReWsG5e}0e@;bD%i^>Vq$J?Zn;=2)=nmX zjWIMvTi<{6Tm7~m$U($mQx<>4br>EVZVQLQEd=#8Y>dfvAbtvb@Fm9C%`Y>rU7pnW z4Kc8`w)R2GYzs>5ENnDrjZfGc>tsL3JK!ENee3J%@3hRea9r~Fyan#Px!P!z03}Kl z`6dx00e_aXXJcbyL3M2l$0e7`S<}}i z#K)RGr4_~}Pqm=uKvA9< z!EyUX*OFiYCTEORGvuY=yvT}Qf^pw-;KCJR#Bmr-cB{fx8d8h~p3SZN-@~(hsgwc%XfvDpc#-pVy`-F>!m;c?H zw~rq`eGw-oC$&mQNJ#YOx36yxUw$C__}HrNI1Ko*2& zk*^wmjTyU-oHOC%ugIFQ8UP9d2}zNoyu7@EtW5vEdfJbEtUc3lJV1(DUGYliWAe*XNu z6R1!fs@VAZj~}~%>>gyzAT~xcA~}y6$VT?FAdn9aeV96Y2Ef$drLo2WUL9zX6dM2l X+o!)^NUhm)00000NkvXXu0mjfPTirV delta 321 zcmV-H0lxmI1Ed3xBmrxYB{fwTd#DHs3SZN-@~(hsgwc%XfvDpc#-ph$`-GdDm;deC zw~wDZeGw-sC$&mgP)PL0_pfh{Tz(+?_}HrNI1K%euaiSOk z3Pdz4zGl;|>y6zrzPx?^X)jPN_~om&2WvW9r+!ihYL{X>>P>??(fl9ccLiw{8C@s*gZyJ`=+S+Y6 z5EG{PGCMQhy!XwU_a>z{j)U`TqVsf4d|wE-h`|3Bfy-KxA%9lo2;uNH;)+0?zR@hD z=?T*F2Euz>vm$3aPk-Y<_*$efCG;W6H#D0SN`+M>O9rpk3p-oeT}zIEr=dpkt6HpS zX-YFq)3Cp{$GLai51kWlo`-V6zXb^>tZFJrkux(n+1V7UPy_|6yIipTeZ6a8{8doI zHRCO-TCFx`W`AUWSS$v&+YR4VSC9AicJmj;hgQNJ^J75*2>Hd!e%s{SVr{xg1%W^S zHn+B6ZEX#XaAn; z$EnKZ>qZbKC4pmpNyErffTqQeidRC8OdQkVBse}gsT)+?bW4*wQh1^utz z+D0Ao#tcd>TtW0Oo?%5$8J@v@%pR>50xlwO?h*J6Xf8Sr%XuUc00000NkvXX Hu0mjf4W+X7 delta 931 zcmV;U16=&O2e}83B!7TOL_t(o3GG!)OcOyAe!EC_Tj&oIy3&XTFf|AP%5Ne31`z{6 z43K#9Vmuf@~$1%L)+y0*TI<3u_yxB<&FA8xnd?gJ)KgiRIrZ(>~=eB{MhJRu=EB< zVGOr?YQq5+9+Dsb|I|yZ)Xp> z%9^fMf+Rj3taD2`xjZo-HMtTzFi52=u1Ub>JK%3r72B!|1tj?x+9+DYCRPCd>$RE8MjG1|=Lwu2?r!yti# z1ZlvCGJk#~W8ZQxh#@C8p+#-65`g;l`-kJ7zFa1eiS!iSzhH#a;FvX|uJxWLYUX1= zOauz*+RX~3!YGr`uqy@O-; z5>;waQz)9I_!@)WR#B=KzV<{6T=-%9v(_@Rp!56vu!#@R%^Jf%b3;vIXwpCC<4U diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-dirty-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/file-dirty-selected.png index d882c1d0f6f9ee5978f77a7dee2b7f85e442a3ec..b6cc22575bee573c5da455624983fd0b9049683c 100644 GIT binary patch delta 304 zcmV-00nh%_0@MPKEq_67f?B%Z}|Y46`iTHBBQ-Sch;pjbHDJ$H61%Ml zltdv>V0wu20APmDfgvum}q6JI_x_`#@_Gy-7+p4OR3F_Z4 znCb?$8B83Entp7)g`$u7_HirIDDCF@|U&p>ja2mU{g_zd2`8yJBD*n$oCg{DS6N6UcK zi;06ro`x5S7b$2DN?nY#uo`gV-mnevUhTQBv%w!m5CLIcCyd4b00002_g!*C~u0uFA((^`Ucgt z;Egxl30?+X)OCG>y6yvru%aLeK@s6X_$NwRb=B$l+03%I^EU6)!rpk`!I^i?bIvp8 zJ?9-A*L7p3X=hF|Ik`6iHWfJgFHPt;4wZ|0J>+;X7^kJM(0^p{Yh_N9F;rrbofAlo zc!c=dNK!Bt7Q%sIhtD`ZHc&v4Ul6X8%jJ9BZujM6GHETB%NgLBHJ;()`L15@Z}Foh z->%o|&lZctNJ-rw@Jv3>C1Be|gsp%r0@iA^7fOl+CO@*i#8;fF!q$IPKmpj}%lUl% z!E81g>kVBS;D4LT-l`{MnSvXeg&5pCINm2%g1PRdm@=k zJ~jn0t*26{CvXMhxEmiFk;B+sN+c3DNfgE4z!komcs2r<`NKvs1qxbMuW%;Nz{ChN zn=N4e@(`Zdi$=28L73aOU6X8GzG#ddkF~`+v^-HBvdCL~8CtJVA2VXei{t}D=LW7CS z9$ph33QzS`Z~FcIbTk^dLEsra_<9_;@pX~@edyQF`vTI*JHl<@Rwk3VQz#T3WwY74 z>2!KWhkw(e*Xs>+{B}yE(uY>7^-+GGg-=3XVCw>BI8=bhF^QlG7X?Jz5UzXI%sA%+ zMw!O~3HOAr-u1q6^LY+5n{xmnolG+koJ2nbO3P=L>R?mhhky^@8V+2eSyceGw-q!k zZ%j2fV)=jIzXZZaxbK1crK$9KwlZr0hlD@>iZ~ELVz$PgB;J%#Gq$0L<+HNL0(|00 l{rc)pDqiF`8v&aN{05v8UKlBr@E`yH002ovPDHLkV1h?wQ0)K! delta 702 zcmV;v0zv)P1=ag=V7&c>(D=_y+1a zkWDt(1+o;f=~~}F*FHc9G$fD`5Go=mNdAZ>nxvT-zh632LY!;vXrY@N_?XGP=bZ1G zd(XKyj_>=4i?mA@Ne=f~zu0>X+sf1c3FTN z@dydG5mGP{T;W8ySNAwRHc&#yuL?KH*g6?AVB~6|hCZTCMggMY z9q9{w8{nJE<$oR%OAeEWl`Ej&D?+N>Ztu@#Gd~Uv4-fYhdy5<<0bvEp$7oc0GL=g0 znUa{s)9Lh{Vz&ijyBjAKE&mWg=g7p_Mv)x zSBI0^?SFO$I)2-wQt3mh)%qyEPr{MV7seut?BSIH6r7L-vz{Q?b7EM^FIW906vTvJ3w;@s}-0#p8gr3 zXt^8Ml4S4i0}buerHd3pfin6*t0`EQ0;zOIhkURuRi@Zm|V~f#>zK kIS0;*D>BbozuK5M1PO~U!S;&VTqWI4i7$qa2jB1Ys+Y9Y2nS!BCtSO-NXi$AuPBg zc=>o36y&5CRFsv4Pn|l|EU#;M@ZW#NNsq4_dxxwCrwe#_co=xNxfmqGMH#%jJQ*Av z?Cp7ZI6HK7bl8Y8fRBfpft! delta 228 zcmVuK5M1KH3PoKDoVTqWI4i7$qa2jA^W5Z}}VeZY#BCtSA-NXi$AuPBg zczAdiKu_zVx|%A7fEq6XF>wa``^Wf4QAYZrhoi+|Ha4~+K#gyaZHBW!Nf{e{ e^yHHV0V)($4rR%NEW;H50000gmcB!8z#L_t(o3GJ87ZxTTm#@}I?9d;=!g@stEmVy)vP#^-0v?Lfs z>Y+`%c;N0o;KidCJ(=i{lS%&?Taq@hy~#!KR6SV_)xv)G4he~AX=R`Z7vAK_W@ni9 z`R%ix5Zkt4n&p_LIrp9c%n}evfDqy!Y(hT0oKPTz5JSm6}z(%q}bGdsE3&;emb? zi1Vt+VRPK9p8~zO)WTPLEEWS@55Zom)$4ry@+s_rpMS&p48|^Y8O#jX;!S`@EBDX8 z{11SIpA@!jg4H#^FbwedwBzRcH@or3?H{;=KP!wO?fOGP0DOhq2XOY|2XwY=3k=hO zGsA|iX?8Qom4hd>N*kZoCjR_8>JMLcS&adJ=1vMuhXf|Ez%(rA_Dt}&-JQnM$E{Ex z@EgsJQGXv%zb-Q#j0*!$rwo#W$LAnmoFRY(r-kg@)_O7f4V!jR`>6K7;D1{&01ev_ z%obxe(cG_j+nW#X?|W3Whcg}29*QS0a)|+8TN4l2w#iAknBC1JqWm16qPQ_nK)8ee zG|;Sg^wv>rW9^VqiC<(q>H`#yeyk#bpXL0PYC)h*Q2Zj}2~1Q%37`}I6LE+-Lh%G9 uF3|)gHay=z@Lh0z`Q-aEfLQ`lPv9?MX_{;-aFZ|q00006OYB!8nxL_t(o3GJ6lPZLoT$ItD|z0)$#q0Y47A!#dA9vvt}tPpK^ z6rwQ@7DgBB{Q_=Wx-o7{bV*#8@M%N>B*MycCAdLX;sUkv@*LO*YQtqn*z_d-OeXi{ zp5OV;xifcI5Cm|U5xLCdl)DMgbU?}h##n^0N6JMut`tBrpMP($vL9Dcy~W%`K}Z2y zX>Ugllw3q%E6Kj%V`keTEQl1qoq-Y`87+cknu;FPmOI_p+tl^qXc!1707_(8CX~w+ zux%UkSoCU4Xs^2Z$DVW)3i^3K0B!1`UAL=evU3Z{WZ{2OG1aICIDW7;w~Vc#J)mo)}*%(3QT@>ot=Y8iEaqCF-f2SSCE1DJ_^BDPUGC~_cl$T|=@ q;pqjWzXhik2z%cIXgYA|4*Uk$n4IRb=gyu00000?_hr26- zi;J^8I|o-sSXdYvvH>i}9Aq{t8!H0~GYi9?zkeCN{rvl1RaNb3T7-Z9K3-nVzsTxv z8oKZ1O`7adJC53{i`9|3B7i)=HT z4GKhT`1zX;nLu$JAO;6CHZ>qIoUs73fftBB!)?OBz-IssEvUjcqY+gZZXU%30073l Wcwa;8XeR&w002ovPDBK*LSTZD4|sb3 delta 271 zcmV+q0r39H0?7i9BmoeSB_n^MUu=n^V;B!UgK!$)>EXuc;pXHmBrmqW(J#sdmm!R} zVqxL>J^Mujcp1KY`NnYk_z8yVckdkpDxS1s)tq<8hT(Jp3kxd)3kwT_fB+wZtD7r> ztCN#GD;sA=SXdYvvH>i}9Aq{dD;ond(11UG{xN*}`Tf6=vhvmRsDOWcyu6%$k=5Ze zfRT}j;rE~aK+S*N8mcQERZ);U1k`vDh>0`c-IpJK1O<36I-BbrW@BSM0@U~x*=9H! z6o}aH^EV$df#Nzq3=U{)YCvK*V*zLbFA#r*+k}IG&j1`+P=#?uBdRjoJc?=C<$0j>n{`Mi+HWPj*EHZB*y!oqvU=bv;o zna=ed6a*K5(jyZ}X|E_rvqR|{cjB3htz8fxK+icx}1;4r*A*HFp+b&3c{U$ zhzSru077>qiXxmi8H4NBCLwz4D7akmMUU#5yEHX(xfKXb00jiLdX_?y6j_Fmkrbqo zNle3~I2^VI#xhMXYbIz_1Ijy^9`LzWl4oOUI8;M@MQNygiyMHw0fgY{3BgrcxM~wut+U45 z7(LgwmGTOQ7ZA0${V2wJP>IYz@Rhh2xRKY>&55 z%$Oy>U0eXf?KLAoPjRR}x++o1nv6xgf@0DCRYwud&vN!kWl>uw)?_S!wu&nObYgGb zDyoQL3AA0j3AAn4JVE0)Yrg;AuDbx;1UjC;FLq6^G(gIqxBvhE07*qoM6N<$g7)-L A!2kdN delta 723 zcmV;^0xbRJ1?vTnB!99=L_t(o3GJ56PZL2H#^0IQZLzZ3E>IMqMJj}pG=4>*gh)RG z18Ex)H9Zs)HA=jCGx1`Ycr)=zJeUyw1~1$=7%K@R0($A8h=~zBiQ%JkySu&v^WUcXpoNJp1l!iDg+(%c8Y3=iYUIdICZTWHK3=N`Ix;Q8um+!0hZN*Vhd; z6;4j{92JBRfErK;W2{G#q^Y6g?a5Fo<**AP1aQ$Gk|U7__}bf8vqwp{n~kpq;}hrY zKuDrDx3+pA*xd#Bl{_qeT<&l>J8$HI^>SwB^`_{2LJ9E68D$_l9B?)ifi(7R)8@YM^>D9!9 zRxOBl0%*)rpk-l0LSJt$j3*P&eI@{!`*_5os?)D3mlT@K~&l7ae;DxeN6VF26-F#%jX0$AqW zsyA@elz-CI(ZLII&06C&x~-w!p?)3eT)rR-K)5m9Ttf50jt;o!tuFtGx&FS>U-0K5 zY97TCsH%hkbOVixB`^#V9I})f8#*_yxEgeHTSdJ`@dTT22B!3A>L_t(I5o7%S|33pIfQb^#AicxD02cHxkXAR*U}0sM#>UFR z$iM(ZaPXhu_wPUd|Ni}x|M>cu%P?UK1D@VI^QLFUqFdj;e^+N?V}q$<`1R`-0~;&* zz4DxdmvBKK16v@z3G}pI#lp<;gM*!eft!ny0YtMfv;GS3w0~U#)bomv0SwCW@<+8b zlurP?!obbR3ACA&L0wt?jJmS&L1fJ!HZ~W4#DDs^IV|Jl;eN%!0yKo1^PQWk?J}VF zcaS79z-0hP&22pmm2E%&{QIY^s<@knjqMt;MmQUkl<{DpYq#&01LehVsbK_35CD=u t`RBOQ5Mux?y&zz5d|`lBBTSNF0|44Nah*xgBC!Af002ovPDHLkV1h#|l=A=p delta 297 zcmV+^0oMM)0>lE4B!3G@L_t(I5o7%S|33pIfQb^#AicxD02cHxkXAR*U}R#P#>UFZ zh~mM23=F@2{|1Km|NO_-&s>J-!!Y3K%`VEPs6>wmTesOJ?S0~pkm<&PR@ ztDj(EVrJk18o}p5F48dK;l2c{M?rD^YXr8VPR$9=Hz-B!6>BL_t(o3GG$QYZFlrf1lgUM_Xen+M=PBYP3PI6>F?wMGNLo zPy`Qx2SM;E_(ymbFW$ZC|B<*#YKu!1ELBu;k|Nfm$?iU9wxn#Ko4nl?df5k)o$Q;L z-|x+vnWai8a*&H1BsgLx1d~G(H zPGvJ`A_TZ~Vc@tf+4$EW!$Y|*&z?Ly4LD!J(xzL4)kpUiJ_>34!4q=9h#};zB47^} zrL^(*!JQAFR)75$AwRkV{+gy)o4h-AOqP$BXFan7a u866z(gmYhVSA;j$4b!JPKMY`?VZK0QaFk$lc z&3xa?es7k#u1ijGv6BQx?1VtlfusXT2ZYKXx;S?8Jt6cKxG#PFPz?0&{k4 zyS4ll{_i_-l?Vt*J~)(BbRkYLMhV(M%W+)d03KjUm&v6taLl1ZV9Q!AFMa%EZT$E- zF_1Bc5Q2m?0_(JGNp?CNGI*}={oR|zQNY;lf;a^@r z$d4)k3}2tAPPYaNXSN*RfR%zKz%<`*4~rkjrrWc%`xeABLva1=Co{w?aO@DWUHUs4|l=+IJ?e zqwrSLpnrEy#U+4uu|89sGNp9>_WHJ4ovbJeFGx^n5(WbTe*0`bw|;e`SnPDW>C2af zl!X__L1@S~B6#K?|Fo2HzSrxg3?ua!4l2Li_@% diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open.png index d96748de6d9a024812982e61b5d6ce60d79dd3c8..653d14fd2bbb358438c109dbc70efa3fc6bdbc0f 100644 GIT binary patch delta 297 zcmV+^0oMM;0>lE4B!3G@L_t(I5o7%S|33pIfQb^#AicxD02cHxaP&*mU}60~jhCB? zkqOCz|Nb#BeEs(0|BpYv@(*rUav7$N5lbwHtlWC2g@c_{9f)D-7=HZx&G7yE&$|)6 z4pl(GS1<`AJ77W#Z)^n;mo00000NkvXXu0mjfcPxm@ delta 299 zcmV+`0o4A)0>%Q6B!3M_L_t(I5o7%S|33pIfQb^#AicxD02cHxaP*7PU}9mN#>dUc z2n29xP+))l`u+d!e}D52Zdh^|Cc%g$7DSe8+}py*&Zf@6&I(h<@ayMqhA%&U+>H%z ztpWQXAVaVjz#uOzepF1D z|HQAq{}`B=SQ!2QX(0jLGs+4w2VpkDXsiYRC4aiv7%ltvpYhcnU^@HrpXr^gvBq+s z_;;8_7>&ySko;{mIf-qr-+%k3ASJq+hll4XOcM$X3Pe0u=-U0~t diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/fold-open@2x.png index 778b395d84598d322409e93594ebb11c9b2418b4..26d06bb2b7e9897d5c9abcc0269059c1e985a1d8 100644 GIT binary patch delta 664 zcmV;J0%!fH1+WE>B!72FL_t(o3GG!)Z__{!-L>O7mg&dP)aL8xm27`PE}Mj*lT;eyor>^R7txIm3r|cYmawl z<~`5OthrK(ou(M42~OAvfg}S-29gYRNz ze*fkLPTa9at`>p7P_GNA^l~nj$)r*##*bl5NI@W3sFV~Ucr^)}!te>i4nsVA_vy=q zE8N>N`5YUNm{2_`1b8UfLEmAV^B>PP?tehAkB0*epC^PFE|2c5N9&Dmo-g-<5btMj z{LQh0V@=ACIe)@;9a4Ab~$&|HWNI3i%x zO4**tXSad5HwJe!rAvSuzAxXrv6Hqkzr28UnX+Io6GcgbbX<`Bka5H8-l^9*Flv+I zQ4)uMM4;x{%A)NGFOVo|4p2(@U_ht+N9xt;Qnh88rhg54|0r-;i$j3yf6tX>I~UHC zHbH?=9;ht`tqI)=LN;40bSmZZJ9ym1I9B2kpn&#Tqqc=M?@201Yz6%!RFHD+H5MuD zO)}JxX$bT!(`+wXFE?E;0I(W>yMD;7%+J~RthEK;o(?$KOhbVDIJZVprDDEwb^h!% z^nIax@r-uiMPg_l#ZjNAo^n@L#w${swotBGI*R(`I6d5GrnnE7KK0q@BmTGuLyPOfp5=&J+?hbCT2E$GPYG z=DTOkFr^fnrKQdioUjuDi2{iNi2_=bM)YuX{WU09%ou6VIDe0F1Uyec+qNlo`}4-T zSJ-j?+j1ij@HO_E@yqj; z)vn}oBcN;jc}pqb&yj`M+4uFAD;t>X)A5ACJZcfvUOZj+<_iB%NDprVdpKjj8Siph zBg3bLwj-?7XMf9Iaa!vv_V|fP;K+fCTV5 zs4l2J^10E*-{w*yW`R}+nme1~#u8x;1TZ~K; zE<`T8aH^=SID95N&fvSb=Gel>8u|y^h4;m$AEmfQJgnhRaMdTw2zPv;mD4VE!HZVj46(rrlgc~e>_RbkLSCc4_>GX zs8xlh?Edg{CSbnWsw5e8TYwSbMVI5wbzuh1XH!bk6lC47EK83x0h}>n_3Bevi)8{B zXJk(w$q?3BEY|^V5hy|&?x1d~O`r*Jd(>uxKh7W+@D_sD?*if9LRf3D>_sYYvj=v1 g8_>fOmJEK4Ux(E>01BF1?f?J)07*qoM6N<$f)%503jhEB literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed-hover@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..9bfdf66ba0c819a3c31d9f5150e1d1f77bc16ab7 GIT binary patch literal 517 zcmV+g0{Z=lP)E)jvr2aho(sW6)if>dr$H>C@-W zmTeV6fMc3IW^t;0B4D^BSX|$QEHj|9wn*N2aL&T#+q2?wQ~WG}$#e?F-z_scMy`;2hL*q#EIv>cO|5HpC^);9rFDl{r^b^Cz3J_8z(=5QHlyuUp5 zu1VYjd7i^$LOVaYM0n6%TCWQhD^{DIpTTrCgIt)v1_>0a)nNh}cP$Oo>3cSF;^I3I*M~UCz?4WxAzz*rwd@zfePen|%PMz)SLy S@DVcr0000mQW!Y>@ z3db~#?dZ^fKqT3TQ0H8#v8^K4OXw;N?2Vs_`^F@mB|JpmzpUHS87b@>*-{&P@~PIm zYsq)wbrCvTb~Iwn0j(nJ?0hM#ggUY@i$vW#liNIR!~#pDc66o{=V_XHcW&DL%EK@u zVjr^e>04V2<6IMLcfPr8W#~k=m!d-MnY56{cjC9gl|VX34YU|_$n-f zx$M9zr-vFS<{l>~nzisE@XfJ=ICeXMoWh$DI@pM0Qbm1+HRiN}PTi zV1U+%hJRG@>r5~_=K^VOh=!`3Owq#w1-jR-&UUIEFr_@X00Cm7UO*BB| x%9N}N(M!@t=DnmGpyzbt{uemOz%Qepf**o7ek=8B(|iB`002ovPDHLkV1hQd>30AC literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed.png new file mode 100644 index 0000000000000000000000000000000000000000..30fca80c3ff25c819fa7ddc1c9b0fff887cfc0bc GIT binary patch literal 294 zcmV+>0oneEP)R*hJI*DIpYmOMl+~ zy`SWzo)CimDfZ8>ob6!1bg>q`-&L(Vf79i-^U$|xPhVj&Ux_FTiF2;FqA2K=BqYCB z*VEnztpZ9YM$n-RP6f=C8_^2_$4qTyX-enAwmOB{!FUu=e=w{mhk8ElhN}WN>cpcDU#*Ua9u`|x@2X#3(*F!hyYXATM07*qoM6N<$f|LPylmGw# literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-closed@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..106876f285173f494c47e7ae3eac50ca88c70eee GIT binary patch literal 508 zcmV-aa19%BGv^Ht5TN;|KPFWk&%RE(E z9Q*UP&rVxmjDaytj#->)ZwXAe7Mz@4nK+I?Ms7&16=YckuTS@a%iYB9qSrE~vl+xm z0#OvzyZ~Bj$kNpD4OxihDLB2j#($o`@zEg`pMaF^nB;kmBow^8e}Ga746fTu=else zJl=VgT2nxP0FpUftw6{WS8l!m{N$!k0`RvG?tVq4nOFIn-)jnBz`PkWaP7r&^&MEL zPr6m$?DD3Vdg^;fqj{lI^Zanz+ACWv!HM6Q;^gDe3u#pRYq?+{ErEmmJ($jRfrx~( z6p!q3@ySA30yNDMFK((?gpQ3aOiQ2$lqHD{%#F512c{)ZDIhT+r2ScelBvWNggu!O z@d%qUtAK5RCzu2A`8RICh(lOP6?z7$)^aM@7F1G&p8-7Dp0@rH__+trEQB;_sjsay z0#F3{g8L9F3GkUU0wvS`A;7C@323E3;e#VI0m39~&MX0?GFakMYZCB%kBSm5U7XqX y0SuUPL4o|O_+jt6J($)N=#f4=Wpo9$1ik^aaeEG^S2C9X00000^hLs|7>jRV4+{Xell!cwU7~69vhnx76u^Ay1F_HEX>Sc zb$|Z;Ww>+q9>d>1f8qMRfB(j?ZS4{iL*Xt^P>^Q;8TR|nAGii(B}F(J#Q5^%3kn~^ zfgAAY(euhahX{f38 zn;Pj||NiY8!%tus;KLv_AhjSpa07shQ|XDZJJ{G*zx?|3ivi?BY#1cR#=`Oiq!y$H z*#HJ!Uhbyo(14TQzkOx+@%=ki43dis^*;?%+XB-JqLE`E3CMRrmc+u|4-}h?ERJjd fvN+LfiZc@cu^qAuN~4sS00000NkvXXu0mjfk>agi literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-hover@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..1ee80ed333d65ca4c4b6f0b32c21916ba8ca0691 GIT binary patch literal 778 zcmV+l1NHogP)S`)OoaV5B@Rz!kyQ5OY8Xq8$tP~xKXXLMT-M5u{WG{xfyPQXx3iG&d3ICkAxFTZGw zI)HBu^3$ir$TUsT_4+oEQj+I+^nGKK1b<2yts{u>y!Mp}Rls0UlS{Ab20^1hs2a}T zi^t>Sn=$hFC(7_lm7Qpx8XrpF_~_yg;0sUN zUFy(3+bX_(##%)V$fup1xbnjHwJE4xa`;NU0df?H@!^h{>-hVs;>E%SIUnxLBs;)}`&t5j_~7mRdw19Q%>+~o-@ZHY-O2$2Sc?mHO2Fm?j*!(7fMfMUHuF1o>B3iZ zEy%ky+y%zviSch^C$qnR-3+TyidPHyxx*id03)c^nh literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/folder-open-selected.png new file mode 100644 index 0000000000000000000000000000000000000000..1440eb35731467e6b2aae3cd11024eabefa4b621 GIT binary patch literal 369 zcmV-%0gnEOP)Q4N|0>5yu`m@dv2lbMhlO@DWiiaD|`Xz%@BQct{~3f;PmRwc^Sx zX%*;76R+1ZV`sHYDb?F1_%>qF3S+<++ef}<_RTuQ3%^%tOIvNwhzB6Q*r%wEfQ_b% zdH~XKUDr!u+sMv2=ObudP0jp*B%_WY!TV*<|ARr_zDVHt2Pk|AA+E5V*oQ6oGjSWu z43}0cP18Fmf7c(>t!|u!E$^^SnYIcFz&muZp(*|3gGG zNJq%efK!0-wkS}11Rfx!T!%5Bh8zL|V0Cmg5uYiL1D`OcO;H<|&41Q8>w4l*(k>hk P00000NkvXXu0mjf%YBNklYuW6ov~Ht(GnjqKO-gCdP#u0~@yd2V+e586+%?D|Jmm*z3lRl?xJ*7M84Fr!gib znpBiZTU2Ul`SN|H+{w_+fDWB><4IoT&bfzk&VA3h_fBX$9xt7ygikX#vHvHqe6$21 ziFA>_U}q$ozIte(COVeDB=|W!-R7jWWiS~X*cu-LJBVZD=vK6_ty%tLe@XL0J?Hh& zOs4DvhOtgS2sL>64D3{g!XY~RToM`)zNaVhrmxZXXh*0;ourG`ihwaUoFw!E;*0fGLYD5B1 z#a`VJkne{N-@gRL7e4V#91<8PnI)4%XnT8mBN~lnG!eM;dcA5q9>1$VoD07R7%mhF zkA}nH0ksbj#2^+i1^WV{JMUDh)qFagzNc|uQm@yaXEK>53h-IjF(t6F$!4>!yWK9z zKv=*xF%+1U_a{$14(aH%TCLq;vG~f;1n`4zV#qZ+gW6A(;Gz`Qgqw{<h!7bGh+PPfrX}{keoKz_Wl6*e@Ouo5ci9xKPU<*CKK82pG zTCMiBTrO|evkML?l}c@Wef^Exa3B$b-DcB|d+U2Z2LlHMrMR9*ByKmG&Bw7=jCbFn z#@kJ$Qu%hfO#&YTnw$ol5*R?B<)`qi)9GxgXg7}<@{~YWbV4)vBe=sbZ(!q!g5Lj!#VW@d<) zfB*hrxOMw3!{0xD;QGFP{lc(hMh}Xia2F^mDgqTUGW`Af7pj4gK}}T!t{KGm^zkDK zAH;zh@bS|}hTllGVW z2W9_f`1AWW!_S{Tw(eLp*9RoXGPR@b^Q3tz|FUy(GBYvZkJW$w{xSUg{{3%WQut?> z=}gK>^8MQCir2q=`_AwSX!`Hpf3RYZ97qjFEl3aC03hR3bdb*uW~TpNe*OB*@CO(R z*f2ZN2${peF^d zf_RWN2(}irew@^+G~n*ee)!(P%Ixl>p-Dt_M#R@3LmT+p(w7CC_512$p4mVWg+{z7H;UrJ0>I zSvC*Q%8C5of&DSLrs*k$Fbsf1eA)OKf5RY5jVB%93lN@Yxl`%op|{CR;GBc56ETA* z$`Y`6%(89ZVFdJ372$c%h#IP)lr1MdaiwJHh$ZoC88=x+s-9j8DQLS)Uk ze(o0Fj7Payof&4ga_@O6W@j$fUSOn8nE$WdR(jhT3xWW1f(MMj%U4gA&dDf-5W$>b z8mCQ@+mr(s>+Ysd9-)MU@!$yI&&lM}rWm;Y;PLYl_0`WKwq1ym3`42iD3nKN^^Wrw zMo0Fo)~XlYBJM32n{L5Ub^7e?59=F4p6eD(Zh@wCXBQ|J2uw((t~HQjjksiTY7^7D zf^C04KR#A>94Edyq&i6??SA8P$7@KKgiRd8MWk{`mO6jx~gU6~M%A^TM4ycGuWAzzC8qel*{50?jj5w>D#Y|5tMg=<$PcfDWx zH0-)g(J<*+Ztq5r03TOxalLW<(xfu*WHDI*%QolVE>4WEEIxcT)`@_b>Quc@PzRnY zK=l^TpMiT=O?hG)ZF-4rV^yAgf+xp7VxxZ%uaiy(-=={lW2$yQ9^dh{Y6o_lR3_*P Z_y_%{$&KeZYcl& literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-hover.png index 83fc42bf703959addbd61a2f899baf2fbdcb962e..c5b3323ce393f6a7569214985cd5eb6eaf6224b1 100644 GIT binary patch delta 287 zcmV+)0pR}Q0^kCWBmpOpB`Sa2$kK(77a6eyVn9^N_kaKXbG&}@mf_RK58MC!|6hLn z%)!gp48!UI7G@>}Zf2EmOxi>IPQQ=!Rf5fQgBTfr*)kfti_^ zfsc=mK}kV@Nmf=mnUkCQik^w12C^Y6$Q&3OrkMelME?Ez%kcNl@Be>>x|$bKdt3J^ zE6TIO)PZR11~4)*FaQ(SpFh7DM1_Rz#6$${HZwN71ysKgi0^|m!T?qS{{H>Lz{kV$ zA|Tjjm#3TaRiNViKztge355p50yQFmxSsU4Luq`t^@>>$E^W3gdVhR+f7h|ywF(g~!O+;19Oqnha9-lT&qyYlh2`ZGw}bx}o0)xuoetB4Jdg zui;z3(DVcc6f z^$Bc{WQM-*pa3T%lSxP!2E*iROjiY4O#)bP^_YRONs50H_)lS32-5_^Fo4T)W^`om z`Q?exS7=(Rtov_GVFAVT|H?pV9^10kiA@8rt#=-7dpR{ZG2`=j3fPOt%k`E9{%i>g zpqewPdw;P60Ca#ASB%7XFxh+eL}sThjmIOAFuC7Ed_mkp=!mKzE&vxo7lnnZHjx;& z9PA1Ll3ovumFYge0Q|##bj*swcD-H z;X)=O(#N%umiHGv371l-n*pDX3`OJ%hzAHV+onP|14y1jrbPr9EVC)rvcbX`z%c>D wQrXzLG;0x17sAR(g#QOB2iEJkA)t|gAD&zFBg7JwV*mgE07*qoM6N<$f?AVtivR!s delta 737 zcmV<70v`RA2Kfb$B!9q3L_t(o3GJ58YtvvD$DgEW+N9~yAFiw2x>D?*&f+%L;&7s? zGC_rjzZiqcWQwr6c=IB7^6teyKo1@~c=AUPWQd}$=|OM<^)%QV>sWutk|sXSW}yu0 z-q^@4ec(%zHt+lWyx;dpQqD9@*v;a1)116p0IdX=6X+QlYJdA54a*E*R`|I@-;tZ~ zc-Czv2s3~nIQ>phyyp%WpL_a;a~zf(AROz1=**=nmGxrTT`HA9(>2gF{ez}yw-)AR z^38xSC*TkS*b@vwcXt#5fdEL7G$_f^%l+x&PZNE^segrF2B5@wo&%RCLPsbB(P$U= zR3AvPd{&m_*?(ku9+}Q&v%*dw%m9wxTR_GH zQ4^gzcn7Ge3Nh@DP^cYnOFBfC>;6&^J{`!MIQKV*9e)XHgKLJg1&xYed_EOq+@9s- zA5bV1c-=G_55S2m1Mq*i{pnUF8PzK2x(-f3C{0`(dv$Gc;;kr(Mk8SQ-Vy*cA0+`m z+WI477zU8Y*fgrzz`@=((^Hc(e!seb7m>N032f~@PKqnW*$rATO4H25zS!)ItGRqU z7A@k>Re#iX)I*epYFL&MsHJbxiYabw1Hz&9`P}%07n#BSC3K+`)8`{p0kvZ>1JFzq zHJCcCS_7}5ES|qS_WbmzlV9;C+K)BV6Vx{pb1?%{tNJE@EJOGxUEoK1GDgHd18F)RF8-J%LijGFqWMulxs#mDSa|LBF3Gip=Lxk5SZY zO%-zjXkJ05MHDqysVUdG!ORKZm>_DUZmcbbRsd`<+&&5O%V_&RyUtqxtpt7nFK5Q5 TP}Ec200000NkvXXu0mjf9P?ut diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-selected.png index 7a5a87741f90c81d44c87da1064269f98c002b45..f3b8cde0e3877f191f5991465e27b03700d93788 100644 GIT binary patch delta 385 zcmV-{0e=3T1DpepB!6Q`L_t(I5v7vPN`z1p#^=sRLhNEyemw%R{_-k_$otBb_GGWn}5&ev*~nt0mv3K`gcdv z#6mEQBuP3=k|bLy4a4xsG|dyNZ9>DsTa0BfB?vGckI&*b&Z>%{C=P<)UemNgcs7b+ z8atbnrYXUy!SOtQEz9yb$8i#V$whH)Y5}nIK@+e*IF9qIs_Lz6+n+UZ^`)2>0NSGK zy040&JXn_Xf_n8g=o>0;Uuy>hA%O4u@4Bu(8ip}L%7;E6Dvkjz0nH{3MNvGuLBMzk z9YgyNod6wi%}?)n5{r4DISi8>Xb<{ fQu8Wzn);G|a#Pkau<$0400000NkvXXu0mjf)a9|L delta 387 zcmV-}0et?P1D*qrB!6W|L_t(I5v7u`O2beThMP8sh#(F^^8$A3Afl5~5h1&i;Fzfo zku2HF;0r{D41y0}mO{aFaEpUkgir*LERqglNN(bP(%fL0rV2gq=ic0N{xA2QTp`P{ z);iH%$4uTDK$wU4UvLFLUPV#5p66X)x()passQj+A&%qSUVpE56Nce>~TED2n3gbb71n`aWtl zxS7VKXDna@%!7I%Nz&zTI6M_aQO5f-YEnwp07;Ux(lpP_-FV_SY%-ZVs;YYJIL=3z zz@!GCBnQw=TzuDk(KPMOw(TdRUZELO-o8eF@B43tVceUhIflW9-V1XKa0#e2vET0> zTp>_=4jn>Wi2i^;zD4DWNalg&2$<|Zd(ap334Oy`#R002ovPDHLkV1oQWwW$CA diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed-selected@2x.png index 20e918ddda09143bfbf6958f0e9803dc8776cde1..952963aba57171b1270b75481e5f4945216639a3 100644 GIT binary patch delta 792 zcmV+z1Lypz2CoK?B!BivL_t(o3GG%pj1o~4-Qg#bESi{&A!b8FrNV?_8wwjM6Ix?Q zLTzJ01q}s_9g#v3Dk+f4n5+r-OLi*=!Ng!eLA1ljf+A7aMgE?HGn;W8n8C%+>`hMI z@aEln&pUJPdkE9%bYq=nSZ8wPJ|nPp1x#8sAP9mPYvgq!Jb#Z+xGSq%Iq)JLCZErD zfeUiE+#v~o6iEyhIq81K&CCOW2*744mD(8&hqY?8de3IFZE+4b2bkvh>YdlB0TM7a zn@y|HXxw(WT#xGY`hGs2zm0j3B%p=`Fyi^2Op5@6S{jYUro1Cj6mNLF-ly$$``Pa9 z?hf87iRg*<2Y*p5hnWJMPDhjp$Rj?*E!4&y)Jeu-v1^EG1!P6&r_bboY9k_0+RJ{w z|L$lsn$E*uFgU8!Y7YZ}z!j`N4^S0$kDwM;Ij5xnVr&SL$;6;^I{Azm5bbvR{X`=1 zmWAqeyV*6eA`3fB4*`;x+bK#iCX;E)>2y9Q7K`~(seg0_ZdAewg6tSoTPXpVjKyNP z>hX9Uw_2@N*=+VEGMhm{HP=FUl$05KSwqd3(JfSgc~lGYzYxIAo9T4=NhlP0j>MlN z;;K7XDS>Xcdzi^&_QT=uUa!~tjKl*8eTl?h!%7IC4Tt#F#b7Y_2IoQpS-t`s4}c%Q z7+AdmXn)UXp-^}asb7@KG%T+>BB>5UWbARdpuOeT|M%yXCAEt6Ar%H0EaRZoyyRd&2P zHa!HU27_UO`=Etu`E?``d589rrDkG+iH`sl-G5k5Vyv6l?5vi5oE)XY3o35a6xG zi-q7frOdht4C^#)Apv?zpMmE%K?2;Zt^&h4P17Wx8R3)*RBxzL#$3w%jKJCz_yu<` WBM~rB_f$6k0000-B!BTqL_t(o3GG!qXj4%bZA{uEY9nZopi)e62-G3aC4-BbB%|P( z&N>L0(xr5BD;b24DTR_r5VEvU!A^ltLE?}=#VttlYm7-7^Y1y~y^!epxhYSw^n=5_ z@BVz>`Of?9y{6%CxG+zoSbRR; z4K6r1IB24=fD}n|wKfUQF&Q<$34hV`y}iASZnvAy<#I2UmzP&K23P@$+FsFdks2_U zN+p}wY`*7qyI-H4o*t!AsV4|-Ckd#r1enpkkm(UH!Ru1FT)rk}WVhQNdcEE^wOZ|M zB9Yj@Jc-yOB7b`R{vj!{!BVf+ul~K$;cz_m`~4rGPIPy7cTFN9D?;L8CTFJ9!ULtf z69@#J^?JRb8akcMc|M;HZ*Feh$9e}qRd{+#N`5EDL=;#+U`T(>2&J&_<#5*w#oIu6QgLt1Qd6x3|g($ zHIK&=L!rJ)CXvI^&21oMZ2&9*=+XP&d$yu>?=I~0OY{z zGwT+>zylPpV)mFDzw_*?TEE{P&*8=^gu~%vG#brdkXbTY2B++DVE%W_z?dJIkODZv z!$zY~jm2W`x3{-H<7SklW?eA-+pDXq58Cba-D0uG zw}H|!YoZRE0dn90`ZJjb0p9DkfSUmSB@64P)V9%%Dtwl#v1e?01bBP#RpY}#kN~q~ zW%G>5ANP3lQx*X5 R7z_Xa002ovPDHLkV1jJxeL(;K diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed.png index 819803d5f7468562f640d6c05504e803a2f0385d..9ae650b248336f70eaaa0967c8c7caa853d57e0a 100644 GIT binary patch delta 242 zcmVRR=dOy^YNpj{q+C;{xt93wCoMCL0G-O z#KgqF%*@2V!otiTC@8?7tgOT;D<_x7#?E=(**9Jq*#H(~4vfvj#K=tmG6dv;zkmKP z{Q33spT4H*nXcNxz2c(6Y%p~o8m9sO|NUe5`SS;ZumJD%kVwDX>dFdtfa=!(@pF(y s7{F@4ub)2|_<6XV_RR=dOy^YNem4A vbC5O?*Yd(00000NkvXXu0mjftCD=K diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-closed@2x.png index 8a0918e2257806e1062360625e28028ef1d02bdc..17e88c0296e324b8f089a0c8747c5962647c2410 100644 GIT binary patch delta 770 zcmV+d1O5Dv2AT$tB!A&aL_t(o3GJ58YtvvD$DgF>kE}`4wO#GjbuI2N6lDkwMW+e$8B9KIxuB`p10fWurC;dXtW1>K0ipZd`=YQ55wuJkK)N( zw}pr0EoK0Y<9~qXIS^eg2zPZtBoYCy*UL*D&keyXejOUk-9Mhpc`V&bZZ-u#5LFN! zBzGJ1M7j~C3lzm8%Cd6L<903$UCQ02x5mWF^b$J&&YmX#JXXa6Z?lm(#IP)(PzYp? z5|kC?fe_gL`AquysHtPaMP`7CIhCPNJP(2(fZx}SAb(K^c65N-E%iA>$JE)(wb#Sx z+(3ipKTYOTRnkwhM*y>+tAb#|1=^15#0hKP*Wj00!gXW)!D|kHN?-y&uX4Fu22InT zR8m272tRLT$6lt+4=)mz8F1~I<;_|rI#w3nR{E)3`Hg8X2BVxG=s)->o4N2F%ToK( zu`F{F0Dn~o0-+fjMj3QXugqB2N?KoU^zD_)sd+_~Rpcw7zM!U1mcb4H)!J8#buupD zj;I>pP~g*eCOI1kcW$DSj{1grj3S6_gBgHkt)ns3nzmxEEUje5MqczEI`9KsDBbI* zNz?|4IhX-7obewVR&tB$sl?z+;`E7SY?ASB)O%CZ3aaT~25>s;nh=s*>qG*g|ay(7Vt z0d%sXL=@F~M}rk$M@{Pov)rZXJMPwb3!s&Ozv1E0cc^RXaR2}S07*qoM6N<$g6a5l A;Q#;t delta 760 zcmV-8$QD>!LdhQ4qy9)VUU_ zxNfVXg1FfUvZHwP=4JRVcoeU?+eB1wj)@Kg5jRCIZj5=W13hdwx1>pYp3-VJO7^RC z?9vCmkY8SYpKrdumxOa12X?dg-GmdnZlIn3Qv!)ZLO7OAH-GNLgk>@S0bLRE%KX{> zp;2yZjO`glw@jVwYmaz6f+yp^P0!_g^Xn&*wLZJ|)(v%eQ zd<%0^lR2sqoJVM-qK~>zG$M%&kjv#@#WcX7@p7rE4Umydh5r+@lx^DvE1w4wp^{Jd zF*=-i*?%EEhxfWNTrZY`N)zy(0RjWq4uP!#*m3N&6NeAJ8p`y(7X+d7r&Fs0imLzv zAh2UQV1HTk#wO%VGvC#zza6>KKjT+b1C5)gFQ|#~m#B2io?w#azq= zC~7K2mX-&S-IK}q=>=>Ocm?$swTP;^m<@8Iuffz`G_|c&OCx4lkn5N-104Vv-!Pau2 zilW>T28RDY13<3$&!DUz_teqOY5_V0HSrY)Tg3r2J=BF5f^^=`04EE~@p*D3&z1*W((gTxs@1SJ4xAQEdR#RdQX-Kg1K|4*mm00000NkvXXu0mjf D(DaS9 delta 295 zcmV+?0oeY`0>T22B!3A>L_t(I5o7%S|33pIfQb^#AiV=`01LVoRQ0Sv8JU?^v9hr- zFd|8ULhARgUkv~M{0YB(`S>cB6ovseFC7z0%_)8R?!zZxW@cuXI)=Z0|1t>i^S+%u zxl0T#2xMS{0Z?FiOnBgle?SdPjEoFSOiW-J#0QF>1oEfhHGcpo`OneTYN4u<+!K&O zW@d;TDvEMX9qp_afaF2qFo59#sKn~H$k3zz|Ns5Z2y_LAj*kgF0+d?~(+Hz+8Ni^Y zqd8SiTjTEEKYtiNw4RpcRG20d8k7=o;fIf(&W=pV`VOLTslllkszLqE-TUi-Yz=Y^ t09jy)OEXBE5g3002ovPDHLkV1jbxg~|W` diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-hover@2x.png index 8734f580320328a31b7b9c1df608c2987f93ae83..be7ab7b0e0befae37c11e048f0b804cdb1a0a9cc 100644 GIT binary patch delta 700 zcmV;t0z>`01=IzQB!8PpL_t(o3GG$QYZFlrfBUkVbcr9;&{|87N>R~^MeyK3Fn$rN z6oZ8{wxveYlOFq5h(ameM8u0HFD-Zx%*j&^Z9*wxK@t^|9F_*#LO1*2{8Cns?8n>e zA(u>;-Itl)%=~uV>|4pUZPrcU-7JpUJp#QBbiD&|&|8qnWPjx97CR7x^Yf4Nv+Yu; zWVm4h320h8q41pz#-!u2EHg=x-14;9aK0D@YTCavEqRI-t^_23gD7!hqVV*0z5Yyz zCs-_oT?-;%Gt;zKQ{Q7lgM;%Qm)&>m@<1a^QTk{{l`9eeO(zVT;!=ddCxOyd5d zV_Q_*XtIMzwSV#Ye_mTkSGD*UtPR#{>Qm9qklEaqYD7RclPw?cVH0%o1cUs zfILf&=BCT&G1^R<@UH_{x3-5akMB>f0qa-T@r5O@d~)Q(PkOpd%c8Al4J4Ju4d<_KNN zra`YI4SyXJYuE--sTvht5h)H#Cj=0;crTy(qV^?rP1B&MH<;QN--Qi607Ruz0=1;7 zejU$Vt2A|;j$ECMXRlXaqrz3u;=pxE0I@G}x3il^`VVbEv4#zfFe36T@XFWVoUauo zb7=surdz`c>}41E8hXXT9wT)Qn}@W6&!H*KPTmX32JUjz_r@nd|n?Jj%P!m5DSMR zEE0`MIk3nxKX*fzUfxgAR{#>47W22r)IidWnG=o%J%1M zl297?e>=3R0)LcL4hDmV>347PhHfxcdvq&IQS7}&j4;4{pd93)hFx>SWC#GaIrDP* zcYJW*!ZeNA*_{YL7hvKK2QH9X z4!N!Lf{-i$;J2PmPMnQDPLyy$m_oB~-x0&!_*mi;SbzGvg;*o=LI8EXuCFZS>6mnv zkqY#|Z3)L3G3>3cE*=2esIl1dN?>>N(a`zSlVqW;0xm^;7^!pZ^iz|`0x;BIS2St# z>y-d%ZPWEVQUm-}%!0V~h-FshkAMMC#+o6p*B|REzIguZSPY~XJjD8Au@W`jlW7i2 zGX#+LX@6mU?q@XIabwcN!?8vTdjQB;hY~Vyq_DS`KR{-$E{a!0z1VO43G~CP_B5`tkJsu zgz`|(j{<0ehTqBY3h>;Va+qHQP{Wt`8A4x;MICaRKwE*9D{u$qqUs7ofe_UI0000< KMNUMnLSTaEj6ntf diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-selected.png index f0a0bf43aa3736037a1b15982ec08256cff1246a..ac3c28f4a1e764be93aa8886b195eef47ff0ec8e 100644 GIT binary patch delta 353 zcmV-n0iOPX1N;JzB!5FmL_t(I5v5bRO2j}E&4_MTQwt*4i_KyYEc}4y8_r;x=lPfGx=$?NijEY=aUNkVfGzO5Silvm23*B){9aX+WQ1Y(3jPvU zvzBUieVG5`i;7eYbg9hqCr1W#wP*6<$0)hjOB!5XsL_t(I5v5bTPJ}=hgo~c%QJWYOdt+lPh=mV8Peo#5#LCLb zhoI*TEPMwF3n~MN4M9r_DyWR%PlRObzMuAWj(^20S&Mzp2SzM3UC>Q;d@n8jKlZ+SMV3W znrmtObA*=I+|03Qnh!-$G-X+`EX#g$UB83*2w40Z(1q+nsJgEElIJ;d9OoJRIj|a< zTdhcR0W_OKO<>b*<2e3QRrLz}0U(R|(Gj=+VtNm!hGAS|rOEeK$2GLJiP#(!9+V7n z4bV_iAcJA_<4gv?Aax-WMBb6P)lBfTw^fM`X)DuE^b0dVMdw9_qJ;ne002ovPDHLk FV1jAppm+cP diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-selected@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open-selected@2x.png index f9650f6fa4a30cef7c14a88cc6d66aae64068ccf..fb9a2c685dc4faaad2ef5ead43b348d44d9643aa 100644 GIT binary patch delta 807 zcmV+?1K9k%2EGQ6B!2}-L_t(o3GJ3ks1i{a$MxPxD7!Z!ie4DFxlj}lMX9Z%ZBS6O z2%=TcHqyNdm#*BpRlFhvLexSL0}0wFf{R>;4+O7|@HIZh$MpYhO$>9#c^Kj59{6$Q z%=upDoAaGHMxN&ln`~Q~bWXmH1K4;57%h9ih^9qul?*oe%72}1RG2fs?+F;y6yz%B zqW5$7nu*4Tk7~vlhr_Ysa=FY>MLpwaG&(32i!5}G1Yn>H$N^zWyV>LM?9J!%-g3F* z*I_c5lqqXT@Q#$gQ#bRI61k91efPv##d%`}Yy>5=wI*UkT!9 zq|Z>=24}$?lgae5QmNEuvl*|2Y&Khf-ZO9$?1C++eHyD}fTACRpB%>>1OkDWoDu0e z?3Jb0>y1FKMY>=D^k;w!qfr*lfLq{sKA-rkEoe@=Z`w>Y+)WY6si^Pt)o2_wjhl%Yj4Z z3uGRHJwON8%GHxk#WEBJ1w$rq2|RQtbTr7)rA>d#z_!O3jIN8h4(BL%j;UL)-DtZC5&Hy+rR?01%G+9UhQ`bTSppw*AeJ+yWM&y6pA4I zZHx~9l`0)vtIH0P;-+{Z5etf+aZ#beX&2CMmUcS@MRi{XL#+D02S9fPDoZ*W=%gcu z1Hi&LhhB4AD+6R5S)dIpT-oT%rZS~#m*&+&-taF02p3hMhwtA%4>GK^tsQ_?2WoP9 l&maLcd%EY31K4;5egX6`=eewB<0=3E002ovPDHLkV1iB8e6s)m delta 806 zcmV+>1KIq(2E7K5B!2`+L_t(o3GJ3oh!Rm8$8~pB*p)4*i2h;VWuYj73Q{{s*Px*2 z5Jab-Ylufr9_{AQv+Syrm#Bk?2oiJ>F(D69;zsU_wW(wNpT0lafrrkYw=U#mAAERk zW`6(Yz2A&gQ54HE$I3FDlkejImR*>*YS9~U8z)L=*$IRp$cdLVJf>l5D09I$K&6SS!ne8 z{jYAf`yy;@z`&fZxf`m`K>!<=FBA%e*9&=vQYMpm1smtUx@w!9%>r15;$^4Pd9T%K ziId4hISh?Pqkkd@f)_R}0UPUBU7H=lF%-wOTCE?8#onq)ymL4l-WiQX6S4ucPlAy# zDA6W81#a4Gwx!$)Rb)CJhf>Aae(t1#4^~T5FzHKtWOo zN1zR)e1AS)NvG4fS%Zm0;vG&(on$}{QUl%;m@Dn?nk$+*aefoFyP>(#ln?s6F>g`%fQdfjF1LuWDwxxe9>5% zEdkREqA|jNg@vWx-NEY2pFe*X7?~Iun3$Lt7=aG?^ZU>LAb&6CV?aHN@EQP={OMw6 zxk5yc?-c_uikX?282qskT@fVpakFx1ES2O*Z=@N`LmXs S(I_7P0000Fc=U8VP+kg`8hn~za;CU6gTxs@1SJ4p7!YMH#RdQXLEE#r TRnV@n00000NkvXXu0mjfQFN1O diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/group-open@2x.png index 211ff8be237b87c811b853877d1b9a54a0425fe5..dbb3622eb0c3bae0f0afab6f93be27c56d312e68 100644 GIT binary patch delta 766 zcmVo=a&xkO{`(ggl0a`_wH)X$Q1Ji#`-eeIN$!D`n(`B%9g9I0;R3{j z07&mkHdfYa9!}O5!J+UU8VXFnP=H}*;sgc%f1oQP0zEGQ^{v5WF-)9T2Y}S~I@?;@ z6A|Qn^B0)FKt5#xS_;D;`@z2d^P9myTlJQVr1)c?o_}31i*V7TIN%2p6Vpk5FP96j zv;Z;`9u+|LKVbAQGcx`R5A?bMB>^RDTD6w5?H+mwBS8Dt`x5*nqMn$RWRhS=7bB@**&?f*kM+YCb`V90!05T^j1= zdHFxk+kb$G4wNPYc)8xW+F9NO%JmYo6-fcP4mctzEb!P^SL6E6AKw{%{P@A(@8NU~ zXzpns{)S{eK_rjMu(4R2&Dp8e0ROTik?s!Xy1V;$9Ackff?`WA zfZryLTM7CH7bH<6RG|dNAddmiA^nOh%h4;LMG)tT6@T8>>ji-R)Bbb)-VG_~N z&=QD`OBLR?l>-1{9`<=X8|}wh;^|oc7Vrl^6le>ce(;QU`QU-Zb@0U&Xs&Vr;B7WP zJ~AA`DSyX)rv?v{&~{=mn|33i3!g!}r&&4UP%Z!ro9k$Ai@UpA-*DOiMQY=42F{;d zfMG=dKQs9?|0)51NuQ2~FMd=gKmgjY_#wOPPrx^@2DWLJ>4fzHZPjtGr!SEb*!B4I_DEC><$0#>OEjNQO0~=|-t~Tg>u7&}ufhWiJ{T&LG utZEskq-Iu3HA9${s8XR0pw2+e8TbzvEnE3z%Mi5pRh(qJl z_#t#LabsfiV>qjcF0L*H1`$lyC`nlwN}hKwwzjmsNCL^_?%n@?KiYfEFvj2*Kj|0{ zVjK;mh@Plgt+ISR51Y*fmdj;EZ5*@226l(7f;=8_oLNn>Dg?S�j&1O^Y_xn@Q{{>PMUJgH*&3|T>wr#&Uj&qJZnfj!o@8!o6&W8mFolb`h1_La^r&O^%#o!R?lTJRW zqY?X#bX|wNtM+go_tBkbng)`TxFd*xaKTV06rf(Ov)yhNo#(YI%h2ofz;)e(76W;n zx0ui8wML_nFa{0O0exS=ImrALf&=CYj9TRUSGhppE+Z=LgQD6&b#iM5jsKdz0nzE| UV!`+vwg3PC07*qoM6N<$f}c>&asU7T delta 427 zcmV;c0aX6P1H}W7B!7%aL_t(I5v`IjYr;Sj$Dc`xkvK%bpo630oDU#6n(C@UC{#L0 zKSa9}x@B_g$LQWdyL9bR;!vxFKn5e2p%Uf#?&#UXq@jWb$9wnw|Mw&Jav4Gh9AgQ` zfKbNKKvLQlRVo#d&*#DSeQ+EnljcnYwcBl|*Xv;0b{d0esef0?<#No#V;U_6DwRst z<#O4p)oNa`SY*94(lf22ew74_fp8X&hGD$7S}j$UW!96;W}(?^s=Z!sLiK-vBuRR( zEbDf&*-VzpB?LhLi^T%4{yH9yZ!ji;fIi5CExN9IZkpx?ZZaGW-w@hIpOD~X{SgS> z|4Gv{^fLI#bbmU%9F0aVp68umO{8_Iqwo30v+Lg_ROoa%WH1E-9hAqXc2N7T`5W($ V>TWva{j~r9002ovPDHLkV1gJB(Gmav diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-off@2x.png index 1644a77383b5611dbae3746bc74118a81c11e015..41bd998198bfb9ba1ea2796e84efd2974fbba2f1 100644 GIT binary patch delta 996 zcmVn5}GhExWE8|1A{Ka zk(6{Wao2^eED{O{3F$yEMScWrFhR$lv3*Xo<3-6?|Zj2B*bu% z)82RQIp6*6x#!+TyO%1E6`U7!x zcE(OmPgyV+)bV^o`mam~WK1R#!#@OMj*gB*=2W%0GNHb{KGxaU$r>9QgsA%QE|B*w9!|tTq2f;WHcICWo6}s7ux76S2S~oPSXpTlL@_)%{wMNtFG|OZ%a-yG~pEI3K=MDq{<051=357zTaUyk($ULsAtCJ@N zDV#9n$YUb3(PuCi?%)alDagsm$ueC9PWeONM<-D2A0(5>`woZ0PAjz%IR;|2wY6Az z-W0$h0xXe8;64J$?Zw5#75e`~h9C2cIUgc9vFkPX!XHxI4nmZmlVhxA0IQ?Vi5j5H#fIV zQQRiqcbG_2LF|l-bco>UBA{V4`$)r-LgPEC`*JiVMmwddWCA|Pe z2@x(smVd7H^z?jlI-Rd55=q?7hzjKxL)0jGfyv3qRY{VtP3xpsNJ%1+)P;a%>Tvjc zKHKcs78Mj; zTz{s()DkWslmZCAlt&l`Y>(3~cf%JLNPGoT&0h1^1EicP4qp)>3Q2B+6a!RvRPpZa z?ktKXEy`K z7Z+AKI5?<=!yQBA2N~St6s0)V@b3VZ&|TQr*rvzhc|_&Gh>({fh&IUB?e-5^IBKQi z89AG@07@K3gj{MqYNu?I9208JC60m2do3Te2x<9V%eY#Eu4O_k&|5-U5&8`i@WB+> StM1YO0000nnFCP~#zA_FeBgw%HAHbQCR3(9Ux?Gw zQgv1~+UToPRC9<<(+is92_{4YgyWMW1m0E}#1F`!0daS%? z3SbcdmPjOUAA#hy$KzR{|IcK&S!B%l5XqrUTJ@gG<$roh;XjDQV$ybCD@q01gvh{# zw(#5H;^I1y`9gM|N^N=HB7`vWI_1Z%)9GBH)d$1juoQ_xQXq4De9UNzLHOtV{JfW< zxJ|zANQEC=B80G*EUCAnqvPw;)YQ`9;bANiiR4KYk~uj!IhdWDePb{fcFD&_dNGvL z$rDjR@PAK}uJ-lyeRntFHII$%Jk4l42p35|N}X1vF5H zePd(8IyX0GYi@4VV(W-hWM*dORZmY3$SjklN*#s1aWcwjYisL+_V)JQX0tg*Z89mc zGt&2SpU>wW930GyjEux~c6MG;-&0aup)GuY0e>uD7LJu`bMGN41q)40O(q;|Y)OfP z>3d~tZ0s?Ot@Zc!hpbkspZb2>NH4rU@G9u)>SC1aY-?-FL^g`TCDhs3DP4ey-i-aA@io#A zX@87oi^7Y`6qstlC4^D{0hs&<!}B@Wd4nE+Nz;5J0%GO5}iiOhtfkI-&3dM&S!U$)8Jz zPDO&sgY@FUN{5Gsm2kLYsQe&3BxYdNqJcjw3=&B_E|z_JJIWZe1lHfiaM&C_(~rT)&A@gsy)!0mNHEYC-w~ X`~JcdZf6+000000NkvXXu0mjfHc0JU diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-on.png index ff80fc98c4527a47efa2d2ece8c20148f1580f17..9f464b89e559c2f5cfa673e2eada3958ae61975c 100644 GIT binary patch delta 432 zcmV;h0Z;zS1Ih!CB!7`fL_t(I5v^0pO2a@DO(a$$MM4lEQ7l*m#f_Va=%2U|UAgdo z#GmjBvP&1zVs}luiU`_8+^A>>QHEhTEC6mni5xs@ zZv?jZDDYfM07Sjt?_Z{A`kEw3jJh=DtOr4GK^+L$$GDh4U>L?>x7)pu6O^FcZeN2x zs4|F0Ygm?b-0Ssjl^L1u`vcQ7kHD`12Dsu9q5!f6ECDOPj^j9I!{KnmVc0Ol@DtRx zbDp}I=%traL4UYW6a{QVya{YIo6U32^E$R|pCaEN$U`?k>{2T56KL8lMKp+703RX; z?gQ(<9MBpozBSa#e)al!<-%4GJm$$WJl~rGur5s1m;XiZr7NMX>eZJWhhcbMr4hOZ z=wel?kAD(P%U|st$ObU0iYDTB}7(e#D9Z0000NEHiGL2=`zBKjt7L|1|j zB))`C&|SNb7Q5@usv>9?bz?zm2x9$??Tt63N|6p6?#!GslbPhk9S(=iJZxbeP1M8t zt&!`xOK?8W8G;w4*E!v^+BBfflaFm2Pt_uF{#tEq;?a|Ff`3k|#yW~KU>W2RN@95B z-UO!jNbnlV03y?9G_Ip4dJV&HKt49b$~T+ME9oHOI{C=}f#-S0wOZ{i?qE5UO63;* zXqHA=E${pOX}w;*w|WfA<#H>XPM^SU0uL@Zgh@cAz$#b=dqEIfwA<~jnlWLA=4a%0 z$GmhYQ>!!X3V)I7^?FSe;%|eULZNV3DwV3)Z1$YEAc#lT!p})+@Z&hnzC- cN&NTx0{jbBkw_n!djJ3c07*qoM6N<$f*=FclK=n! diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-buffer-on@2x.png index c71ee4e7d5fe77f52a2cc60830ee4142c7313e38..e8f6025f0aca514f12e30f982c937499a6d35f7c 100644 GIT binary patch delta 1115 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 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-off.png index 281ae05dc8129e3411c1a0f51d2a981a7caa2a29..028f16c0bc7bad9612250fd3e6b573e80c577cd2 100644 GIT binary patch delta 373 zcmV-*0gC?H1lk0UBmp6jC2xNY4xaG+`}aRQJUjtFab}PvQBhGzF)^`u($doJffoD` z7Z=|tA|fIUGZ1bNP=OoJYXU&c*Y)-F7X#^=Aik88lp{!Scz8IQyu7?~XlUq_=;-M4 zrlzKDKsB>r2EoG!7`W*`7cppQX>9>Yeg%fpp1*(pY6Am570BN+Wy+Hh0UrZHLqn4^ z0X7CeCk6o(ag%WY7=OdWQb9o>Z{oy>YcF5EJR2A(@0pn3Ap>y{Fd$R^{Q1KW7#KLm z!^7h##100AMT-`x?bxv+Qcg}T8R*3opnTce+iO@_TFPc;XD5Q3%$R@*#DFnn`S$JG z_xASoO+Z5$VA##gt@Xu=7hi$E4XF7tC?bH#LRV8$)9m-}-%_8SJb41MlYtRv3pbEg z48$LRcoJL&!T`A)B*zNG-9Q`&#A-nN0*JYQSO|z4fEa8Lh^81ILW@G9sAdoV_As9= TY6V~000000NkvXXu0mjfPvN3E delta 375 zcmV--0f_$E1lt6VBmp9kC2xNX4ekH_{revt9-aW8I5S9-sHmuAH36XJ>$%z@}7wa9x`wjrTqExhaoU9aE^zE z$5V(M3=E4FEmGUDV@IT%oLn-{pp_u;-rior($Z2kJ3BiOOj*8t`}V!P zy?ql&?^OI92Pk>4xfD!0-ZXmN5 zh(7@FWFST^4nR>~1jMXB+yle`K&%GDFMyZ}h=qW-0f-qu27zdb0V1?0G>U2l0RVDd VoiXyF8zcY#002ovPDHLkV1ni;qxAp) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-off@2x.png index 9a2f176748101c10c1821d03f51a24ee810f48d7..b602eb2f65e163e2788e8bf269d87704ead4ec6d 100644 GIT binary patch delta 1457 zcmV;i1y1_K3&RVLB!7xYL_t(o3GG(dPoq^7zipuuC|lXf5+E!B0Zkxm0f|Nw7XpLP zF%G`SDDjC99sU6l9Mt&2g9*e$V}!(DGEowc7*QhF2cqCG#=%gQB9tX%U&`w5cc&$# zbQyV=%mX($`R@1KbIv{YcYf#GPjPyBDjuhye4K(|ELQ<}%YVofLRScRL%`d*k2i(L z$jFa3Id>CD~0fEGI zObtoDv9Tcv3k!eE&CNA%&cQVVd&Au(NOd?W^p;+)AAbgtR`}b7%?1wgEe=-T&dy~( zAt50;q@*$+AVBg4)n+a$EBmpsvhoL}ja=dybY28dzQh^a)-0;j$1=#cZp#YIy=L4mQPq{PtN+}zRA({sD4tLwx3 z{QQ0v3DPeT6BAWXZFIK1&=^r!iOI>yGet#3a(@l!neFWCBv*!Q#?RH&)qN8h8XCr+ zMn*=Cj7DQLxMgv0{siW4IJ{diS3uNFO-*l~oSY=^P(wpQ)02~vDGquU5du-)1R)iE zetxo=K;-*vZEdEou&_`{cX#)ER#w)1rjt5A(g#nb&2jvVmuZNhx3~9e5P1}`Ut3$7 zfPa)}l}aW17%Ml*(TMX42&JT?{8dp=kqM+^|1p_NVsvzr_m{lO7)WVpDV@pp)zwuo zFfj1*+}zwVj?USm_V@Q&p#Lt4B8Wk-g&>szYXWOxdU|@Vudgq_(e50naf&CAu1xSk z8q>+%-ku6L}&>D|?^}iUM*g=jZ21?sn<{y=x2xhZ8wD zIpgi^?PqshV`Jks{gQvK##ul(tuj45{VkR$)3dp`dAhs1Yb6?dcN`3Y$Urni6Mx8Z zP7=$2@T|zp%#`2JGXBN831Wn7pp8`1#%n-?9Vd$=#H;``$FYu%j_=^a?WnJ>zlp^- zk{kBLtWb-^GKY?wL=N&h_?6e%+S&&6EkL%kxVShTz6JY{t8vzckWYEDQ;F9D$oD6v z`1=In zjhE?GO-;=m7*N~}l(BViaPS2vsE7u5!+!NiOH0dOLqkIi&u1mqDga>=b`w-f5 zEI>LSeh^-sp%CYBfDaG;haXVlwxfcbL+SWv4~FZ!OuYp|Jz_q|iEZfd)IrGDFCna~ zV+jAf&5(Y`ZxFY`j*wf0M=HHwN9a-S>vq4ZfZRf3Tew2V8$$m9!8|1$)PHE&00000 LNkvXXu0mjf-)X`n delta 1459 zcmV;k1x)(G3&jhNB!7%aL_t(o3GG%}NUT*9-ZP`)sN?-UUKsDGXo2DlO^PhLh#sR( zbf%X)MfoDq$!8(3V?{4M1WHgu3Sn?4!IMOmoMZDKr5#V8;dmL-@xpk&jMu(zA4f)= z%gKksd2qwx-~YeYT6^tpeQWJsaddPf9;cyvoPuF2mjQXo$bTh5mk4=6z|*>yCxwWJ zh)=z|yl!}Vdkcj^A$E6n#s2>Omy3&w*FD)@WL@E|F(LqEbab>iARs`eR;#7|y}doL zv9V#Dot=$_?cPN;b!X!ip|G&9XH!#Cb-B5@0zpYX2u2JI4TS=!3Vis{o%4&Vxh;F7 z=~`S|oKUG$f`7Z&LxD7cf8lVjwZgKG%(nyXEa>M&I3hdQ0^E`N};!QTdKR&kK;aj<+>c1{Bd z3JMBEN-F&P{3LHsZI;r~(w{3TDt=Zfc|9=K@GJxtLNUz1F44Q!uAqD)Y5vZ7)oK(%v&$r?*M)>;k#1Ipw2;o!)Y$nqM z&Ky-$Rs90pMaXPfS(%YMVk!}|z-g~KI^=wAZqA&apKmHIE;cqcHnw$jb=~af==eA< zFYh*s1nCzE2?;8w)}OUK*BDV*35kh`lZAzaa(@l!nQCuuH^A`DvGH>TgW1R{^Nw6sizgoFfBIy*aOGBYzrnNI2eNiRH^HpelFmkEfmySw{a5P1-?Q&Uru zfPa)}l}aW17%Ml@(TMYN2qh&Y{as#Oo&ls}|1q1*VsLPf_m{lO7)VJ;37yIJrKKg& z+uQr=^z`&Ij?USmc6N3?g8sWGiU0<|7J^g?tO=}%iHV8ro}L~*N4ry`#wngex-!5E zX-p^E+uMQ!2Fx-5UaohvDo*d+N@GG~uxw!@CTY+pzQBhGGd<*s?XXB&~A)oSQrxLFRknb-{ zciahnO@?{TkxNLM0?5fffUK-63x7#_oZ2Wf#@`_KAYWjga3V|_XG5k8j(yPDrL*Pb zWnQN1)z#IvU_fy>P{y{tzP=ZrpduRJ4ez$knwpyW>gwvMfmRT!ab;yi{%Im0fsVb4 zcQxoBC})R62z+@NQ3v805GY@fl9Dn4Ln7pMfOB`jxqqgnrUWXC#qbmYNPkIAP98_^ z`U=%#!qsqybne6Fd}U?jZM|M^93LN-`#1~8-iL_P4T$DAf^|GUhd3{VVw;{hnTn;-zE-V!u^@1ItN4>Ah{VoG?3C;1sB|@GM`VaaAB`05^yjTDL N002ovPDHLkV1kX~z%~E? diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-on.png index e7686933f9ee80f7255bd2f1984649a33059e61b..6eb4ad3f5d017da7c3a68f001d86ca9c42b475b1 100644 GIT binary patch delta 609 zcmV-n0-pVX1%d^TEPqr@h*Dt`eQ%s8i%|L@Wvt1Ha3KV*7$^gUXwx=g)UAtJn1Ks% zm{o{ihzr3*tp?mg`v}|&2!n#Dgal=XVCJ+?VJv<4#@FbaGh^ZmOfB-l;of`h{SNo@ zeot{U8VR$oxmk!%V`f70(-VoJcoMlD&;knZ6Ib7X|F`is8h`&zobF1u+x>xj)a&(H zTCMgfxVb+)fAjkyY=E5(hr`bhPb?aZ`pJWs^c@#Z8gO7So6T-eC6~+9=rftj26;dP zci$@o!$={z2fzr;qL zlM4g_uW>k7uh*N*5#OWZ+c|Hu*&e_PPN(xN9_1v|7xGsK!IL0UEbZM`4JGG>FZB1n>%50N>8Vaee}R8{m?@QRoz)1K51%+kzOs vIo8kT@v%ir&Yo@l0e<{?6jxd|=KP9%!rLBx6>0~LJ3qi@6i+xU(q?|+HYo$2*@Ul5P`{eD}P z<+I2w{Ob9e-xXm4>~JI!xr_DGVzF3&c_gNNe_Br)u;FAbms_VwKA*4CXS3OL=79+K z*hj%Ba2gy3%!z0fc7+f~Mx${>QIwvlszZy#@_0BLju`9p`b(%Ug~MT6wOUOh-`6y4 zr`c@2_51x!)PLkzp(Dv;@*$_dRPRu{T`U$qFpkILYl!u3x7*(7bT-rJ^hPF=dCgj_ zRx6=q!g4y%a=Bd2vlt46o}%cAMx)OeLvRD}GB^irTCLWQ*Xw=O?RLLoI-cxBw806& zqRnP=Zo{~7zYPWh#b7Y3qVGIJU!hH@R4N~lSJ{jF>VKZY*r`-1v0q>|P9zeKaH98^ z3kHKvu=zow(fG<3>nn7;JICyH`!#sM<#N5ir3``lU@Z^`yky?ta9qV9n7Q`(eAf$w z!Yxe66QuD(1;OL-ICo&}c6&>bq#H2w}kP=$|eGMVm_N~QOs(db9LUT?Ho zt?p!^6(00000NkvXXu0mjfVR$f7 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-case-on@2x.png index 335ac21b832607d7ae73a4105aa9ef03b02854ee..729fea04d34e6cf43b2980c8e38b7055f2ef8f70 100644 GIT binary patch delta 1664 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 Q8UO$Q07*qoM6N<$f?&cQNB{r; diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-context-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-context-on.png index 6330e231061ef173d9b6ac658a89dc819d5447ac..e4ad24bbb728484bfaa7624f51739dfb88ad392f 100644 GIT binary patch delta 221 zcmV<303!eR0r>%tB!9q3L_t(I5#>@b4#F@Dj1dD1qB96}>o52KPv{r=Yy>kEOSkkc zIFTzQOOyc#OVyoY`&wTk-h0;~b}dAx@q0r1xSEI@z&p(sAag#sVEqk$iQk~vYIqL} zfyyoN5~8O^EzA-LoDDBg+wan9=w7hiq*EW|*MiD}{Z^Y={5%{E;32^8k(W^W zv#VKSkDIsU6WjkUtIhaRquTLOe#zV8o;aUvP4HJjjzOANeY2C0Rj=XpX4lN8nPx&O XO=ioF{PJL*00000NkvXXu0mjfcFAgT delta 220 zcmV<203-kT0r&xsB!9n2L_t(I5#>@b3d1lAj6#PlrJV~QTYu3H^a=e!pIyk@kR_XY z=gLL_YcU=Q5d@v0(}7C9^x`bD_2{hHOjmx0(JUA+E@>Msz^m}-D1C9awNZ8BD zFRo^dJ#D^LE-Zg9tIas6r`j6gj| z&NhXLDhXj{NLv+Lf39D`X)=K-(3|B-u4&eMI=a z6F*v$t$^5yb;Eeeko6JayLP(=R~RjX$fl|G`b6zs4#A~b4rEf0bLc~e!;GJ)*k^2I zeqBIh0sBlwL4WioS?^1Z;}Bv#e()U>P_~G&=``!)A0cx@w=6H(bm_~_VUwMOkgr4K zdDcNM+jJR&I%}9z7tqMC_C?ZA1&Q!o8&J$Lp%b&L@j%5aYlLT}{Qp_TJ7uE1K2}@2 z%kKHXf_#H<06oZ-yNoNud<)^rDvU3Z--<89{>^fu{#CUY;-!Z?v`}(6;|JfYVUp9` z^i3fIc&kv}fAk5fbxoA4fT-S-cF^+({@NqI5|D!UQySb|76WQK4-ug@ggyb}=7>HS Sgd4vA0000li>%_71(P)${5-lwf7_cNjvrZ&MC`D*i1ZK^z zOdI5R&b?X|GZ*IHBoD<#MG*ne%D;JK{t7^RcppAU}s%b|VI0ja-BZKsC!O+cUa0 zs*V;th@aeI2Z*ECHpaU~*G7f!=yAJ;@i(e%bPpFHvT2~RKGk?ohTuvg7c#5px%Bf8 zml;0;amd)(dVdFq+fNqUi^(VmxmQRNJ&xxg*5fCdVK{Su(34GPg;4$|GAB-7wP_;D z&+&J54p4Z9rq^vvD_VXNb@s3n1GH|ee+m%3qkvMDDLN_3%=Me7QkG3(5taUbmeJAm zbk<#s`{n<<)=0}hE$eDP-=>z~hZ4dbmfXYmBFj7RrGGf|EVq(rsvv3|`p`|HYeE!f z{NS5CEaj?MUnUd*dKI2``kzo-DTqb2*9Z{w(Vj+gj_45aL!|rK*LI8!5U)2+I~#^4 zo^~O$4CvrCO+G#VG87=&Q`Z2=_)p2zvIM9affS)?G@QI}5&8fu_>Ms&ZpEhn0000< LMNUMnLIPld7g_e1 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-off.png index 2cedd0a5a6e6c35048ffb758e8faaa0588878d77..2549b514c10b2bf0d7f2d1bc0e1e27ff64062696 100644 GIT binary patch delta 351 zcmV-l0igc)0{Q}wB!59kL_t(I5v`Q5YJ@-#hF1-UZ~;M1xju;RU}qOlQ<~lfxyyrC zT$i+_NavHJQwRux2|=+?Gb`CJyBd=Ng<;42e?G_AQE41Uvc)BD0ile|Kv%dT(==~Y zRZSock|bg2C#S9Gy6z1kF3a+T2^`3>th48NdtKMb8mO-8Lw}ZKXMmWCAltV0Pz@=H zg0yYRJlv{j8ZiumP%jp^iYTP2Dv~7012&0y9LGoM#TN%c!U^R|=~3LAfoR!|6ieOqlVOVl^Q53BXWYaWsx<4?91HoxLrfGT)GqRKA<AHZ{TE{h`2siBaw2MHf5`v<002ovPDHLkV1nGwz%XiAdImY=#DxvP4iJz z)fnR-NfMQQbJmKsZQn7Xq9|UuAb>2(I(weC*L5A%Kvh*8(tk8PBg9+;*|xn$H<0H! zG)=>6gjLftFbo4&EfoYu3{sXQ#Buygi>7%LMMqXkF9Ae^6Ump-<8*TdVzwQ}8M>~U z){ggzqCgM?#p{Q0(d?H{kKOPz}Fe*M>z3m<`V%x00000NkvXXu0mjffbOBD diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-highlight-off@2x.png index 243784e567ee8e11e39512b048fa2b77d53d4cde..a24f029e12cc21b8b3d77e7eb6f7ce40087f5513 100644 GIT binary patch delta 579 zcmV-J0=)g$1=j_TB!8YsL_t(o3GG)sYa&q;edE_)L_q~X5n_t97c5-1HG-vS(pjY! zY*I;f8`(7ef$gmJ52Ol<-EJfJ0~R)om6C{phWMF#9t>E#J8yK%V&R3ygg5sd&pY?y zO^nTEBaX>D$3zBPPK0zBIVq%5$Y;hhw{s-&n8Lf(VJ}boxx>K69k(6uOVaV)0xq zC(`M(2m}JI2+ikn(eL*~uh)wcnR`0Hm688#e{UR!ll^M7dIxea3tJVZ9gIj56mo08 z0G7^Jg%p8b6??mE-8pgADujYW4Y)O6083|D4|E3#GMmkAAUTSB7el>(NV!~=ou+FF zEg?%Gv@0eqH`zIom_b+vQ^@+EQmN!>4p}(5r&b}W%MLY%Tod|;Ib=sl+hvwQb|6mP zf1T!#PNjo&+0o1)eBohXu;Fxvm_xv_z8*P6*|sM#Z`5HX0&z=MDisdH@jj+Jx|Xon zZy2`A=(>2COeSJBo5|~9*9pDN60slytnqk^tLWeM`|ZD3g=)3hL!;3+C+A=!5|Q^1 zKCbpLq#nxPfWzVNwb^Vwa5(IL9TafgMeTOKv|6o8n$j0U#&kmnX@WBKdi^n<&;KU; zpDI#zgkltML+eZx=wk1cVt>$jQDN8-f?TrFaJCBpE0u~eIF~}8% Rk(K}e002ovPDHLkV1fa~5^Vqg delta 580 zcmV-K0=xa!1=t0UB!8btL_t(o3GG)sYa&q;edE_)L_q~X5!MuIFIc#2YXnQT+0G_E zK(N_Lk~ESu{(@9i`2(qv#csC|`~eHQjg=BaK|}n^Jr4#f-kmo(X0hHV$W?=Sj%zFdf%ukPJ)xtuH(i=X88 zV=x%};rtD*?Lme}{0@aeaUvu<9*-~#gTt}i$8W6HYe9s>bUJ+@a$h)7b_(6aVzGEG zmlNr9S_A?CSA^#Cx#;)%qSx!iiOe0H;G2>DZGRgM#L0fOTD<}}n1!v1(+)as)4A=iXHVh-7n(sr4pkR6DV zcYmijq*Li&U3N5c2w!+u7;HG*A?6UUtglB-QMT=g%o}x>i9p=al}d%faJ-KxkFF(b z_8W%nGP*AQO(qjDo6Y2PvFn81W{Fsk0oHgt##Qup`~CLctU|R~?Xl5ld?M#yBodML z5I(NK;DE#7@U_`&K5{the;pL?y+zyYer>f{7c`~MiHzxn5Yhx?>h=0lKA-zT`^lUw1)pcDmh0UfQA&$8xQc4l&68-{Tz!f}XG(ro% zz%3@a_8=O~jh9tb^=7Tr$Onxugattc;Ju%NB*~32^&o>N5`Pv%x$l=6ZPGMd_1BE} zIPAtlStD$aF1k0g@fo&o%PWN`UT>~oJK;yO+|auOLxjUrpZ zu;7hGXvC~RB2;5F3KH?78=(>RNR8U2X$&9nPf*u&1IUkuNg`boMFoKM#lKdqwJFQ; zg!;F~DEh_8AUkk|WeaF9>!*=O27diagw3by29blt U{L&FaH2?qr07*qoM6N<$f;elG6#xJL delta 321 zcmV-H0lxm-0^9nV|sq7IJ$d@|c*AkPhOYesXPO)%{+#`jgxf>AI_X;kbiFNLgLR`m=Uz{|YNRJw} zglWMOweW~lgF>h_^(ZLBkFJGB+#@|YmSt&pi+?6XQIwz@JWUGeG)-3^SYP~W)LNS? z%f3_&DCok4;KE+P3v@5&DZD|U?Hyd} z74#Y|dV_Xjq(wmy!M~&?t=8|u2z{A}Nlbz+9QY`2GVhx&^IpdHv?r5^^_OhsFNvX+ ze?e(0GMf}&+cv95Fov)r;3v0nqY~o?@7TNV8jG!M>PXBSCVwme%fJHgQ&H1mUcuu5 zJ`k#+C}1=pi`)=X1C~U7;jpYV1n!1m=$DdRAt5(&0BiC5 zS|8$)tvHUeh=1QoEaF}PIdd7N3F6l@Z$woyU=HRoOgQlLtM?dzXay&541v&ikUn7h zSj5sC#zL7}6hsrmd2E7lyMyBfc#c0v8FOj)na54bAxnl?Y9Rc#fD7CMd2AiQNu0tj zO7=~r(KpZ19%Y`Jv}7Uy_Q0`lR^Z4}p$gJV%6~z!CV!?U^B$_851C4$QD#AiFS`#F z7F0za5~A{K>dd`FWU;KwbG0Q?FKQL_A(1PoLLyLk>!L0-hbD-(fiaGnLD7s!^FhrM z18*SW=Aj8R;>-Bo%<_j~IVVWRrz~@%0b>?{2F@8RY6mtSP@o{_-S=WN8_@w;zYiGHekZ9SsKx|E?xKs`n%Ea3OAuI00000NkvXX Hu0mjfv)Vv- delta 688 zcmV;h0#E(91;_=EB!7=dL_t(o3GG)+PTN2fj)CxBfrOA)R9)o?y+HOzaEjg_Qj$A# z-ChB%!6G+E7L`#VRDlo@E!atHApO2DmY8|Zb%JeRktcm}=FOY$eR-Z4&v=u`r1Ft$ z=_84umW80KH?ooz;CUXaM=*x)4DguSI8liQgD1@0Cym9^CV#aNvx)(mz!tC$yjIjp zF(dER{FC5R6*l#o% zN8|DMZ!Q(eU~-Zhg7$$GkzY8TUsKKZ{f@#{h>P41Qv)_de&MK8b`khBilVTP>IO%Sgt zqi(m$4{0gZ>-ESz1enV*O%OkX_pMgzGzbEoe+soQ3`fmo^8}>#fc)sXXIcdD=vo7g z>-GBgPJgF!4ies>a@lY=jKJlSliVhUIOL0qB6eh+b9aC*X3%HT=EYH~DBPLGxx#t# z2e<%!0Ry)2FRdzRJ2fX8%lRAN?^}QZc@)hfh>L`RxWN*rTzm9 W=Fal;s1P&&0000m(S@j=+i-C@F*^6YTaSr9k00000NkvXXu0mjfgYw1d delta 404 zcmV;F0c-xR1Fi#*B!6{DL_t(I5zUgZYJ*S|hOZ(R#DYU&@B!3JAxnm=ZXM$6oIXMy zAngP65gN$m917jC`V7)J6f&hH1OqYl{5M$6wWTf@df?#koZrv;-+Q&Hs>C%yzXk-1 z{|7R-HP>}-P1Ag)^fXPABkbYvczmb4+ith7T+eF-)bIC~lYhyicRHO!Q4~_w>2!o) z7+#iTOKQ0MV_qwuAP6L6u~US^<%zdL<7KGOGesecvaizgty6=kr-u zmi7HJSdF5n=Qxf8VF1u>xAWHPwPIBP(T483-L5?t463p$#c((j90CwlhF;UEa*yIT zPB)v)el!}1@qc(MV6{FyKs&%bT<KBt73HPi~TX?~6$Wha9+h z_uYHWd0)PJzVn`^)oRgWGVfyo11w`eZYMHkXv~ls18)0%lYc`z9)H67pC%?IR{ef| z)Z}}2cUP&`>)+VtUAbKTZSpnkd322tU>L-Ya5!u;Ao6;>A~kcI2L%oFrzErlzK7c6OEmfdJ+6`PB}m2i#}@&CJY1 zg25nx6@b~>+xuwBySBEr#m7vDL9EHww5M&1%#f%X00J{^1|Rt12oXD|X&FLYz)-C1 zWBMQ#q@OMoQlU_|-%Tcy9iI{Xs!$8)KC@;rfC{6m!hcuHWR_#KDs%;eBS7IQOsFuO zPSf1nobWNV1+aW}c1Fj?$3_)u0pX}H(1W0&ZgW(aNF;>pyZ7aL0JSWO78Vx7)ucib zpc+$bg+d`A>7}Km7T19&Ts&-keqJaF4EAgAbyJ}h5cicol}hC)5}>!>ist8Zc1MM} zV^V>HuYa2gwSd;w*AF*0H!0Lo6U-3&V!1@qBDK))lsl2P|Nqq%F55}?d>ld z^y~EWv<3k3P$e71nwB9>ezd&2oZR2v|EhXWE|=RfTq~J2i+{otYRy3_u;If;s7Xs1V6bH6h2s7k{59eC>QgOHL++B)8saTZMFeeJxsn zi7D*iJqO>*%S*Hots>kQ>P6CC=~kih5vK}pnBg`;=jZ1)ES5u+AJ(*AjJg16{SCiN zpYQJOzTnT3MeZ+_u1NwAjtR_cY-~U*-#L%66ovs5VJ9!f9>9u^RCjMN5J*+@}H$;N;rGL+LXLrPkt#s4z|UEqZ^ Rs#5>}002ovPDHLkV1i~&n3w%y10uKEO)i&9nF~4Y@9&8LQLR?H zVsNhp1!ZDrZ+3RpT3K14`T2PP=I-uJJXl5rpc4}l1W*?j7j$-ZW-*vO_IWG&^*h%B z3I>CzL?S_!Wq(nLpO0d(n7Eo$ zXadw=ihY4VKu9_skGHuFMB(CLOG`^aQDCrNgRhecwSc&<{Hs>0FOdMf1y?jbyR+LW z)E$!wB!7IJRHy}%PN$Eyx3{C5*dGdoD49%(WcGvd`Mf=S^3_(bDp1S!`uh5x-QC@< z9CThN6dC{^4^^^JtZ5nI$OK2iAE{f3sDObkhGkEb0K()IPV zXay#wu!r{?d@Gd-+K5&WP7FOp(pl+Mq5To33b2{sHbSLR>4wE}r1Ha>_KQ&$Ag#aS zm+9+lHoL)}C(GPle7YtHKsY8algVU$uvmU^9%U&E1IXf}?|B3yko`<=*a__f_4&w# z0YZdi0JXtmnTL;u$)Hr!&M^-q7z7Us2suV-1FLMLD5PX#KoS|s>6jrUEz;tDG&Wt} TJqcE)00000NkvXXu0mjfO0k7u diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-on.png index 3c582451c378ae625f4ea7d29f2893ba4295fd6c..a8e9290eb17e4697abdc8048174fecc2b369cfcb 100644 GIT binary patch delta 413 zcmV;O0b>5f1I7c8Gk<496pTg`L;`{fe^7BHxN_+Uynttr;3eh&0T=E9bCle`jk9tk zqQ?0Tj*|GjF+IsdM+v0hO?6kjdec>O+k?TtnuX2HLPQ!f6PhP`(bZ{>~?QTX<)TBVUEpktIwf6x&^)EaF7`1k$pp5VZ*l)MnO+k7UxrDPQdR|g2ENfEeBuSFDQF(dQ>-Am{+9>Lag=L)vK@b_b=XqvX=@HsB_2c)b z>o|^!SOUS~_&!$X(+rjhbUPcR9fvjg3yX3>sh?oR7B( zuVm!j;2aRlhh4Nrpbuj32p%Baw_2^6FbreXCG&khBzH?NAA^!bYbB-5f1I7c8Gk?2P6pcm{L;`{fe^7BJuCnw5UcfsTyu=(JL2w!3QE~$}&dQaD zn#}*eXpCQtna)f!kw6OGRCm>@H(f=y?)Up@7B)8v5z&~L&_Z@1t+j}*fHklPG!T>C z6X1i0QGDTlo6Kl1r*f;+x{ji#!@gh=lN{A$5-ESwn;@jUAb$v)fnR2@adhW(^VdCp!El zN%Gomx3AzV2|X<_3#CSd&cZP44$F(HIF8>D+A{TKq13tSx_(BtZ97|z^$6`}_4#|$ zu`KHzu@Hjd_tDpjQ!9~7R zcm*TZg>ytO9kywWKo11q89YIHtk>(ep63Ou3+6bE*Jw2E2&Q9@q-m|dl!VsuR1OA^ zJCuJiaye~*{2V_~NLpP6lips8P;a7;B&RVmqiKr%;ud}bu4teDU_j$800000NkvXX Hu0mjfvSh-) diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-preserve-on@2x.png index b515dcbd51601307fbbdd4307d3e2d08e9ecf875..819a8c93d41fad348e9ab27f1bc648fe8dcd2905 100644 GIT binary patch delta 1009 zcmVmNKQ3eMmVu&FFmE@0so$?!E6jbH`hP3lBWp z_v4)NKKH!moI4ZW-rkPwB{{H{$cW26p|Bg-k5E{ITIwmp<9~5QPmqofcfi9Cm8wv3 zP@#G&hE@{`aa-9tWDa594vv7spdKkNnJx6PAO{MONQsaaI8szZpl57sY+!kLdAe9E zX3=RV(pgzqnU;-g9UliU^7#*&8_GCJfUc2|kvsW(exo4^%cg8(DUqTh420Nq@HT0osEHhRtn+k~q3jC=_x{oo-Q*t$cuF7=hrv5;Z8gvW`R|(dOMC z(VsHk1O?B!Dz6o^LE?d&J0jZ?pdCArb2n6Z)gsV)FgCA$^w9b-pqHqU;8_MmP=cshgGowjUZ8vm41Y-3E37>PH$We_1TGH`58qo{ zTzs>Icy(>#a=DyIr_(PcCnv9Bt5%{HueKfZYaD6=cM&BK8VovR?Jl?olw;lFk1#Y+fpr)+Z(=uH!?+W3+`RggS9_Zgq9_RoLP6_Se?dUgAT3jJ9CQA@piVsIe=0 zBQ-pQyMME*t4mcBNs`IrH4Hm|5?(n~6B{8_LWu^KZ$(^9-u9WsnM@`|_l{A8?$(Z< zEiEm2xuf7m&{&lc6n!A`^Yinm;D+YpK#Ux=ZS8c;{XY8aY=jiT%;@Oob4uAbxl*ab zs2HD$SPfbm|9UL5dGBY#tfq#g2n_*0({)5=g{@Vw<&S_Xm4mGv2 z;5<0(%=L3bPO4p)oVw3AvT%+{|2=+ud1Om!-G{pIXP|sa0})h$i;$|L8~m%7T^f%D zAsAO+uPYJ}J%0i>0rl-tV>}bALe`oV8me+rgbY&FdlV^@MP>u&8}<-(>BM6 zlDZe%>hJG=JU2JDu(`RJ_nm5)P^tOZ+1UlzV0#<%g0|op)*tyO))n$gd8Z`TN4OW9 z1d5uf)0zUTD+Q|kji;{vBTx$bf{?DRCTd1f*LLa;e-9M6%3E^~CB f_X#zMj`sclJA#YZ3aTwv00000NkvXXu0mjfOmE=+ delta 1032 zcmV+j1o!)g2%QL!B!A>dL_t(o3GJ7^Pg7A8$6KW$Khzp9Vl<`$i6+F!7>$dQ4kR=t zBqk=VI(-qFmRG@ z`tH5wocn#}o^$SNbZ>7ja+u`UVIl*UBSK*(aulJk2z4}4h<`?-L=T_?q7L+92&D?j z4yvl&s-bm&iKwmY9Wp0m-!1eACxu3&ykvHzmlN_ru_aOv@+5saVC(oeCL^E!u(6>mM?~V>`1tt!LZPs=FAK{eHrV0=Utt7->o!6h z(_ku`P0IVP$Qw=ick{DB1&s8NipYzlz%S@BPe@Cb5Ddj!jN!HxIQ*E z_Hboo<^8V2t806%R4Um_Ci8k`X6B}BX(h*nTHBF+okRV>os<%Ul0m1MdmvmD$g%TN zQ&V?U>Y1Qk*tlFSZ%$87-N}RPfR*K4z?VU zZaoQkc7KI8qTvzlzW)Axs;DK2$K$tTcuFAQ$*H>72vG@THMl~j#MRWByyQ8X%|_I{ zBdS7osp;R2jt*X~SNI(?rc#2!1-ZMlw3G@iFjfvk#MQRc>>Mv^1jI&&5N=OQOuSMl z(|3GQtyXKKe3PvWAY<%rOh}2>-<+MD{g6y1pMTHK&wo}`7rlEzstsb>`(GdMI#dx8T~?#Glvk#NVWa1 zm|a>P4MJjE6?^gsA{^Bpfvdzl^IG(RRjBm?iyA7q2_b`&^&CZtC_yGGaEJX6z6)Q4 zZ+}91b#?Wlep?cKo-E6dYBmV@x}=DNj^%`gHZ(dq`ZS-(xPuq z-ypbX8_Nkvoe=H~4-Y?ETwGk<+1V-hcC`#tsfC4wg=K886%s;Ma1ZN8z7!jZQ-yq1 za0w@b3j$+ujPE1h6*pHnqAh{r|00kAe19T`ULDfgSI95^S^N^%jEaO1RcIrm=?IOB z{stj(5^#^<%O_1o2x~=&ZyEX(cT$hwtwIeAfe;a*x_Ft|rK;$(hmagXiVB4oa9e^N z8;VN`5*3kQY)DHHGQ6~L!rF)0=)Y-KM})#c_7_*$i`jL2o)7>4002ovPDHLkU;%=L C3g2@8 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-off.png index 61ff9322f3843388d742bf1fac0eb0221c691904..54657e1adfbc301363e59cf515d4ca51e6ee5827 100644 GIT binary patch delta 298 zcmV+_0oDHh1AqjOBmu{fCBJ_F4FP)}WEh4)Ah9=Z-WV@fu%PC}ix>VpJUk$E48MN; zVgOotJtQQg-q_f9Gf?OUSOQ957zFgc8!$}zl$DjWb#!zXczJml{{8#M@cHxS|IeO1 zW4wCx>c{u*-?st1-48VQH_R}Mzy=!NsjjZB4GfR>=H}+BKYjZ26BvJ93^6e=(**Yis-6+S(e(gj=W#WJ@CP!De8D5zNm>G$_q* w0C5A77zUFVgD^B<6Tlt_Y;1~%5u=(x09E$t!=k7kOaK4?07*qoM6N<$f+q%u(*OVf delta 297 zcmV+^0oMM21pfn&Bmu^eCBJ_H4f%u9AfN=}n>TNa7c5v%^Wwz|e;ytlu-ae0elY+o zy&fDKTxVipvK1)!9cBoK#&8kP|8BrA=~Gr#*4EL{Vc_NEW%&2+AH(O*pZ`C5_KflB z)vF)hzklBb^maec;NLLAFajHBfTy~;x;8L8-kY17um1Gu(@$V{F~onw#7q|u5V)hM zsmU)bEuFZ3|9;dY0SY5z{PN{XmyV8(EnmKTDFTXXM@2<_kBEo>GXIMMam=!1%NDO+ zzrGpB2Kxp`gY}~r0u^Kd(xyNRq8V&$ZNFPvTLYPJ3srz@NhChl42&>>`5B1@r5O$& vZa@;lU=m{xh9+zR7=ay)O%X9-R5J(wW^3xs6X3@-00000NkvXXu0mjfpVx*N diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-off@2x.png index cf88632df581ad6bccedb23d948fce2f3583db43..884a9bc5d94070db33e9e526d8bf3966885cf346 100644 GIT binary patch delta 1173 zcmV;G1Zw-V3A722FMnfK82%IUGWdv(IKCgrfQu1|R*B$5h6=NC)rA$h61o)WfI1c(aAhSq?LRW3Yl0`c>rLE9T(M4&TW*o%_iAJMF%}dRE{~>o8FPeJ?yKs>M zU+z8UKacNx|KmoJOeX0jHSJ9z!(6h2lsuAED65c?0!ntB(tiXcyVYt9>GgUG&r2i{ z`#U>3MM`Tj*{@9qWGF8$Pn>adbd>2VRkmvrYH4XP)z{aH!&X;UO{z}KFiCaVS5;M+ zGwh>iQoCLip<8H(k5$CW=Y@rZ#p$Q#qY*>WGu>WVTIwh(EBhrLk58wMU-hnv z5I(>U5hvDyf`5VsAmjtF2RPioo95=`l2)q~Fq!`ISVcueeR+BL4A%XgpPzph^Ljz< zy{m{w;~J%}J#Kf`Xf!_S?CjK>oSe|g$_gDHAJggS=}QoCb#-;QLDIzg?C$PH=jZ1u zpvEUUoo)aGO(i8IWV6}G@AuQv(vmkA48DW8afyKZsedM@^PxyE8jV_UW2&yM7NpkK z*X`%$=ii#mW-=HI#Dt)R9vof(RvvF`Y$U9ua5zkyjZkO{m*BDxkQ0&miiEbexBosk zIQVLOeB6N>IxQB9k#EfK`Fz4^9K$N zN{3(eRDX0C7TpHA0n6Ijnt5_^@*ZS=7p~B<3LMl@!@*n}MUWpJ9ug34ZEgLzzrTNx z;|%Qu@Evf>oy(dULcETZYzLfxac*wT=J9wwgc@3QMS5e`u`;a6(9qBmxGV;{AMgOL z(&t|Gu9lEkJ2EnI%j@+%!foG&LVCGEJc$XhN`I`--rn90vTI~|dioKY#3l+qst6&) zbqL6hu=%G5ST0M{-rg<(HN6gR85xO0=0S20-?={(_>dwYNPZ8S zdQ^}V`KNt z27j^_#li@6Oo)>^H8nNV(a|BY2la*!*hkIH%|DOv?Hvx9?!k1^(h8p6h9U|?Y28-EbJKtSG6B!mSz1oP8CAP|Sk7Dq=%C&$Ld zo)#4qg<$)h|8j8Lu*)V^-4Xo#5Fau8{fN%a&gNpATN!X4xQ$rSKxrdXdV>BkaDQb} ztkkKMKZ1-6@BaWl0pZlRPz4HzZb~7^w2haq0Xr)BeURZv8OiWMwS*2a_X~g(y3m+} z6c@;%3$|3}C7zD~<7Gzqm|B;~*QNJe=t%!Nn9*nH#MxvKvV&lvYTlfXuFuO@ENt&1SP-tJRu#UL+FP z-`Uy8m#s-3T(kO3)A=tB9A+^YZcv;&;!;$RL$UC5B?pczaP%k*&D6_~&pq{7w9Lvj2(* z;RXB|;>4Vrn}2%`gxnx@51Z@QX=Y|7snu!$gW=_km6es%mX?-IW8H5#IXQPQuN&mv zPDUg)u9Cg&ak@Ktz5Ypid%NoN^psXsR_Ns9gwD>+o`Z;^qoczKk_Ps(ySp2lpPw&- z8lPx1nm!OT6c!ee#bP0k$3sg?OD>5*d88b#--AUS3Y2P)H~P;@+N~p1H2BuHUh- zFShwrPk-r^VbLw%J;1cKwq~4|n79ks-@;X>Sp^R2Si{C#97T{H9v%{K)ZEd>Q8Sn#;v|uSA#E!Ip4X^|H+1Xjk+}zxUP(#gE5#RXgSQ%DjU|`?@t}Fz*2lxqC ziJyDbf2D-P+M%JL5|_(0hSR_Q&Ur8Y!Xf+KZ*z; z#We`XkC3|e5wKjAsI|3K1ZsR8IzK-r29xsp{j{*KATlx#2+V_IKi+eHNcfPH5G20~ zCtu)tZUbbsTB*9a`h{PV1d(MNKgh%j;0&aYcsec(w7k4bTU%R%k`aY%8uLHi*x1;8 z;eR0EkF+pC9RuRzc2!jswY9a0>_NRD1om-bW8*{Aq!8*3G4OH?I~@qNd21+>4vPn(;Y5v25!$;ruar1GDLl_LAO^qKVVy3^p|4WRyp@y(=8R+Zl`+p8Z|9j88LgQseN(fUm2*eVS^r%%&u$Wj?OeejN&3 nE1?^wP@0hJe$xuc6#5s!=jB4JNDf1^00000NkvXXu0mjfMJ__x diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-on.png index 658b0c84ff028b70452ab332fdf80da4d9097242..92d7cc8286bb697e7df265d76452624c8e4027c6 100644 GIT binary patch delta 509 zcmV5XHj7?~A*hUP)s8DCSJsU6am!C=r^DwXotY&ME` zR+0d+wDi_Sq60|$cxAh zB>_!59^b20tFQ8O*Wr0yJQl}s%~UFN>~_2976%^9m`#)7K@#-EPH^)eF$+h40eAP^!ok&sL$s+gm0y^L~%7F zMc{2r!7+hzU;~`T?O#pg;0j2O@&)GIp5m;R$seIx6?Y~pzDDWD^{00000NkvXXu0mjfRMY6s delta 520 zcmV+j0{8uw1fc|wB!B2hL_t(I5v@}_NJ3E*elH6viD$oJZGn)8ZedefHa65MEwyB= z=FsdWuHxV(2n~iy6m1R_>L8BMAB74^8k)W=pMB@zvo7(h!4E#}Ip?15IU@Ab*iaT-$869Za+Uir8mnJx3}4bW0Y4!LS_)g)X`S^t@83)Q#0@Jwnei zIH8&_Etd#_Ar_16mCNPlp1K>f+imt)vMe`}$z<5+becFVm*9-h6eVq`RI2)XzSBmd z@lLtR<%(vr*?Wp{;17}{En+IoPm|1M^Liu_IW`)N2Y+bXhmlNT#Jix|>-B~`9?v0S z?u?itiVS!gBM9ce8L$q{D%<(_~ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-regex-on@2x.png index 3b6b7f2e229f74c538215bbfa591c318912fbf15..aab3f57ecdc3cb0b65a2af895a1fbe8569f8fccc 100644 GIT binary patch delta 1270 zcmVuGyO2fgR|23NDJJb<_$98I3l^ju}5YA3FB; zpT0NPnfGRNLLdtd{O8_x?mhSX?>Xn*_a^<|;2`Z$l2eZo8Gms(A(Sj5ClN{(AxE+a zcH`*?W3m@c8}s&Q_XJLFO2;>4LI#N(+B}d8B)eeCXhrPU@*spV@pdj03b`E)$K&$6 zx3{;Qk&*F|x(1{^mOMyNM+lJ_r7j{y{~Qk*OG%POh*A-0L3N_K2m{rMoSqGb!_iF{ z{joLU6IC zr{}8I>-}SSdHF}4OMwo-DW+TcwNz>{qS|;2l+Vr0jep9a1Y%obV`Hwu!onKH8Z8LD zgZ@fOOUs+{^Ydevgk>k`?d|=5F-0nqM@!$9N=-(TPN&P|y2f*KQTh?t+}ym|-QDei zkl)maTzPqU>%_#w04FYJ>dn^Y^W7;cE7K{=MQ7>TQmM&^im|A#ukYiHjSa84XrdMp z`@6flTYq+Q78VwMYiMXt%9XZs`H4lG!kl0bDxkewR8({wVm}jrfSt2M`o+b?wbs_w zo2>OVxCC@>r9>#-Sl~6<_esZ%p`ju7K9Y!lIkvvOzTMH$G0MgH1@>2gqVqJ+X*)I{ z6}67APD>@I1Fhi8=H}+_l^)ZvRqOG1{w^*qc7HMdbI=GxrVMD44s0y5W2w|+M1*wg zb?-b2u9TFNG!G6A{=`WOS&@jE*zfnp9e2YvOd5Y^Yim>b zb$`QK){vaqiBguyF?QzX=j&$vpKulIvg==nTF4v-1OlPT%F0_KBO`zEUW?@29HUKl zI-M_%kB`fz{p$nvmUVBa-Hec&_V@Sy%1!+>V>fnocA{4SgrEOMPPJvbf&7RN<=;a?}1N6 z!gOj7`Vf61xCTDs74qHE($bpS?VjUV_s|Q#_NI=oWI~)6Z+gAU^p?L^QBiSeW@aWx ze-r!##QFj$`z9Nqp9X1vPEJlUT@TPFN--$P%F4QuvrVhI5+N_px6)lhX4Qo07*qoM6N<$f{O5V<^TWy delta 1271 zcmVKHC;H}Lhyl! zE?f{uv0*XzSZGX)iA2K25Lj3pHU<-RO$-lrADC9!lpSN4g1i z1gbZq^39l#L85^6EGPt$5ZE+Y5j(Ox3ZYzfJ0FY1d=7`>jCS7H z+1bj;$$3$a2BbccJW5h02oV{DE+NMAc{&=)NYZ+UR1s-Gd7`|C1LcY~EsDqE$wL{> zMaEc`VJkb$Nq^d=%CF>N7#t8IadQ|rQ(s?yk@f;O2l9akDTnrp#vItnPPz3MxX{qh z@FcC~r*?vh!44Q}(c4gwsoQmRb=T2716}}c{)_0u5E;~tYZ|?_;$y2#>E%&xmYBHkOJ3SuHP5Q}2c}~d2#>Tzw?rsl+ z!lq8-Dl049k2h2rNwUF2i zg+iNlbARUN=YMZ(Y*fh4Y7Z+|DmBp{R7`u7@VFuN3l50bZI(*Eu&}V|cDwsn>n(5v zC>64gN=Ox`64nB*g7*o>t)ZbI-!77hfH}6dwzk#L(c$Id`~v$Mzy+QLsFz<+g60dz=}{*ei(%2f8ccb)~;%FD}J z1_uX!rqW_oB=+l8R#pP-?d{)U`x>YLPXKx6G;=M}#ZsxshzQ9;#igaCFAWb5e?<-j z_iuWb{_^tjRA*=ByR4;qNC$LeLP%wQZeU=bk#hAPW@Ew_4~N4C%H6Py6UHCg+S(NU ze1AJaO59YEOBp+hii&hIKO|fQAx`}(UW=Inkw_#~U0r>9WMpK9_gccu<``|Z)9HM9 zY-~(>>eZ5bDL*iWH=bQA|GyX?XR>Q@a`F$;L)x^zzyCL`g0C67wY|Nayb2&6M%M*8 zYin!2o0ynbFzwWKb#-+!CJ$v?_IjCKb$_{BpAd?O*Xy0Gsi_f>kHCB2Q;{$?H3)r( zz6sm}pYaO$esOVe)#vlg(tjK18qrJFvdR)l1`h}xm1||FxBTU*s;Vo~)6-F&H^5&& zA-PD%zR5>;&VqEfprD|IM?cUfN*O54%gZaILSr*CGn4e^!QUK0YaNhOp90!20)N)P z9mwd@R3t?x093vSBD4?6I-Pq!^Y4Qd!kiBot+fJVFS3?mOGshBOET2LYPGmTd7{q@ zU2s}_zigw}0zsg8YIUFKvsT~S(&;15QfxzJ&Nf1)D?pIfL{1CpKDh*xq)!3KU7)bs z0rHe8Nr#qaleV1ImXNK7SRf&Wac~@B5B=KER}RQ)$`_qnvI?Y2+(on^5oNB3siM_S zDQP`qXU!f8gYrn8QY%q76h?hTChsQ=99GhLD8)hwod{@Sa~<>dM`IaD#vaOOw?h~y h*^ZT*5K0%0{{TcdB_=Ab6~zDm002ovPDHLkV1g>(VBG)! diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-off.png index d6ec59580a2569358aaf4dfabc766cbe1f81739c..e6b8fd3e86f71128da8f900228c4d59da67ee391 100644 GIT binary patch delta 72 zcmey#^pk0WI-~lu{Y6i`ihX>}2#br0FA$pZ>cFo;NhaavzyJI!cFW3o73syxBzb~o cvLBLT_B$M#--ya@2yJcm)iuB@Tl03mQ c*^g0Nq^RUeZ#>(4CI%qzboFyt=akR{0638wU;qFB diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-off@2x.png index 503e6bb67a33cb7dd7accfbe95f7f04a5ffcf878..0687d849f691872f694ee12b35ac86cc3415d16f 100644 GIT binary patch delta 807 zcmV+?1K9k%2EGQ6B!2}-L_t(o3GG!qXj4%XJ}>-9bgg+i1_Bm-$DK}RrlX@H7>~y(8jTXb5d=-p5CsAO z8X6j^fjynEdH}q4e0)qQ(HjDUgc`*A)a`aZ4+eu)ej-$s4n`!k*=#yP5IAw={$0Hc z=HTE!9B())%N{_y7K`OQSeb-&Xl!hZ0F1EL@Y4S|N`Ivit*@_BE|-(G1t~)Sa}Afz zLmM}^iej;tt`~lyiyG3O3EDMi#4<22pzGx#fruX{1yn2+3o9!tZzGY&x9#n1z0a@~ zPESuoe9LDkAY{|}`ucvRQmHIT{PV`fMvb*_baeDj6){K#dw6(AySux@=jx63*rysw zQR4y!?0-J&dM1;(n$2b>QE4tNajqy)_5uup;KaPHAH2t%e0h0UDnKKE#J#uhac6aP zH8?jnH-&x_;?n14$R*E>acgUf(&_Y!2xu@*H`v#=RZmtQ`vr>+Xox@FVSt^lo12^Z z^DrBNq51$b-_0;2?hoK&3Kp83olWHP`EmToT7OtW&=@T)E=r$7T+(PZ1Bmak!|uUi z3kwVV5d8yudv1KIq(2E7K5B!2`+L_t(o3GG)sXj4%XJ}*BF`E3&Vlahe3(pnwTK%sFE#HAob z5Q=tk?Cj#=BCRHqxGN%-4#o_YK%kotaBWDyp-!D_C9TjjNki>l{LYgniMIE?yrj}0 z2M+JuckaFCd*A)ed5Kc5*Xbge=^}w)EL}jd6X`P4Wk|+=EPviH4ULS9*!uhXU&|88 z;z}nPV_#oiP*IeRNEX}P-d1EuMRAKLs3!(iZrSbj4_2!+(A(RqXNho5TYv@z1|B(` z&KF*<*E%*fM&)u@m`qR55TIZ%=r}q$dgXSz9|i&eip64de0)rYhlhG{3g@&82mmgd z&E{o~#}kE+X@7itoO*hCs8*{H*b*kw6Ko04;Nakt&1OrwTrPh!8l~akVFEaUpa~ix zpU+35qoZ}Or!v+EfS(;49FR=(x&R@e0`Wd^I2_OXe!rQI2$iLR(UMv$7L_3goVarT zu3iRHtyZ<^XI`ac4s8mkD1Jv@6hvDI5-~dO1%Z+6xK+{eb23`8T0Z=9H%E^)3Xt?V^041yE$xY76-ck0fhdT1C|4jB!68=L_t(I5v`LyOT$1I#_#-FI}`$n1(kMjii3+}a&!_jYxgXI zqo2Ud4-kTz;HStSPLi(>$QE281hPZ|ZHUj?_ReeUH5Ky0!@YUm=YF}|U3gVhtc}=c z144|}KwC{K;+${8LmFcn#yY=tMF3FjnWlLf$MGuwh7K~V(|=~yg$BK#=Qz$)k|YuO zbRuVUpZdHEvWZFiuIt|Cd7cr30<{}xI9DHIhsr`SnR(d`49C9jKbK`WK}{R*4bYVH ze-p;IZQH{z4Bxf0l?Ff;DoEXIo~CKHD2gsj>%{JYNl;Pb&>?i|dEOg7tyC89Cxvfe zMe@@Kh@d?>fpo5MVlP>ieG39=Pte0$Ba8!@BP0`9V2n=f0J^j+>oJO=1ik;AngW~N zBf!oL!?+8A-~;)}d&FligRK^Htx@VgM^F#?g!c>M($Ip08@#ptwMLKtHjU~%YVxk^ Z{{X8fd=ag%Wy=5n002ovPDHLkV1kjbvVH&n delta 381 zcmV-@0fPRP1DFGlB!6E?L_t(I5v@}(OT$nQ&eN*Fq7YDVsKljH99$%mqm!VJ%{`0Y z=09-r2ZZ1z^jBmkog{xDkS(~xELkFf8sc{~uYIX~NeemnxXa!5zTUfcWo20^UD#F^ zBBaroP_L~c#@G%xgek@_mGPw=L4Zs(3}YBY(Fg*XOk|d)VSg_J1^R(Q+qQ4wI1W*# z7r9vGiQlV4wsGjlah#_t%Tf{{L+%bZuW$;lQkr8P{Bf0C5C)#-y%k0AgPhdB(@~tC zUj&N6EX&e^Aecx`3k3*Wi9+JAS&}6EJkR$*TF3TkB(91i15SW@*LBCZS|K#>F9fgF zQSE9d(u~f4TXwwIdzz--TmiXP;JLyBypG!9y!b&ejF$8mxHe7mB@DwD`ORL9i$Y|! zNAQ=Lrak(;|ADx1k66_Ocy*(-Mn(k&zya_H?ic(@lL~P*^k{B$-Nye~s}L7#^Wq-0 baU1Ku%?EiB_5bz@00000NkvXXu0mjfSvR%p diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-reverse-on@2x.png index a3cc2493a698277040a92009bf7d8738aeb6d8eb..8d5efe78004bafa1b29304bfef89aff1e9a30d13 100644 GIT binary patch delta 910 zcmV;919AMh2c!p(B!6p3L_t(o3GJ6%OOs(3$J26^Rw}bMnTZRFkcJE-&4WP*fk7d9 zC=5D?fpk#x5&8rgK@03q2UVNSvc;E$p zp8NH>f4lDMzMp4RTU%TEc9T`_CK$2o0hQa4y$F?y(1CITc7M`eg)z=Z$CktE?)X*= z1dF!42FaJozj8|RbZiyn5HR~$@gQml1OmQzJRYTd6baCsDY8z*O4=SpVDyU`(Aj7-dM}kqty=+{ zmhsbu>I{JWSrmv#CSRI79#6;2%*>NaCX;pwU>}d)Ab)DKgP3|jASNg>9@Ny^+uOac zu<*j}*liu3SI|iVV1JeZsv8|0?OR)0TXeg4p?Sdcqh>pZsTaGcvxy8$Eyudz*9!nv zGp`tH+n=%_vp(q+s4l*Xe!u_b?Ck6t`u~aH^E&e3tl;hxi;!;IO_YemVxPLYx*m^@ zkG~|T)qh^8$#15plR~5n&L=hH0J?$hatA%z*x2|{NvQBsGO4QsRLCr!6}n+hpfkwd z)6)}}n3x!9ZEe--W1BJW5sgXo)7H68{XKex7Fc)1)}O5w1yV20v&l`d4hf(IwRyeX z_Q}b~p-?E)PW>-q%6%F>BLP?~LFFr2L~($=TYo!Exg?MV<)HK)JA@ihGisB#7Wzj< zMlM%l)U(F;TNK_REp9b^xC!bTN&=+%cB7+Uw%gm8nSZ%b?nKI~JK=En>FVlgg7I0S zZ4mvxmn*B}fmukA$bWuNzKSB9ot+OjjUPosy`s+#N0%P!W!MrWtHQ41n?lLCxw&L8 z7=L^>H8nLrUAdz^S5`KM3VF5VmA(X&SGq{`VgCVr0e+3AV{;TC<2|=En}F$f3qlE6 zi?pSRo}%c=Tt&#b5>SHBH=Vj%BKsA}R-r@L7JuY2Ux2aBK-ZSb>+SmH zO$aVJ_9CEM>aV8|r*&<4X$Ue0+3_$a3kHMLb8~Y;^p67{@Mfy4TdBg22GQz@u0!wkR?(##+EBb-ln<5F6=;bwqw}Wqf@6 zMLZtg5D{aHw||4P_^xuB^*INziS$woD$q;I^z`&wL&DlJ-vus%6E;X&idS_D4FP+b zl@P&Jz0~yf_I55TEQGBjtbNw^fDmY~LE5SYB3fi8V*HGR2oxFpqB3xPaB%SH+S*#w zO2BQOJawSN5U{st2@z!UQttEl8YU(tUL_KVxLX4D^?&4T0w?W6jPFQ@098hBiNCF_ ztz~9r=AAvUSD&0Wz!^ip-ew|H(%;{IGZKl+dfnV94l*5}(oV$q>0aujkfYIPG1ofp z&kF>srhLlpY=)V&Wu=Jf;ky+{*4FCtvBQXa$>1tjvrYcS{|Y{U8TMVY%_rTogfvTXRZ0)zQ6K^} zpx*EIUl<)7y&npNF5+)9LY^@&2cp|1IDY(JV1E|;gwP+`eA3NGNGnpL%y&#Lb`_`t z^?d0&`uh5=7h?EHWBo%qpMf^Fn*O-0fQVWM$=_*AII7(`PLwJoQmRx1Xav&Lp^oH_#D61?-R;a*?%!^P>QpWdYb?M002ovPDHLk FV1g^-(9i$? diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-off.png index 91378b16dcab1a4cb8ba9923ab593917830d5206..fa2d2bd84fdbb6d320390dec70c0907ef8750950 100644 GIT binary patch delta 312 zcmV-80muH)0@DJJB!3!7L_t(I5o7%S|33pY024I~0huw34PrqKBQY^CPj+_pxj-z* z$jFE+1!sdI=jYF#&w)7k)vH(AVG<|?iHnObbaHYM6%Y_$z-Q2h4<97=?c28yXowhA zgVfa2M1ihhc=F^4!}sssVQNrlPEJk+VPRngkXmF36c>REd4K!%EyLWoa~qKrp|g{d zlj}ihkpYT9ND>W5Y;4SWbU_q@{{8z0cD9Cw#{a*6|Dvk}^O>2M8GL z5HKdb0AqzGJUkq4V1N7e4HR>qkWE4{$j{Gj#imW0B7pwqVPe9b=s~Uq8RFyPvl7`L zMzl<;0+jRzVliH1MOfG$fnuA1_zp}C-5{7a$uxHNlWfdj%>4kSW?o%P88(^#0000< KMNUMnLSTZ8;fizs delta 310 zcmV-60m=T;0?`7HB!3u5L_t(I5#5uqii1E9hG#V(LIQ$B$pf7D9Cj(RHN6j#+=E!8 zODzZ2`65>;90-C5L9tLX8!*f+OCXTqxP@WI`F}oUcU(426TD+-?|^W|KL#!69XXD( zH4G!g*f7SZ^vL;e`o90gNUEy(o&^Eqy6!Oug0*E?@C?*6&3`7(^CLnWCCKx0T}hpm7}UZTr4I z4Z|RN@87zvLl}lo6osG*Ae_V%r?QOW_~~W0ZObQjp(FtmMbRnCvK^j(scGt;o_Eb5 z*wu-GnDl5rP%n%yR@9`(&sh7y_?a~sG}m9=)&0MW`HQdr2B~OXUp4}}QUCw|07*qo IM6N<$f~k3uB>(^b diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-off@2x.png index a0a59e78df8651fa11afd04cfaf6438b7f15080f..4c06d66ae7afbad47249ec3e1e9ce2a59e8cbc31 100644 GIT binary patch delta 620 zcmV-y0+aox1*rv)B!6&8L_t(o3GG!qYidCd-RsxIh=K}&B1DVW3l=_JYXX+O=5~Vl17roKakEUf50jPyQC5P0S{gpDgtO8=O*cqMQdVjs%g2&?tS;gwkmdoXM zu~xpi+8znM#bc0{2Td;(J!C)bu&x?3GF5GUnDtB)m zlr>FLq*5ulw*%Yl0+S;FDSxZg3UwF5;ZW`%cZi|X)Q2J*4$B<`fz269Xj>*I12WBK z^M(W2$6qd&uRspA#a88M2P4V^h3pzIfTc55Aw}Y~!gq-4YM)@OLTE_zfL#Lyuym%4 z!0SLmW-^%#BuA4UV(1qTDHeFYi&sk*WoHN5LsX{M@pvqz)2S@ep~~#CL@vkxYcv|61^(@x z@BU^Ls#GeiTCH|U!TwMvBPq)#_s|mtA`%{09{&D?$-U_(SVNm1x5^quhI1?c3tDMfvcYLT z);=f^z4J&*Nd~|=LWucNJFZJVk)lILC$6KAqmWJk-T8k+z4I7@+!Zwd0000|A5H zT#El>uKz>^TpU8WiZ}{63h5NkojX0GQ^;c#5R1hQ>HNm)^?&9)9#6Va}9go43fK9|dhcswrLZnr9TV;__? zO;e;&DY-YB%>g`^da^Z6Wg7yW);?jW~~q14oeA{-9O9Rz{R8B1tYCMW|kjYi{| z1KGr1DwQrl4z|Kp4lj{#Oo*)iyX$reyEFAOBZEl2irqbrjOxpD8}QlEYr5itg}Qe$N+0F7@!5-*U#5~ zvkH~V<%epux=+FWP$(qpkD<1Utv;-{V6WHvs?}-_91i=nr-ZXst94wj*H36L9uOJR z4k45Y%2XYfqJE!}mqGceFlL7*>Q34^k(z4rs{+r+ruZ zrbP746D=hf0P6@L=1*<6F8xJ{4k4YmjzW$?It6s+KSa&*7^ls#S^xk507*qoM6N<$ Eg5a_nH~;_u diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-on.png index d303c5c66a0c676a268a35043d9ac6ee18e8f12c..7d34fc86f9bfe879eeb027025ff62f307eea1aea 100644 GIT binary patch delta 304 zcmV-00nh%-0?PuBB!3b~L_t(I5zSIT3c@fDY(zX*tXS{`9zHSL`dWRg!=x|lu|U^5EuaiKmo6UOf7*6 zVBzt-CXw0B@F>f&m9?RI@S^_Ci+@0#;2C|ACjP)f zCD_zxqBx6fvl=8CIk?Ht?(XbR27<`*+^ZuRb%>D0{|UA1qX{ACbX}kav;hH>1(|vQ zj(~>8w}M1w8^ePnNoHDWiM%q1LtF?1AU@{Z#`8u>skBmoHGhcWWKie7?l2H_`K+v}NZk1B1IF8roA1p`E>!X0d0hTE+0R~h8%7O+Y zupwZcPUdl;CMxm{@XeQfg3$Fzh>~A|d-RJ=eZP@AQY5^80+rDF00000NkvXXu0mjf D_IitN diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-selection-on@2x.png index 473d6262e457416649bfcf22868aa7ce0e914472..775593d1caff14c51fee74b3e57ad16b881aad2c 100644 GIT binary patch delta 639 zcmV-_0)YLh1(*epB!6E?L_t(o3GJ3aZqq;%hV2RiEvl$Q7c7uikSlP3?2+IU+#pht zJFtc;z%{za4U!E;i9iA&B%s)-own)!2_waxmkzNLwF^J#m*qEa{`dRM_|1gNVW5+rQtWPnw7&XnP!=R z@_!Rz;fJt^JyP&GPRH+*?3zrAYsP#-|2@}QGV_wHJ%8}lm$k;YlWSdtoWL5%d{fLE zmLN{ybG7))$5?k<)cmQO{LYDQ2_i5XkH>7_o!DeDftwE|SII0vY{1jOVDKSH60Qqd zwKPr7`u+ZUk}ev1 zJ%b<`EFcHDO6F9E(VM)&nu1nDQ4y*L>Q)CjjAf(ZV6#iUCft}c{pzg?Zw2{E+E&O{ Zp)39xz^cF07IOdq002ovPDHLkV1fomJO%&& delta 658 zcmV;D0&V@61*!#*B!6*9L_t(o3GG%(P69y?Wfc4%MiOJwj8^9Xy zS5e=^^uXf*J`jkaDCj#7$f{7lZoOVVY`5E2VHkRdNo3^cbUIh$B)7`31kz}J!!fSJ zPNh;g>i7GviBt%a$w_Vq+5xgge#0^TOeNQK8v@@h93 z7f8s>96(XeFMoCF90CiuTy7oDxtQWEfSkD^CJEwh;-a3nO?GZ150mRQ={*rYTERIS z+_wG5fc<3^A#318@3(<$V+5+~>IY!M+4~%<%z<>)p z#RbIpx8WC)OYug@q#)%Be;Bh3O+7d6lNkxH2F?uU9Df{f75a~xznDW3#8Y^H7XRDb zGstVUq|NZ5D7&uCO7hGo2tzq9`V(FoKo*s~Aij89y6EQN#zt@ZB)6 sLPpd-I+FhDnM+Lt*-DyL$X1~*QEa`epOWI;01E&B07*qoM6N<$f~93ErT_o{ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-off.png index b0af0cab0a86f0f382a4c154d0d259ccefd5e0c3..0e7d77e0f0e9971ad59e2bb8ec306029697a607a 100644 GIT binary patch delta 37 scmX@abckuf8b;TNYo!^vC!S0d`F$!h)Iaz0Ee0U)boFyt=akR{02b&DEdT%j delta 37 scmX@abckuf8pfj&*Ge-oPdu3_@+T%VRIE`YgaHUVUHx3vIVCg!00@Q*z5oCK diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-off@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-word-off@2x.png index d0328da5182b2820d5918ba45f34d082baadf9d3..bcc96f96d1ea8ab57eaa3ccec7227aaf4c4867d6 100644 GIT binary patch delta 695 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 Hu0mjf?P!aL diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-off.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-off.png index f9de6d3ad9f261ce3a6f44da06c31bcd194af126..f60a4d3caa3bf7ce89b81b984ab941100709919c 100644 GIT binary patch delta 495 zcmVtNnq~Y`S2@0NTo)!;$oO>SMkN^43xe`GT z$U2&I9grzwZ6GhLg!;I5*%(c)P-<&*v+}VzKM_ z{Jf`*X1Cjq0V24ir+7U60@!%I5&Z=Ef=Un%QH=tjA}!tu35UZOKq`1cD&+BvYJO_9 z+6Ltm4JZ&yAIM_I?RNWdtT&BDa}o>&Nwe8(i+oE%>S+4CUhgAJg45}|(dl$OgqrPi zI)vl6!je-oArbxkTt1(_DijL)xWP>vuZ`Tw$z<{ZzKY084aK18;x4oYX`v2Oo(bLR l_7`aNZpI3g5o6!KOTQIknZ^Z3`fdOK002ovPDHLkV1fdQ?<)WR delta 496 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 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-on.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-on.png index 09161a21b1d078a1631f535aa5bf8f24b1954f38..553da6835f7035f5d49016910ee7e7c770c2ecba 100644 GIT binary patch delta 569 zcmV-90>=H&1kwbMB!3x6L_t(I5v`NYi&9Y($KR__@JE>uwFqf3I;$Y4piRsoXf=OA z?W1KI1vd(Ui$WKs%+6-hqNU6b=}sa18$uXEY^pa}BEB~;(|fPak@0!oj0C}a;mbYe zo^!tM-1F|$UDwqv0+%jAL^Rg^C8X=R#3QE^`vlIQ#Yt?|C4b~-t4PLLC=|Ng?RIx8 z%lb`@JDbgZHk-{i#BRaMYTZf^NdWOytFZaHrX&A;(M6%iJ zUN9KU7mLMD;*ZDU?-IxK>ODi<6dT+<0oZON5_yjQFdB`%bR5UEZTk=Y1GUCC?9<_f zNs<9y2?PSKSbqcXAdyJu<#KtG9F0yV?D_A_LY2ULxSdXCdpeyS@MT-NS%p%m^q%~6 zV%~|ChNfL_x7$05!eelxz%)&(*Xw=D<#HnO1jbY$T^s=|Z%ihW&#_qSMxjvnn9t`s zbmb5V%@Z_j$~uDkum^upySz&aRRZ^=0em+c4!^*^mw!s7w!~-V$M}!nO(^s8-62vm z^@-TS;m{yStJmwB$z(EKsZ^ecKN^h=SxgEHDC@ElNf3yd5Zp(5~J{OCX;#B@ArT3ERF_)fl;kiciDdmeK+~6kUDHRW*lz7b$At;@GJZR z4=Fx>Br*a5PiXGwx`@eNl7R-H++Yzq|JvD9;S*8=|7-sR;j97%bg5j_00000NkvXX Hu0mjfxWyNS delta 567 zcmV-70?7T+1kePKB!3r4L_t(I5v@~AYZ_4$ok^TYJbZJ#iqyOdD?2V&NNN? z4UMTNO21aCy+iE@>-N5-CKLdndcA&1gS)6!t2BFzeXHLJ?3;{%s3b`}nxs;x&u+In zlgs74FdqyC-&sd!^;>~`lQHmo0w6YhKHm}Ymwvzh*f0!J*Y!WhckMlnVb6x^g@^+V zxLmFitkm3u!+&A1P$=v}BjBV%>^a}f+?5R+ZnN1u9gRje_+_1^S%!Q*{}K8KYSxVw zfgo%(8jZ7g;~2QLK~+_)-ELo}(`lN#27`7ZkqLn#$A`n=*I+QXoy}%1GMP*hr!r9T zOa+24!YYFu@Dlum-o;y*yQ1)d2S9Fmz23LTcVe;F0e^GMe2jb#M&1;6hI2ZvRfhu?fegkDR z|9d4A2r$cNnuy{i)`uVm>H_(KX~sW+@k%(1R;vD^@E6wN0t4SY>^uMf002ovPDHLk FV1in567&E7 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-on@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/icon-wrap-on@2x.png index 81aa1d4a934a115cf820dd9051ca219f1c9ab6a9..ac044cc6b0b694ca768ae154cba6beffcca70558 100644 GIT binary patch delta 1412 zcmV-~1$+9P3zrL!B!6B>L_t(o3GJ6lOk7nM#~B`iC?Hxw<uHxCPFkQ!d|2-SP&9+#$Ze!2w@2kO&YBRg@po!ZeT+Ne1JuHs7NX0H3Ngc|77mu z4&2LJSeP#Sl7G(YJLmlGcfRwVGp>V!gRlq5!XG3u;_^SCP=6;98OkBlxXa~Ilp}!F zQfWlpOMTCeeR9=Nvbr4lv2L(I#6^!tM1xrHu*;$*NogHjyYI+Tyb08k*1AIZi#d2TE zPCI9#-@kBTMt?@emASdO@94KRuh+XbGBWZpb1njrion@+U)uq@Ni3-7JOVCGO-+4m z%jD3&(9lpjbIt=DuWgi0Lr7syZftC~i>grdRn*<^m zv)tRV)6QA_SPaM-9UcALyy42q%H7h^(oY%tAUZnw!+*NEx-Zw(*6y2qNc0*!+a z_BaipI2OE063>`7tFN!uW{uz-Pz$a@>W0~8b#?U*`mcg`J3@wL;N#eIWYLLYE$*OT3T8zM_|-_ z_77&%?MhohekImo`XZ4KR96luIv#KrC}Q`4NJ}VV%gV~qD=RBsDk&+cq8+e_iHV{SfHZhd=LT6`Z&Ghv2&Gq&5JAYj3Epwtet)t?e{QUgutb1%yTi!Az zDe0udxyb)FPB*plBXtg1TU$S4&1)bNIKTEdEA+YvSu-;;Lw1`u?cdng*lK8K5Sh2Y zGvExc6-KKZQr^V1x3|BXot^zW^&lhkZZ=UK*cv~qp2fw*^|7(B-`d*Ren2#T#rYSo z41YF(Z4Oxx(s9KX78brr88B3LlUwsWL~rxcAVQLDMbm(Mb;FD|ke(j*z~A6^AW_sU zb1S-(mS#&RB_-vOc}8t*?aluFeuFa*H&7QqCZdXfuD@>H1`vrgAaa@~(zbTw zfyQ-z0`JR&{VT*zS3S(_cJEPdrurjT0DpRL#hk^KmX^Mt?lXOPczE~=G!zvP5JBT{ z=3&_OsPOZd!hP7(7rDK?ZA2jq5sXYqO41iE0fk>exKU70Ao2lYjv^ugBJ?LvbTn_* z2^su+=daRiI^X^I`FU;bGolvO+}wPHhR7=Ebwu%1RaGxi28`rgq#+Fxfo-6Caew@t z)locN%EaA z>gnnEmN}A&3n1Dlfx~`QgmjFLfqz`^dPPOWC;UhUz5L7sdG2_7dwWOleH%Or&VXYd z@`p*7+g8`!_+fpVh0lZRuS$&$7QtKGK zkdmlLM|A6?CDI-d5>XLy1L@8h5GlQlq(R3jG2_RYWrU0w{z&P!O(gUvQ>l*-ArUo{ zSRyKdC(<1JkJzz<H0!tMn3$ SnX+&I0000cSR5 z3K1npWU)kVyvkezFGReNAPhn=#9PUNrFO&83rozs(HnQw{mZWbBNY%v)L5o z1fa3>HKOjPydQ#(T#YGNZ87>W?m>fyjT(_i0?FV}m9w+69a&jfiBnTkclo^ww!seA z1OBMQMChboV3BzQI6?04@bE{=%gYmcdwXt8DBIoLbuBC`jI_12y^GItAO&av!64vb zI+9=xDk>_zr0tWWq<^GO8yg$Hb-7&kB7I2oE41fo z468X#LrBSafh3-foUEy-Nt3mL4?zRC45=%TJ{ubwe^UPnNVOtlXa+tpo3_m1bUM#O z_81!*o8k8jFdf2Ne}DgVw#sMtrKP21P*(mW8g+-i8vLZDqz*V74(U$VIBoO%xCK^0 zP#}@2XMZV&jT0xaly0fr)S?m!DX}6cUkwx_Vz=8HDQ5$%_Al@^kO*p0`Pw=XO=2i5*;X{Q0Og!aCroc3CA@s@ zf`7mtK%%Hq=23L{nwk}%l+4V`7b1H!G&Ect8XA)5>3Hj%BU9q*veZ{55{iJ1|2?47 zClW3oa_T42mUiTU#&Opk7ZCPq+(|*r@qc(c`;^mB+`& zzsV0pMPx+4fT9-lfu@ijHih55v$JDFAucg7(Vm{3t`$p#6@KNyW^r-xQ+&flACVAQ z5n2a|j_UT{8q+h@jF9%%x3sjh!Z&C{Ev~)2y^tS9&S;BLYin!Yy6;UCUOhk-v%yLLcxyyrUrm(QEz;LXuudhi?PIh&7cVFX@Ol)p$zF1aPR%Nv7ixTT3 zX+f4rE5Bon6&w&poOLY}F!`mmwY3e}V>0s0%NBl2-+d6-7);x77L-+0 zRedJOkA=~|z`zgmkyJbnER{GKF@G~c(v>HGa^}72>gq4Jqz}COA_?-`@ed9T{)F!b z;5DFAdhA2~I8tWEUP-AaJqgZ%{EUo@Jd*nL($dmg{^xQ+A^^#)nVFfD-rnBpBO@bv zoGpM~fu15t=u_p<5kd?W7kUFZD2h}z&=%^omkRV85ozg)M9@&;+muB{uYUm@j8mD} zDbqk&om5vkqQRvl(v>tIq9Wu0s+S6j)EyuVvb?8gi)yA3Qm@bhO%v;GlVS@ZqeZA* zL`6y@1eH&uIrtwjOG1Wch?pUWBBQcQcaowZsI0U(Ro>7yY*7h?Js(iPSnUIvF8Vb8 Z0TWI4@j{mTj{pDw07*qoL05AXZ0qg;gB!9C>L_t(26{V8B3WgvQhBGB}a&m2I&PxpM@T0*C2)ckK!N|V% z7&hjhAj*;F@%4w@_uaKLL9lbK*ILg)h!Mh}A`I7c?Za$V{G delta 215 zcmV;|04V?L0rLTnB!9X|L_t(26{V6f4ul{OML|}Pii+0C+Rl4$BjXKMk6_{fEDQSZ7(bnog%>9q-mlFn^!V!*;vy zmpXuj44XZIcw?_IPHpHIV>01a2ZA8@#A1)2YjgqwBR9uMtD0*YE|<#{Huz*pzg7Sn ze<;j1fZQlEqyG?IeF?d+SS&(x!xKQ-CIYl!QTyz0I7hrDNpk)aivXPP@niG=ug51x zv`m8er8N`=*BWoY3gSim{I?9$H*Pi8J8&z(rp~_uUt{&L?f>VcfdBvi07*qoM6N<$ Eg2F`CWdHyG delta 453 zcmV;$0XqKZ1K$IXB!8w!L_t(Y4eeE3YQjJe&Ywa+vQXlqg?f(msb|PRJb`!u50W$V zsXc>FVnJVh$sz@tx;dPkjy6gWxj7_b|>!fcyw+#yjzl!J7^1-&1PN{ zMPuLhM^#ms?=bCeet0fC&&$fP+*oT@)9JKo;@xHm=JR<7@P8vN5_dijP=@PmGMVJ! zTm>-lIF6qgb-&+t!Z37bkqN>6$s%&+a5y-5p4XPy*lxEkk_j!ra5x-;!I0P(b2hQ4 zWdIUTmSq%SPFzc3bQ1mK34d&{H!9nkAJ z_d`UL*L8^}(tyTsR87yi|TqFCp7cGlViCWCcd_r{oxsvro%#2C9_7{I(R zDNM9WOCXk6w3~oeO0}G2kj`16`6+nhoMn*BulnC$pqKc%9YA;~<;h{b9tzhG0%rmc zd`jufVFveochp$C_vf~4g2s6q$1+WmV^BW*W;d*`O+-QzC0qai002ovPDHLkV1iL5 BV4?s3 delta 199 zcmV;&0672d0qy~iFMotE424@gwVj;0Iyt-b_Z5F%m($tlz)``eR8TLk5V%_o_Y0)y zd-;ZvR9@F5?!*I7-}jj@rYnk~sj8}^2ui7W7>3a~cY;Y^UDut~x}_=zLh9oW(r2UFS@v~Z1?3)NoV@oVgYxb-djle@N=o?UJ!}8~002ovPDHLkV1j;& BUtItI diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/overlay-scrollbar-vertical@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/overlay-scrollbar-vertical@2x.png index 3d3ff15c7804de01fb73be7a697edf4a87177771..e860a90e08de8063fa2f983d5d7f66ca032d2d95 100644 GIT binary patch delta 446 zcmV;v0YU!J1K0zQB!8btL_t(Y4egdqZo)7Sh3zE#1fh5XOOTp#&|~C0h!b!ScI>-q z&%lyO>Ga&?c>_Jx13RN z50H(Df{(KH(Tmf=0*j*f;$EKTH=ztj8wy&1_R)*e!veD`+ahAh===R%w@cU1fYLP8 zI%Ws?bZz*4)#%Z0U(r`0_LiohouteFd~a&>XFwXSRyQjjkze32Wmj|mia?#=n7gW~ owtzL>-W8D0Hezeo2IBgD1C6?#l!T~{JOBUy07*qoM6N<$f@JL8(f|Me delta 439 zcmV;o0Z9JX1JMJJB!8GmL_t(Y4egdoj>0euhWnzZ0yS*91Zg-2bBvw`aRLs)30TZ( z<_s(uX;^mEs8YqFW&B6u$dqa%3Ok4t*-7lrj#J5X&*!uA$l*N_23F7nE*1+fNs?(0 z1Y_U#Rj=0zNIIV9o%;QLURBj@v)OFt^LgF$#oCouUM`nW7=MN{*L4*jn6x&M*N_w} z-|zQdX_}TzsLc?WjYgwcUDqm#B4;=pI*<}X**Skx4F-c5!9HkSWNK>|$MKY|rI3+Q z%48;^1I&S7gTU+cdMa(8TOc`%vB6nEZyNvuh(XXv)5Zc35r=HtG?Z&$6U%+*7D&3O zCo;HU*f(Gw34i3hyW|UaSNV+dtA?){cBsIUe>NwS3_at9VSJ>$tf9ui;c$@Wt-7!= zKGMhnsh$4dpe)OOv&0P;NKMW7v;wu(Tk0!0jPpFVPHYSif)z!xFV)*v$8w1dQKlkt7|Ml|a%fJg4F6_eU1w%ta1)#=F5fKsJ8yg!R@bU5e z0U01DDEPajrR6?I93&4?ht&XGUEN9Y^75<>4i1+jB_(s?acntH#hgYu&^-4qeqY4+_-VW^4PIsszBi;AkMe4vihv0rFBGBR+jJ0 zn>RluPoB();R04xR(7CG4C3PABGS^*Yk@-Bf!LIhk?}atMVo+O!v~TFslyrzJUl#q zfQngxq5m6b1H^ei0CXWE&{f};EnD^nq#mRWYZw5X_ZbNOf&2`#EfeT$T{APYb3j@N z==b8-*x36(`TrnwSi|7SlP5>}`}-|^|NgxR7*+KO3JQmTF~Ils?b|nxA3wGN8a4r_ z)}x@H;Dn5fj5W4&;B@ZXx%o3^&Xi_hVfim5CB*_v3k<+OV|e=X=`Wx<=JfRRr$$Cb zi9ogcuqGrRsDAO{MdHq#JB5KZ^8kZ@87Ti3XwVl=PtP~N0A2u8(~2`CLV*hqR{*g% vC~|=KHxR!B;x=TP@fk2$pwQa@fB*vks;Ahn+)_FC00000NkvXXu0mjf6ulH~ literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/panel-close-pressed@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/panel-close-pressed@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..8d8b03bacdbc1660301fc93fe2945d01eb27891f GIT binary patch literal 1387 zcmV-x1(f=UP)T*C^5Yd8*4lcT=n@($?3yWQJBPhs-#dcgM zNp-YdZ+t{!OpN`$+uYQ4`nZ`X43u0r+?#v<|D5lf^PTgb=x%Oq#LK+&;w4{x0Db^| z0Db^|0D)Q;=I7^|+-~>1;Naj!NMS%gK$1?UyK=c)yH{6NPaxmu_4>~@Ha28m0OuiHQ*aApk;zgoG#ncp(Df^72x^sDN={wOYl|(b41c^YfO? z&CQ+X22fmFoEaJ#`h8qnTv}9Al*rA^wV#}vgc1PWi@?A@Zv;Hvi|FWRJB)^6oscBy zXS>~AwYIjl<1+~vb{P30DJdxpN<~9MgFH4iW+^W(muZmRg9qMJ@Lf$!jWjwsYQg=O z(MnEEPJ@mwsmo^)CL|<`K!FnW+S*#VtE)@GYg=n;tKv1hG;tlD>3LaMnbg?WNb=eG z`uYUODVCR)qw%{C;6`x&P;&$-D=SS1`v>vy@gh4r+tJzCN#k3P*LZJhYm<-;86jq_ zMM+7C)YQ~O3aM1a(oT<_JK+Js3BFc;fi@*f2FUWf&S7ddtI= zX2{n7p?D86x|^GuRpT98FgrW@Cdg<;{)4Ck1T}(~o%l4Z2>?Rh!Hr}*%WHjoJ)^q1 zdK$pqz4;A=0;0XWUESg}-qFz!fXF;Vx}pKSLYG|ai~ca$`6+SAh`6%-W6+{3-9p2WmNhh_li|0iTW1$k#nzFp^D!fkDB z%@q|DP2U1RPHdrBNjW(=GWSvk`Geo5ngLW*RXxQc45ieVZ*;?c zl2t8Hta%$zGot|o5f^k>q4Nh{PQzD#`m6Er@l2d*23eNH-rnA4X0y2uCH5(#J2f>` zVLSKo_}$&z6$b|gpP=OKqx9zS{Vp32$mbXy9^L`KyU_c@m*e;m6c$cSPNoAyAV~#K zW|Y?*o)Hu_EAHjD!^1;GkTx^=#~pq{S-J)X2Q5g|I*Z8Hc_0Sz9Y93R%*>=9%m&so z6{~}Zr@<)0h&qU12vJy%^f(3v2KHc_Oi7S zL?jy{fx8>s(#iu0VQ1;xhkW}YGa*5I%mxVq`jwTHNX&Xhcx(VLWhR56Yed9>!Q;HN zwDb&lzs74j6oCZ1VN>{hpnj{eXjm;}1JK zI|Da2_wT&Cy!(EBe&0dzAaz&`(A3nN1XRFkZf<@FXnU@YkI(%tU%q?;YP>5XB$Qxi zXm}MU&jwP5H4K1?T!9*yrca-)d*#ZN+Z#7-%uh*4X#z^+2LuFs*45QD02;vf@83UH ztS$gLmt9m;ltEHbQUn+dYk@-Bf!LIhk@2{Mgv2JG7#~O;qz1oFzzG<5^H;1`Aq`AG|7B%mS%8U&;q~j+3{Rds`SttvZ{~=Ih^IO_I*CBF z`>-Y?AgBh~n0VyK5#g_2zw$5x!CxQ%riCwdc6M)oE?NLo(~2`CLV*hqR{*g%5VHdD rZyAOIi$ zAOJw8_QT4`N@G}9SWj?p@cVkbzC@?fCFAkM<>lqh#l^)F*iUx5eST$SMfL}P=5?D< zU0q!$f<1_ij{Xc5VK5j300=x2TCdl=;+7>`x2lYE-x>i-x@$eLqjIA zew>hyP--+9MNCYLh>VOBc;*FwVB-AzT%4Soh{MA}A4QcX>b3^2B?uCD#^ z^76y@_;>*yCG_PNfR>gPV^UJm=P;3(ndvZ_%?$n*>>d6#H8n|TX=(D_-kzYLHBxPD zEhm2!Rt^wbot>TgsDP6`=!-s8(~wj{-wknbam-LZJ3Ff%7#L{3o)t6f$L8i{WdOkj zw@>hHelRgH(Te%7pjE;2fl3)Ln0N7NTor)iTWL^)!aA54~5` zFaffKe&|#65Msd#(a}+NR8-Wi=Q~gJ=!eF8)c_FW()Vvx z-0gN#6{Y$4`G!|ja&B(!-HM6|nbLey+(5-k)c}wQHyB0XIE2u40RfU)TU$Bp-i?Mm z*HDFCTnupB(a|AgXJ^Y)MT(Qh4+P1f8bD4?&Jla=*pL4Ai*EA-@Lv)Q=yD*FOJ zS`ajY@5rY9{{AhTC4R+c9sZsNVqwbw5e0gsuoctvS#vazKVpi;Ig-AUZBQw!=P!E#3N1 zdfkpk9B38CcsJ}%*u8%%yT1Uu?0|i74I~2Q5qKwO@jCEB*dPDtu{`jvSpYx)Kmb4h f!2ce=-vSH(2hVSR literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background-fix.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background-fix.png index f8740348655a754850164d8f19322116312ef9dd..05440d46ab6ae2fc98e51efd08f3d5e4f1af9da5 100644 GIT binary patch delta 117 zcmV-*0E+*V0g?fbByL$rL_t(272T1s4Ztu61R=6S$NoFfF$$%Ho#6Ny_XIV>^#J&!9oTRLBH_< X0Ru{IKb5$K00000NkvXXu0mjf0hlw0 delta 119 zcmV--0EqvR0h9rdByd|vL_t(272VP?4gettL{TtAJMMobTW>;1l->AsMB^X(FUynN2!gKJWwwOO#em}NjV~mw z_rB+xQfn2<8e>RGNn!{=_TG|nw&aU`TG{cGc<)UOIOpB~%8h#IDW#Yi(2r|<_j#xe zMn&yEqri=8Ee2n}fo4@rQT5D?j5CZ)IND3LH6tw^X66+F# zL6sNUb?+sX$)dpJbB+a{!1Vxbz}^7bLAw>ewKsr%)HVRFy)^;sg|-23-GCc#1NJu} q^rP8-;BNx--nAt>p4l0|*M0!;bgF{f*=Ux5{sWtCXC|pyhJesi>CJr2QwLrgPSg%8xZ^)uU@l_KRxsChgyX(ljme zJb%(w+!mA1Po(Ka%TagqBDPp8?lnPMY(AggiS${^(Li-k6c3}(=xR2b-N=`oYC<1`Z4#eB6uBvJ!M+XFd6Q}2uO-+?nPM@M* zWg>hO2gCyC?mm!watra5IK9jEI_}}d{HAIlrQFTMDUR`g$!!C%12kwYOolsiL3RZs zk&F;yP99MInc9W15FqBbddngmWF)UD-me2A#U2{mUQ;{L4~= zRpS)5&fJB_bZ>wxjD_JQCNIMN8GQjE{eW4Zdu6PdyEDc>VC*4?zJW||w=*;3N3035 wyZiZenc_ag6!5@Sc<`(L79AX53;r7D9i!Rzdc%`Z9{>OV07*qoM6N<$f&q2?jQ{`u delta 502 zcmVD1PpIkAf|wNv_+vC0y;y{OvEgv|EZlbsV+oa6mW^Aj*8N~Gc$6b;61!~y8;p&)p$3vqImQ~SCB$AKP6cpStC&pJ<_mEG|Acyf zkia3TF_}6G59S`1?7-1{4(JOJP$DP9So2s^go?B(O>d+eDgg_(x+1x>QvPKHMRj(SSW^oijb0hXWO@K8(6EM$Yb(HtwfX(- sbH5S=nnJpH$T5724w-u=*S`sU0}NB}C=26F^8f$<07*qoM6N<$g22<|_5c6? diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/quick-panel-background@2x.png index 57ba0ec33a27c5b063d424e619dcc168d421fa27..ee7e828d97c69018cb038be5ab1d7aff187c1e49 100644 GIT binary patch delta 1176 zcmV;J1ZVqz39Jc_B!6>BL_t(&1?`x0^biB^;^JZhkNC|9|PHVsOC;cPL?upccE;Y&OqA z!N-k*3r-Uhyoter?(XiI?AFQ2$+vM64PAeFdiotsxI@*(t0YT{M8 zzP>)&+1WXQ6YeSm`Tybj2723SwSHe-UUoJ&H}@b6C5ep#dt+l`@BI9{4JX`&F_cdp z=1l6Iu774XEHf5N{hAq-tNklS65f(4-O7Kt*xznTv=IpXV+X@NIu(m z0U*SV^zrug_ScJxi*~QqyD>pO43y6hml`zg0%%;Gj9fJ42||=^PUr2JmIyN6u;ky` z?av!~o|xHDYnsTPMt~RzYTGl7w_b8j_uQLP%70BucEyN(v7^>sHerZGJex3%fp0px zBz$9y7eT^jUi|n%EBa#fAbJ)79zi`zK*ti#F_kcR8_F~#hX+qb3D42;T;liMLPo7f zkfH@50!j@jRl)njCCHCiNUnKou!QZscomxq#>9vq&gA2JVW`Xkg-DH~p~OWtjv$FZ zn1BDAFfM-aBtHH5vA9t|3_244fB=)Qs^Y~kPsP%L6k}6E1}>JyhXXMZrK(Ak;#r$% zBNcK{T1>JeCQ%YZC^3xd^MfE7mM7+M_HixGtXGo&iBezlc;&^~5i1fzuoeTvE|H`X z)f|;k_0koxsX`Nm5(bBc5H*dJih-#}5PxKtCj}g1Od@sER2>PHPZnhS@%R!XOo@>9 zVw6t?WR9T0i$Mt~c>xGeNkHl=!#b_inmjKod5z(%0vlUc=BF=M93?KJTh(Mr;m>s{H|k2yY79K5Q&iYa9PiJq8Lw$coLIW9e)T4 z1tkH4FC}8>n|V=s*0}dPhnN>dyb||bF(UFgT+zb7Fzv6Ad z%=%SpwLxo`U)|f=dvkkxTi@K={Am5T+2P^g`<0cI_Wu6b!AwY>yE@9;xK>h(ZN*>-BmYVB4)coSBSqSQ?E+t5&P^ zj6k9axk3f?5Cib?@^T?y*^|aeg_2WZ*rim69Q*BdJ1Myll$~=SUZ>NcI@nk-1cg+H zxJs+lO8mS(Vt;vq1=$;J-dYmASl*{AD7N%vl{1$2=~gKAESFWGSl-|Y#g@J-L9x8S zf?{{7EJ3ln!GdCUsw_dVyupHEcd9HwvAn^8x?TP^oWI5L1`CSasj{TU@;+Tbv86Ao zLb1G$u0q@$1eyIFvd!vN?Dq-3>E~i_!3lRLVT_;~{(mN1U0r<(1)nw!E;volO(<5d zAbZ>VwjHaSoSb|P1)nzV?Ck6doN$L?1q{n_SV+chx7CEHk@#01o_-jQwy1Ux{BSf=%SJ8@fKVh zG%n;Cc7L7q+SaVDudgp292~sc*w}cxwzl@l#<`H3*mwaTzL>AWay%ClpsXu<}@Y9#!398wHMZAX}=8oyEVjQ1@YEP?&&7IIi9d74Zy>dPky z4SzaH7F=3ZjK+ymJ}mVZ^b;E;h)FF5h{W)DVukgs3{>#ALDZFu_-4#6?_Uc`pPe zas}Njxm)L+0mVd)2*&cnOezR$B`>cr@#06?(7!%Da`5_Q$nU(KM+pfPBPyS$e@2k+ z>pAyzEwDmC1Z6}eLkwq{#=Yk`#7GqLUdhYf&yPB434(o$6-1yDm2~+qQzf$gZe;e~ zy!u%$0UB3-lrOA|K!OC_2b(fI4}7-IaNbyES*j}melRsQt}1?Elpw!Q&zb6Jcw!%u xd>+Zvg7QHB=NNCQM`h1~9(p;SEA-GS^cT1Xb`bg7x%~hD002ovPDHLkV1g}AG93T_ diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/sidebar-row-selected.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/sidebar-row-selected.png index 02d7e6864a24e12b1a53058d0313a49ca0330431..51662362868631578225c119a06ceff63772968b 100644 GIT binary patch delta 100 zcmZo>Y-F6E66@jV;usDF3AWDkw4vM zebzMn|Ch8;cDL7}vmoxHT9-XgTe~DWM4f Dpl2)F delta 102 zcmV-s0Ga=R0fPaMBwtENL_t&-8DsQY^yohW8ekC-=SCA^U|{+B^$(`t|9=>|8Cd>d z6a0&*5Qj?a+Oe7W4~wBVY{#bkFE&A}wqq5fP&-yLvABo<04Q)a<Ga_MjwkQAq002ovPDHLkV1m&wUi$z5 delta 241 zcmV?ZC9* z1eD`NmnW0|nURrk-@kwVs?yWbuOXYkh@1##&z`Lfn;_auAIY1wcz(kR@rv21P9#pg4e;nQ9p>K`#bM6aYhD7mb1* zIZF2+dk)0`zyw$Ul>CUSfXeKTKs(Bi%|vlPVqzjFi&y~hMqo&MK~_i!8*0`@kl9c> r;Kre3L%1j%87Lt@M|XNuePwvgk>= z2x?8iQt%LNgz7@=LcD^w5ZBUbX=vj6AS*N9G}(2M3=9Kr<{RF7^UY79MNza>XfF$F!eh!K(CO5gXlZ_d&#$%>Z53ZBc-V zm`sc87HA>^RDaYf;@lL0gbBBR@B2H3Va&N874aYlc5U1K`OlrrfE=wpX{MhZ^iD_= z;TaG+^_3W{F7OFW=?#c=-m9xO!F-ZHVnCPy)E9=~1s9|mj%)Ce2#iP4%~8q zs^Fx-vaBbLFKYk=;Qn;bOu1kM@M)}pr7AU`)oRU`%WdT;qHj=D`h6T>%;Zb<;kF>5l(^@JFntSXa%P0ss8+wm+ia`6iemSsIrcu4_701iS2Q;!N};O|X1kWiHZlyXX?!Duu(M)VDg)8EGtcD^OB z3l1cEJRsi!(?Ok1XAeFb{d9!60885Euz1IRfcPWkpj(qDsj(>O3Wz1w5-$D+?<0UrR-u6b00000NkvXXu0mjf5eMh7 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-corner.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-corner.png index 6369c9f110e8e0dd32de440a0a18da5d0353ceb3..89cb55c9e8430989ecccd77a77496fa6ab0821a9 100644 GIT binary patch delta 114 zcmV-&0FD2c0hj@hEo-|Gz#t4m6(a^<|Djli#!gVWw4%zpQ~A!|&o$?~#~2`q-us)@ z8kC6m1e>ZtW@4>{km#}t(se;S)#Xf9V_bM7YaA~8r|f@tH;UR-7i!5}sOxZf0L&d0 UVs|)BwEzGB07*qoM6N<$f>dNQmH+?% delta 114 zcmV-&0FD2c0hj@hEo(atz#t4m-C!=j{m;aKkdhX6>uRPx$>UQXpKH!}k1;?Jz4te* zH7F7B2{u)Q%*0v?A<<_+I#<=iE);L`FPuc(QZWOhvF4U5{P}kw|0PBSo Uae(Zy@c;k-07*qoM6N<$f;jd!g8%>k diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-corner@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-corner@2x.png index 75ea7217e8a3b5956f2330f341f7774e7599bf04..be274edac86b50b010943535e5268ed24c5c7cec 100644 GIT binary patch delta 238 zcmVxf9(39d$I7mx%| z(~t0;e@~07*qoM6N<$f@)oHhyVZp delta 237 zcmVJhWY3Txc`|r5I=V~5%~ZB diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-horizontal@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-horizontal@2x.png index b074beb3717c8bb957d6e85424dd9a244ed8870e..34b18719eb29d461dfbb35b9d732cb2e7a2f7036 100644 GIT binary patch delta 145 zcmV;C0B--R0j&X$B!6^CL_t(Y4egh^6#yX!1m#nJ{WnEBMtB#K%LHmh;yiX2KN1mp z0Lw)Lz>eW_P6n*158R@Fn^_B}LLBoL>lmA3OzPKhjs2;byCg7=ANW+5>c01di+O(h zy59%(%&dWHsX6f8$VY+;m#?*2JgpGyBL_t(Y4egh^6#yX!1m)9!{r5yc26z{f%LHmh;yiYjhe|~3 z0UQ?*06T`yIT^63K5&ZyZe}f@3USP1tYd7BF{xk2HTI`!Zb@JsKk%tA)qC#?7xVo1 yb-xennOOtZQgh(Fk&gryE?;Z4cv>OYIDJPr5I!}o&lmsz00{s|MNUMnLSTZqe>$cB diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-vertical.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/standard-scrollbar-vertical.png index 1321ccc8f7be1fc1c0c15a6c94bbd64ce9743d21..5db1229a2d88f1a35f651dbd5ab4d271e33638af 100644 GIT binary patch delta 84 zcmb=anV{=m|L;$|qoX66n3xz_SXh`r8W1ofs;Q}ooMB-3|LL*9PyhNfWXt$&t;ucLK6UY8z46T delta 84 zcmb=anV{=m{_ak_qM{<3n3xz_SXh`r8W1ofs;Q}ooMB-3|LR!{f1}fzcKhS(|W4v-0r>K9{A^KjH}yVnHB>EH;S6;3+BL_t(Y4eioF3IHJp1yHpUbpJbfpcXmUVh~}Lzs>wW)Cz+~ z>h7VcGUpUCdt;1o>dbkbXEkU26;jsTTQVZz?5?#=wsPlc$%n6TlbX~p0e&tRZ68eP yE&}UG4WsRYN!>+YJ*i=|eK4uJ2(14f^#dWh5Dsmk4a)!k00{s|MNUMnLSTXgkv^&b diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tab-close-inactive.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tab-close-inactive.png index 17d53d21f82826ea22730686a386be0b575fa6f3..f1fadd1f7f50e93e268629c4b8356cd5dfbb3984 100644 GIT binary patch delta 262 zcmV+h0r~!+1DpepBmrZQC0_`uSFav^{`^^yj3a;Z=g+6!-rn7kl9IE4YETS7_re!N zMaAXX+S&`bxw#pD8W^~^xc-}%n9KpPm!WG0@-btfsHlkZ%$YOsAg}%U^$V0p7!Mpc zkeHX3#}3kf4p3YG3Im{xhj@5+#J+z0`cz3tsTD}S0)~McQ0x%MaC8G0KsF&^o;7RM zES_#KVZxJV&z|K2#dRJ$cu)f5zg)d~^Ukhsyw%JV6>I5DBWrqJUxp0LpK}t$*;!cmMzZ M07*qoM6N<$f|=udO8@`> delta 267 zcmV+m0rdWy1EB+uBmroVC0_{3moFcF`SL}Pj3a;J$B(BzK0e)&l9IE4YETS7_re!N zMaAXX+S&`bxVRW3BqSJs^nX)R(>XxR%g{9g`IxZ)^akgtQ>WrVlD~id2Kw+n)czAdY6%`e6q8q>f3PdE# zvu1P6n#B_)OnCC_*|U70xXyzI4@&y``(LhJy?QZ_&xxcKtPfc;jP3RP`}b6!LQ$w< zX`m}IfNU>x%^*H{EWljI24wt#aey?)=_sKOlfz{IOr9W(5{Lv&iS}YoDLoW=kdz*p+&p;@A(!UT9$FB3Z)s_RjVQ+aX#4?zVD`P8{oc$svoj+V3I%qXOLv>j$#;c7B?JFY1|}ybAAe|dj0mRKhEN8krltaV zz5WaB#RimjdiR_ZrAoBTqftHq*U_(O#J3T#R`}_Ngp-|}MW#fLoKiJyZ z8Za7-ESJj_ve~R3n9yZRxw9$(I3KL3srhQN*(8(6#Paz(5nwwzJBuqTE1bmi^mMSZ zvoin!tg5PtWq&dmw!OWbKR-Wr0Q0k)V@0n@fIyK*Pg8jM1++IXPjH zB(d7sS}yb}4-XIJo}Qkt$K%-qEC%^Sc(tWPfbKqqSbsM*HeBg+dIGf|A#!$cal!3U zR%BV`&aGB!*zIp6of`5~Lyv9R_z}(y%?{gF(cXoDmMroHR^>iizO-)S};^3 zql32^RXqBeT2O{A45zCH2L}ZzNUDprwl+=zvq6}IDuJ1qnZfGn>UoNeoGaDd-Q67? z8XDRh85#L#Hk(&z`q4MSP?j(eUtl`kD;06EPJgot*4EbE;Qu~rZf@2gdf9Hbhet<8 zm1;M@pfwtex-cCl@GGbo^S!;jug1s6pBFnT^{!uDUM{6qbOZhU{YF%OG9HiLcQ_o8 zOU|j!e?TmKeSMkT-QC>Q)|SKRbiNxK8~X-*7LtLeF3k+!wi{xHaWke|8s}22#^=O8 zOjHV(?~qRr%_TyBj1Y;tkT|5=gd-AnAi~apWugQKYlHlugUS-YT)BQ70#`2H)Qf5x l-jowHI~4+z4BUDK{sLCQSVzj})C>Rs002ovPDHLkV1l?Fl`#MS delta 848 zcmV-W1F!t@2ImHlB!4bRL_t(o3GGx(NE=ZQew!GrztyNjh>=G05RroRQi>&_R6;M( zqs6P0kVa3+N-y@5(nFyKN$H`<&66Mqxipvd(1Or=OG_I~MKPv{#vjnQ>Gv%SXh?Q9 zAV?ttv-@`Tdo$mgc{5TjmlM}1rt1vO*cAem4E#SCn3|fptAE!qMlgjw3}s+?dRjJ{ z&7b)$bddkXzW^|w@b>0yM&6R%Ao6;>a!X4~prN5boSdA9y}iAKU@-XNqH~|m7iev5 zm1}BhL?)BT?eFjVfC*m2lv-;NK=6U;>gq2JheK*^ZWd=}XCxqYc6Jt)mX=f!e!oA^ z(a|A;fT*gf5`U>wN^EUyWr1}8^P^PYyj_z3LE&)t4V*q{YipAj;PCKJB$G*zPNx@4 zCeuxb8oa)~UL}}FBov#?_G)r+axNc4>#awiADi#3uCBV_c%rGPNusmk<72fPT-Vjr ziQ3v)!BQR^94K8~T_KOhvyNX;=ucv_M2`UXK7@9zt$(e#aq@}A#zqMu&d<+Pzqoip zQ54o*vD@vTv9U1*{{?-o0(2fm2+)N}A{vdlQI5wvHPWxTW^iOA*WTX#Zg_aOtl)HD zs11ROKz}2}R&L^{Ef$NaTqFGwZa)7RJ~{nRj1myCEZ^VV-Ssdag(s&{Xv58aG(SIo zM>Ul3Fn>f~c6K&^E|57380_@)R2&^0DNH@r><-)w0ZagGS#nqvP~tMRsH zvst#Ff|`fW70W0~jyB#45D>tYMM9In%*@OHYCXr(F#uDJTC2BxXlQ7CWMt%n)oNYl z^kW+m15yGL@j0erPq7mh+H|{MWo6}cJRX0FNq=a1? z_5{i!p-Zye-Q6$8$H$))1}pY$UR+!(X3yLD`}+EBAkB$bEOy)Fa)mDhXPyB|QEo12?fOu0W;7i%?jPW;0pf%yjg2-OuM z1WD+GByK@t&{7kQBz@b~w>zjEcupCAP=@6De-|BIKG z*9({?WE#Z)P#8!^NSxpW0+41iGc$(2fB!P@@$rdBOG}>!3k%~!)(m=PgA^iRxkHBz z{hl^$+JB(azXHYcKYaM`WAfz5{|_EK_zTDvM^X!MA-VvNzw6JRKVN{f2~^G%7@*%l z;!r-ag&45_b0I&F@e#%W(&9jj68bPXY_R~7BZ@`|M51({DWcc_0JS&BFZi_|^Z)<= M07*qoM6N<$f`RLK<^TWy delta 267 zcmV+m0rdW$1Em9yBmr!ZC4ztN-@h|(a&m%kNJz*x9v&VgpqiH;jWB?207%^R?%lgv zPM$$`eE9HV(xgfM4<0=D3&ATOmQb_LIfY1 zmhOVf(uJ!+Ty~Xa*IgI=2Q6KOsNDr=QH+QdLZJkt_`pXJNeqeB+|GB(h1>ML1VIWp zFqwPKnKSd9nfXpEk|fc64(ojeN9-DaS_b}~3`|T+JTYR78GoB z)6>&stJUi4=;#Rd_V!X+TN_m>6*@XPqVe(ZZ+^euD+kfjMgW%h@Y{`z4cgw`CY0go z>WZ@2EK)51Ih{^wZEYp4Kjrgz1gDXa5&XeCXZfHP9DjR80ARDmGR;|_-Q8W{lF-e~ z4VBAf>`_BQ1J%{l5vQES#>U9&^&SX z;P7FJhHU3&`JBmQ2&_S~0R+Tuw~N}NEIdr#9L5-C0np;&VsdkHQ`8+Gl=S6hq(mY? zp-?FK$A7K)UsC`U78WAtb~GXCeSLi`-UQSg-)KPWF;ydTb90)Hl^QHV0N}aX?G`+5 zX=xD+n9Jp8aBz?Y1_lV%1_;z0JRcewlDpl>4yW-(fTnn; zPEJl}YHI4c&*yto9ZYpDHT6y zuzAKJ+h}Ud4aH#ut8lGZ{S#b1FF+`4-Yd?_HmX{6QRT1z!RyMKSDyt4+}w0<#Hn_o zH#868fPt8?`OAl46RI}O9WLt9@DAf>x~c)FW#Ha3@EeQPm&PSfAyxnY002ovPDHLk FV1k0+mQVly delta 832 zcmV-G1Hb&z2G<6VB!3=BL_t(o3GG!&OIuM8o`k6Jl^8XOS{KD44M|9f3yZEq5{<#& zrlpH)T$V0e6_RCFx9+;@qW_?!%iyCs11&xxK8jK(0Vx_0qlhnzA=mj%xtOLm_uhga zg&dfid*;l{_nkR2Cni~z={|?~KEZ)qAyCP{|C52Cp`oXG9e-oQrZk433=9ts$1E1h zH_W90lr`^3Z1a`pmx|8FY|?v(Mn*Do%+}uo8S68&Rw?{iWJMo#B89`!v zd|c}8?v8bIbbnBFbu}Fx9#T4;&Yz#3yJlx+f0hGQ^qK@1kE>bF$B3#4u^)($I zA0yR*klk*lrluyUs;Z*X(^G(x-|xpC%nO!}THx5zBYyy!d6ocYf!5d8slL9R^7*{D z4rHXJriM5Z#3`r#{(ka$y#W8r@{C1y7$JZlg2cwg25oI^5sD4q7%1Y)%S-b4eB^Su z{s|oZhT4$rf-GOSx&W@0oy+CKo#Wn7l>7jZ&1R#vwl*meiIjb;)F|l^ zK%aA)i9QDi(v1#qyWP~=+bh1I!(L}+C$X&*4u=hs&?JC|sG*_ZHNS-9a5%*2(Q18t zeE^q1!sGGK+1Z)M0&HN*nR+uiI{HZM#HBjjE`Q*={gq56A8>lb=?4b~0jgBHodX6| zS669yd0BME!NCCq0)Zdgl8;NlRM%)D4VmJ-!rI!JJUu-vZ*Fe#QZBH3Vp06&q2zZ$ z*lcfaV+ot*EQ(Fpx$H)fGr$V``tkG^xZ*EJ6xngtBXH}(&vmn9x;**#9f&@-( z7&#m`r9rgTAvoxW5t~0^=oX=B;oPB8vkmVMr);PSfl3DMJp(trnUt9Xi{U~50000< KMNUMnLSTX+_?8#| diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-background.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-background.png index 54d9a5002f74313bdcf21463f349f35492915eeb..c895cb9bae8ff1758e0e3b8f796ecb8c7fc026e8 100644 GIT binary patch delta 57 zcmbQtIGJ%mq7kpO*@6`bf4;sJ&)T!6?yr<9D>pN9ltTQ@B1uR5H7=G8|hX6kzb=&VL3V N@O1TaS?83{1OU_O7aITo diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-background@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-background@2x.png index ea8ae030f045c0a1f8ce9359d1f28bd4180a749d..21fa7c4aad97d227a4d085fb9f7045d9ee2f3edb 100644 GIT binary patch delta 150 zcmV;H0BQfT0kQ#*B!78HL_t(I5#3cm4!|G?11-k?f5fxzn7{A=CSKfLP!DD<{9 delta 151 zcmV;I0BHZR0kZ*+B!7BIL_t(I5#5!+4Zt7_0~1IP`;Wl}Y*J=m03<%N6F85kRE00b z9FAnT*18BRNhyJCBFQ;pL5%TpK?uRgz|7DA`#(qJeTOry@F#gj4jW$ai>MEQ9Am%& z*)VP?rC3M; z|NlP&rGSZ213>yoGK3Kb0s?|V{QUg5kS%9H=HO!o2M1fnCnR+O)mWRE8cPD{Z!q-) z4G9SeQRU?3Zc|WD3>Fp^X8iK?D}#`b5G%|85RKCizo;k?9!BOGSy{O(5fNclK0ZDM z4h{~6e^?>{t07@wVbVf8e8;4vB;~|JMHslaxENSiSQvmo!obXg8H`vBS+#1_pN9`0 zHgDOsE#&vF-_AhIEKESnAVV0j7=mdm#IrBu<>l9AXJ$@uc5*KJ`TNf)P+0u^`xj^s zNC78A6_mv45~$pJAiYIbS6d#49~?PyMDN0dOVQuH{ZK_Q0mxw_A;szfU81pJ(Z0FRvFc!$>g%qzB{~3n0D##NUA!3`jKurVoV%0DVJJEu5KuVgLXD07*qoM6N<$ Eg4Pd+$N&HU literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-left-hover@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-left-hover@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ab2433af8356f50603cf9fef104a41b85d9aabad GIT binary patch literal 803 zcmV+;1Kj+HP)F|9LIn2W@l#Suluimkfs>~9`usfK?4(_liThhEge!ZjG{{gL0!UbMyF2E-9u1{ z5d?uAIt57(h-8-z>~6oeHp@7T>yEDCp$9+S`@MPlexLpResAV6rBVs@ zLnHeEf-?LEv1ejW6zT8pmwJ19J@(*Me^)`q#>Pk_5*r~N{=mQ5xumHUxEYwhcm0K`3-+)aMV}*EIDYRX~>IGJqfmz;PT9LduG;?AB*uTOb1i@o-~P z^Ejs9B&`Dl0caXb0G4HeVVJU!zrB+R);U)ov3R_bBkUusK|AC3`v6l>PJ`F$Eh|6) zs0tLqZ0lp{i{_ML?+V1rvJA%yjH1Y(A_P@~)&fm~se(ONz?z;k+n3!0_4IaOk znwneuv=}dzN=K|8Z>th!pbAKJLBJIVZeG?r7Kyyqb^XKS)O6p{(zmElC}@Jf1LyJl zt4xhGvUNF|e{H>A!0tKP7d)DJ{Nliy;i18silR=ZQfWP#&4HfJSKb|_8noX+kZoTB z-XY8++K)gWkQpAj{48=Ie2evPZxN`JL!f-#fFgaF)p+l6s|d!fGy_MoXs^&#T3T9` zuZ~`M($n2@&nOn>S5qm-<<`J345;N6$kei^61w`}Fh1@=qu&&mnVIdFUs$-5%Vrys z$rU)=)!Bq^;ji?WQ`?Cm_Wi!CF*n%>uU_T!D~*Y2KZZGB1z^UJ>2?$=Wh``)K; z;y4%9$n3M; z|NlP&rGSZ213>yoGK3KbGP1Kn($mwqkS%8<);pQGxz>Vw0-Zq3)@rKCk_ib3FJK0- zz!B3 zPoF`xv!aXzW&a=cP^7or(rb| zb~QjyhW{Y>nOG4;y1TojU@*w%2hZ&X&4yIxt zmjVH=#PWANRn->=QCXs@Aj>i&l1WIV(jbbW9YbdPpFz61ZmFVd-1HFbhNfvEQ58@W zB@@``bi&-60a3^7q7ZblY!JE&Let^Po?7LOrqy_;0*WGoBuO9$0`NQ!9LGT-k;JuMW`NKk(W#%{N0YULKsDnLa5d zT*T!P3M#YI)D&ONfB%_r>^?@Dz^!R^96t2C^GN44j^_t-J!;PWhyy)ElX!N}l2vCZ zdX#?+7{)kH(7w6dYW&2pqmTCQYrCFErC#aLXgVJM38>gS5Td}p9D6r*9@S{3rzr&rO-;ep&6^s0KHq2hOrcT<5X zj1A$44%@diE(%iW8lf@l%2%8iKpRCf_q4XYJ#+fh?YjE9hnShL-AmSZB^9xJTJ=tB zY(e83M; z|NlP&rGSZ213>y&&<%--iT%&d%Zn}y=70SBnPJ(I#f<2Z7={Q53NeU?2&1b5^O;## zv5GTd36n^yDiMOq5ljdR!;rL$%>P0H0_f_%{C6KdFwB}Uov?SfxVRX2`LKt{k6*vA z8jj%-peo9}lbw_IUq}##ciw+sm^x(=G2Y>1;K32aKYsqCqj#`cML>`$$sPbp&@p; zrQjG09|H-)VI|_8#Hkx_{qu`UMe-c7EOS$fe&6>XyAHlSKhWaFUD8X0R~4LXHb4pz zQg6RUwi3R#+r97m@DeG@66(4JN~y1wl^3~yei*FSlH($Xn_I;g148nszf0Lc(=_n*`jSR9h5+Xk&`8w?t(tL6 zcv%S{z!(KaSyV^NMHNB>W4x-t_ynd&MXSbO3^vrfcSILjRbzGY?*2iQvuYwoyp-^x zRrCDxm`FAH!Q-53)f5&%pCj59hLj_okYcBrxVHvjq~}ON)tG|AIL*t~g}-@&G1yRB qRpWLlq$4ioU;LwaeODsx`Thc{r8<6y`Y?T61)t)YnAsV8|fByfsXI|RS8N}@H zEAj$wW^V4^^M{%gdBVOuKQG_*>Q$CoiLJycRaI5Vii(O0R~vuE@2feeb*@9zM{2_= z7mlP8$r47Xr>6Xuk&)rh^;jcsUuW>-$rFKZeERzD6+WKpku?5LrLL@;tWn9XY1EMx cq{axCD0iRp00i_>zopr011UZDgXcg literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-list@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-list@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..7610a76d1c9113c830527a2ce5432674e95e407f GIT binary patch literal 325 zcmV-L0lNN)P);7ur{ZloEVcA0J2{hnI+6 ziBLD_|4q|KAq3#&EbKkpUDuI-Rw7xJK^TS*1ObN!?E4PewvjPbB4t^EB14XPI3kI0 z9LW$X5hPI*1;lYo4vLP;vatLGVUDz{h^8|M?~Qz|H`4dL#IihTRxD3l*KSm!q|24B z!kLviw~(4OO;d=X=sd!ObzN7Ors?9;G?R$rc@9aEnCKhJJkP8lXzh_BS9_C>azw)a zk7|Zt_(?TYRk=|O-aS_V3WHzjA~oyNw}vx&NJQSZhFVvbev~=lGT&t^Z|2BLM6b~o X`Nmbh^JXh?00000NkvXXu0mjfb;gLx literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..8081499c6e2d773dae1f19539384d22ef83bcf49 GIT binary patch literal 351 zcmV-l0igbgP)3M; z|NlP&rGSZ213>yQ3<(Gb4q*fWQVhp1gkOMfbwWbok>KE9Yhn#zL=Th7s;d9Iyu1u= z-@N_*>dou5-@ksXTC--&ZF~k|7}DI_@?SzioZ-ih9}J&9ePVd?=I!rSuU}37{O@1$ zre(|CU^5KEJ50<>46LlI3_LtM3}Rwp3dBPl==fc9mq_G*of-cC!$i%?J z#KZvdCMbM?7#NvOIXgM8(ACuj zg~?GMeh<=w4loS)4%Gbh`}f-hdU{K3Y-}z8HJk(Dhv*tn_#kH^<2CEnP5ATYPbg4K x7g-!18^r*qLQ5bGjx~H5k>xO=7{!@HasXSXz-`$MTPOek002ovPDHLkV1m`&noT@H9=&)Fi`XJw zJcx&FB8uV<90R8aPC@bD<~nEFG>OmCwNlFZrt4PN!56-H-z3kEPoDRE^K!Or!(nLr zVSpfp{~)z9!K}#O;GhtX$Gx>qt+;1`1cO08FNhy>ef;A5{5;ckzi`apojpA+$>WJ0 zk>#83S3kZ^BoZV0O<2#LV{PA5DivE;Tw3#ayJ+1q_sprG0HWIslH>(hmLVJtgA$2= zrm4K9DsyrmxH_4fzJ6|YR&FF2%f=DJ<>EmQ1!!w)!)o{-5D36g0#YK-p{bIlMy@Kj zpeH7hmuQ!%1 z0|;W&Ku~~EmC7&#L?SpkJ`KYFvseT!S9Gj)H4GaB3$hzXB|v5C(*bVb1uOJz;OszZ zT-TqX&1S`}k)92L5eNh?JAFAK0hp!<27Y-yU!W>%c6P=x34QFb+wC^XX6V>>V}g`c zNY&Jo04xiN2v9(P98!cr;fG`K^AB)q|G~5bv4(oqIKqx46XahoDt!sSghHVNVB^1K zNgo}#+ttouZq|7&=G{^x%Cs>-S>3;!UAqn3o{i^ z?@+5vXWhk-dHSxbtc0V{XdM5ddGw`)_zCrLmpJ==#00_UbeN5zsQgb*m92@xgKbVA vG<9wit%sI2Cy_@Ck00000NkvXXu0mjfcv*zV literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right.png new file mode 100644 index 0000000000000000000000000000000000000000..b44e89c697400e5d5ef83491d4f8ca44a1abbc63 GIT binary patch literal 345 zcmV-f0jB3M; z|NlP&rGSZ213>yQ49UpO4q*fWQVhp1M37%#byaoEk<8p&Yhn#zL=TfGQ>XnG;OAp_ z{pRie7cX9}{rLTB)r^S~Z{sr%!;rc2=Kq(Ll4SV#^C!cH4tHp4Ky!^FhIz{<+Xz|GCgAR!^nps1+Gs;sD#Cm_szF0-ge8k-?3=z^dy1sU}3 z-#;M!&+r!r1cin71O^7Ik(8ADgRUOL$1nsW`1|)ChOghgg6LC5hK4ItR8&A=aukT) zgXGZxh9O_Seq(t5@#Ae(WtF8yMh2IF8qNXnLv)QOd{FEl;~l&9OaSsjfmjz=93LCS r0H{JsAPtT+d>WDEFrpa6nM85`H{-~nSHQM)00000NkvXXu0mjf5r3Qc literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/tabset-right@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ac80fc0c706e249ef3be7536142de12d609a70b6 GIT binary patch literal 819 zcmV-31I+x1P)Pm}?x=^9GuvWY9g)aOFE-FMQDsByRD?%9*6y3NGMMR7n zq2fX-qA2)i6s)yq3@C*@W+s`*JaWf#n~ua_=Qc?TU3kcuJ2N?tk8{qwiNiEaSP$Y{ z4-k~$KS+HiY%4M{GU6H<8Vb}qt;Mq~$hU=sHqPbwax$Ghfl#)#t-{#`-x7~=KA$hO zF&w!e6-#f^>GXkC6E5fPu-m@bY_{veL@pl+wE-a}h@uFBC_Ywn?c7zKf4^MNX5QO^ zjE#+TJkN{u!5evIf=qp#&L(<%X`3J+N&x{);IeTSv^ffEK z^*s}0iQ*3kb+jr(VHUw~rSFJIpsZ@38#<_pzM^Wc7=+dVYw{Neb+)!WT}g%D>{QT( z`?+h!&O3vH`(B_)L&M9gh}9aJ+!C6c4z8x_P%f*eWib|Ozkht7|B1)rsVv!5ducf_ zRwhW*zgiIsK-Udyfl9Zs76=9(9X@pAVI&e#(3V2vQD0X5R(@rJEDmY40@banfvhMd z=XQ_p?cZ}Zl}gQFI{NQw2K5)M82zF*ZnM~dr+&}OXw<$uwYzXAA2IBujG^Edzi002ovPDHLkV1mbfa(Vy& literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list-hover.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list-hover.png new file mode 100644 index 0000000000000000000000000000000000000000..ab8b8c667d2392373d9dbe6825112ee3943a99b4 GIT binary patch literal 312 zcmV-80muG{P)MA4f&e}PASwuu7|}>TJ%;qa8vp>RQ+k}gFYd4a0000< KMNUMnLSTYbZ3238LcnJjb z#2X+LwQ(iVq6Bc_1RT3N2CEhlvEx=kmAuk=*Sj6i4k4Aao#f zAavmWaUh>Bu6g}6{lwg^;md(_S*}H6>=ztw7jWL!@C^zOFq-s1Lh2c=Q2HM~)jZxe&cg?2)$eWuhA|E?^ zw7AHmk_ouFGN9M*!|AWndaL!xhA5X`Hx?c))Y9oW;8+eWG!44lpT>AJd`Sp7x=Ywu zp9+9M&F07EN%!O(6<}xsjSBR7eb6qnIz%EV3if_}J`Kr40!-7SfM@2DrKPQE_00!I zWZLKjuuPLzx3@p%)E{a^5Ms>4m{6Mspz9+51|Tkq5RbGG8v_EbUl2bcsCC2lN1r|&U1NVUAID6DbV>A0Ks}v*&g}>9jXK#2s`~YAH z8-kE_uA_D;q98CV$J(V4qVF@u0`4}e15U2(#LY`!am)`2 zfVl`Up&cCv-2Mh2u&6uH*Jb2R_}B9gAao#f;5RQh-p=_6FG&CZ002ovPDHLkV1jAM B8AJd8 literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field-list.png new file mode 100644 index 0000000000000000000000000000000000000000..53ec75436ed4a05ff645b159326e95b16f02c587 GIT binary patch literal 298 zcmV+_0oDGAP)C>k;et!N{TwGk@Kn+hne*CCfSXlTSrWr(|7y#t+ zzj^bf`qZgY$)7)e7W(ny2LmT3Cj%cJ-z!T?%Q-;9+JI`lf;7Sax&a_@rFZY%Wt=^G zHu>-0zd|f5EU(PX&FAp)@=gWHJ%ee4(;zQk!5~Ar4jw#s6X=arpu7|oHDG<%G{eM{ wfSOA2X$BdJ%?ohX5yQZUMq)G}s~LC$0Au5-j12)plmGw#07*qoM6N<$fY5bbeae?VLy2T|}~4w4)_2Knq`~dz%~h(3YZF*3j7}ha=F~T)>l(c z&i5L+6et#pd6&y|i}8B_WnM0q8&0S5rc^2&DQkL0N$Y1+tyc36hofP&TG?u~V$0>S zP^nZ-KNqjnY7M*Hp0`*mY`tD{$~h)NXPkH=#+8jaX|J}*$% zPv9qhzn}ShK0#oySnxm~aGp-5bzOxPfjt5_Xt&#`e!ri=bwL4;n9XM3$8d6h6A?yN zz(b)>Gm%I5YdjndH)F9Fz`Nv{ z;MlLF0CHei@|{knlj`+)8EP~%n+Sl6BuP9Hi8Q0pr~-Z^f;t|0382(F2`GV%htahN z*^Pi>|0V>GFGzH|-PB+($k3bzgF!i5JTK@q8;{4uH-Y4rjh_eiHTAB4FRK=U-s$x8GYID#}ku)seq}#3n|so0;r&9 Q_W%F@07*qoM6N<$f(~l=qyPW_ literal 0 HcmV?d00001 diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field.png index d4e67d3587741699141936253350feeb884ac57a..b992a459b8fb1af1ba18bd292dce53a8b4b5316f 100644 GIT binary patch delta 237 zcmVTB!AFJL_t(Y4ege(4Z|Q1MFB-ZqF@?EVG5_s7-{1v7=?m$XdpoJ zEz*H>rm*bBXBpnV`*)V%IcK)wdQ=hT+^5{OlLU8b?XwAS&NpKW6GD(`rIeWWz83Hk zs&uV2lTwlj#TXABDrzE1spADzP~2*$<`o{O;5`TICPdwvUS;#M&3_(fv^!nFn@j+Ay>~>^_29O6io#3AEvW>aJVw5laSg bU&#Fe{{$O=FJ6|`00000NkvXXu0mjf?F40n diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field@2x.png b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/Soda Light/text-field@2x.png index 3b55fda7e630f85b3bb7899a28b547924b72585a..045b666591d4df6547f70a2ae39fd8a8a2137ead 100644 GIT binary patch delta 414 zcmV;P0b%~R1Goc_B!7QNL_t(&1?^hPje{^0wE0NT%N{6zKByu0Eo1Mgfj&|I659>G z^|NAc^t`8NSed71i5P5{%)A+!3-&k;BdHaV#E_OM2pY%nqb$q2yY9o-Mf%6KZMR_< zzKL&i=%QjsL;vJX=f4hFYrQjF*BN*2Cv*Ry^*>D$(Tu5zk$(&1>$)~hu_=mZUYN?N z-}l|Ps3n{dL2cWbAS$^#qX81LEK9;D3JNhJF-_B$s)|#|Lpcnr>*{F)-horXn&L)c zau-Abh%0YV5Le1?%=Mb{&U@v*QAKeMi4&xedn4ATKEgKzjohi_27rrmNCg2>0fpe3 zf^OT;@&drPLw~x407ZpDaEEjaLCXsO;|}Q>0u&VrA>JX~6rgzlfQgTsxLr=y5FqwY z2=Qx3BL~e30E|aY9Rd^;3c(%H_YkzaCxEF#ClnP5869${4|nwQJVXEBpTLN_Z?A%G zZshKEjrbA6^Pa3b_wfaZA@07}KfcEL3X>P8Bt;0<0F|OvHQN8D?Qnj$N-lD2@S*Wljr%vy>9XBB=)(kYu|O< z1bDqdCnW?4IKQRB}I08=JRlemAeScY&#xXWo=EcL1D7$T& zajh0$@`O;8x~>h93T_KJ05bDD2i@ceAyyz$Rh21e4Lb|pS7dSc{ zS4d|lx}=E{aD^^6L)vnQJ0?0EZ#h*cx}=E{s6vSgnmip>$ggepoBz`V+iCjyLq_rs zJ~AO-$NY3D^fZ7Mq}vzf9&uWCfIiwgIw!F=ijyit&lpeZ{{rU@J9lCcIg9`R002ov JPDHLkV1m*Hw|f8p diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/package-metadata.json index 171865a..55e09df 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/Theme - Soda/package-metadata.json @@ -1 +1 @@ -{"url": "http://buymeasoda.github.com/soda-theme/", "version": "2012.12.12.08.37.38", "description": "Dark and light custom UI themes for Sublime Text 2"} \ No newline at end of file +{"url": "http://buymeasoda.github.com/soda-theme/", "version": "2013.08.02.02.42.26", "description": "Dark and light custom UI themes for Sublime Text"} \ No newline at end of file diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Util/Default (OSX).sublime-keymap b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Util/Default (OSX).sublime-keymap new file mode 100644 index 0000000..2e3cd54 --- /dev/null +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Util/Default (OSX).sublime-keymap @@ -0,0 +1,31 @@ +[ + //AdvancedNewFile - https://github.com/xobb1t/Sublime-AdvancedNewFile + { "keys": ["ctrl+alt+n"], "command": "advanced_new_file"}, + //{ "keys": ["shift+ctrl+alt+n"], "command": "advanced_new_file", "args": {"is_python": true}}, + { + "keys": ["tab"], + "command": "insert_best_completion", + "args": {"default": "", "exact": false}, + "context": [ + { "key": "advanced_new_file_completion"}, + { "key": "setting.tab_completion", "operator": "equal", "operand": true }, + { "key": "last_command", "operator": "not_equal", "operand": "insert_best_completion" }, + { "key": "auto_complete_visible" , "operator": "equal", "operand": false} + ] + }, + + //Navigation History - https://github.com/timjrobinson/SublimeNavigationHistory + { "keys": ["alt+left"], "command": "navigation_history_back"}, + { "keys": ["alt+right"], "command": "navigation_history_forward"}, + + //SideBarEnhancements - https://github.com/titoBouzout/SideBarEnhancements/ + { "keys": ["super+t"], "command": "side_bar_new_file2" }, + { "keys": ["f12"], "command": "side_bar_open_in_browser" , "args":{"paths":[], "type":"testing" }}, + { "keys": ["alt+f12"], "command": "side_bar_open_in_browser", "args":{"paths":[], "type":"production" }}, + { "keys": ["f2"], "command": "side_bar_rename" }, + { "keys": ["ctrl+alt+f"], "command": "side_bar_find_files_path_containing" }, + { "keys": ["ctrl+shift+r"], "command": "side_bar_move" }, + + //SublimeFiles - https://github.com/al63/SublimeFiles + { "keys": ["shift+ctrl+alt+n"], "command": "sublime_files", "args": {"command":"navigate"}} +] diff --git a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Util/package-metadata.json b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Util/package-metadata.json index 9054c07..1f9554c 100644 --- a/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Util/package-metadata.json +++ b/EthanBrown.SublimeText2.UtilPackages/tools/PackageCache/ZZZ.EthanBrown.SublimeKeyMap.Util/package-metadata.json @@ -1 +1 @@ -{"url": "https://github.com/Iristyle/SublimeKeyMap.Util", "version": "2013.03.11.16.06.11", "description": "A simple repository used to host / share my customized Sublime Text 2 key bindings for Sublime utility plugins"} \ No newline at end of file +{"url": "https://github.com/Iristyle/SublimeKeyMap.Util", "version": "2013.09.17.01.16.26", "description": "A simple repository used to host / share my customized Sublime Text 2 key bindings for Sublime utility plugins"} \ No newline at end of file