├── .python-version ├── stringencode ├── __init__.py └── escape_table.py ├── package.json ├── Example.sublime-keymap ├── LICENSE ├── Default.sublime-commands ├── README.md ├── Main.sublime-menu └── string_encode.py /.python-version: -------------------------------------------------------------------------------- 1 | 3.8 2 | -------------------------------------------------------------------------------- /stringencode/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "StringEncode", 3 | "details": "https://github.com/colinta/SublimeStringEncode", 4 | "description": "Converts characters from one encoding to another using a transformation (think HTML entities, not character encodings).", 5 | "author": "Colin T.A. Gray (colinta)", 6 | "labels": [ 7 | "text manipulation" 8 | ], 9 | "releases": [ 10 | { 11 | "sublime_text": ">=3000", 12 | "tags": true 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /Example.sublime-keymap: -------------------------------------------------------------------------------- 1 | [ 2 | { "keys": ["super+shift+7"], "command": "xml_entitize", "scope": "text.xml" }, 3 | { "keys": ["super+ctrl+7"], "command": "xml_deentitize", "scope": "text.xml" }, 4 | { "keys": ["super+shift+7"], "command": "html_entitize" }, 5 | { "keys": ["super+ctrl+7"], "command": "html_deentitize" }, 6 | { "keys": ["super+shift+8"], "command": "json_escape" }, 7 | { "keys": ["super+ctrl+8"], "command": "json_unescape" }, 8 | { "keys": ["super+shift+6"], "command": "base64_encode" }, 9 | { "keys": ["super+ctrl+6"], "command": "base64_decode" }, 10 | { "keys": ["super+shift+5"], "command": "url_encode" }, 11 | // { "keys": ["super+shift+5"], "command": "url_encode", "args": {"old_school": true} }, 12 | { "keys": ["super+ctrl+5"], "command": "url_decode" }, 13 | { "keys": ["ctrl+shift+r"], "command": "escape_regex" }, 14 | { "keys": ["ctrl+shift+u"], "command": "unicode_escape" }, 15 | { "keys": ["super+ctrl+3"], "command": "hex_dec" } 16 | ] 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Colin T.A. Gray 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright notice, 10 | this list of conditions and the following disclaimer in the documentation 11 | and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 14 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 17 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 20 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 | 24 | The views and conclusions contained in the software and documentation are those 25 | of the authors and should not be interpreted as representing official policies, 26 | either expressed or implied, of this project. 27 | -------------------------------------------------------------------------------- /Default.sublime-commands: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "caption": "StringEncode: Paste Encoded...", 4 | "command": "string_encode_paste" 5 | }, 6 | { 7 | "caption": "StringEncode: HTML Entitize", 8 | "command": "html_entitize" 9 | }, 10 | { 11 | "caption": "StringEncode: HTML Deentitize", 12 | "command": "html_deentitize" 13 | }, 14 | { 15 | "caption": "StringEncode: CSS Escape", 16 | "command": "css_escape" 17 | }, 18 | { 19 | "caption": "StringEncode: CSS Unescape", 20 | "command": "css_unescape" 21 | }, 22 | { 23 | "caption": "StringEncode: XML Entitize", 24 | "command": "xml_entitize" 25 | }, 26 | { 27 | "caption": "StringEncode: XML Deentitize", 28 | "command": "xml_deentitize" 29 | }, 30 | { 31 | "caption": "StringEncode: Safe HTML Entitize", 32 | "command": "safe_html_entitize" 33 | }, 34 | { 35 | "caption": "StringEncode: Safe HTML Deentitize", 36 | "command": "safe_html_deentitize" 37 | }, 38 | { 39 | "caption": "StringEncode: JSON Escape", 40 | "command": "json_escape" 41 | }, 42 | { 43 | "caption": "StringEncode: JSON Unescape", 44 | "command": "json_unescape" 45 | }, 46 | { 47 | "caption": "StringEncode: URL Encode", 48 | "command": "url_encode" 49 | }, 50 | { 51 | "caption": "StringEncode: URL Decode", 52 | "command": "url_decode" 53 | }, 54 | { 55 | "caption": "StringEncode: MD5 Encode", 56 | "command": "md5_encode" 57 | }, 58 | { 59 | "caption": "StringEncode: Sha1 Encode", 60 | "command": "sha1_encode" 61 | }, 62 | { 63 | "caption": "StringEncode: Sha256 Encode", 64 | "command": "sha256_encode" 65 | }, 66 | { 67 | "caption": "StringEncode: Sha384 Encode", 68 | "command": "sha384_encode" 69 | }, 70 | { 71 | "caption": "StringEncode: Sha512 Encode", 72 | "command": "sha512_encode" 73 | }, 74 | { 75 | "caption": "StringEncode: Base16 Encode", 76 | "command": "base16_encode" 77 | }, 78 | { 79 | "caption": "StringEncode: Base16 Decode", 80 | "command": "base16_decode" 81 | }, 82 | { 83 | "caption": "StringEncode: Base32 Encode", 84 | "command": "base32_encode" 85 | }, 86 | { 87 | "caption": "StringEncode: Base32 Decode", 88 | "command": "base32_decode" 89 | }, 90 | { 91 | "caption": "StringEncode: Base64 Encode", 92 | "command": "base64_encode" 93 | }, 94 | { 95 | "caption": "StringEncode: Base64 Decode", 96 | "command": "base64_decode" 97 | }, 98 | { 99 | "caption": "StringEncode: Escape regex", 100 | "command": "escape_regex" 101 | }, 102 | { 103 | "caption": "StringEncode: Escape LIKE", 104 | "command": "escape_like" 105 | }, 106 | { 107 | "caption": "StringEncode: Decimal to Hexadecimal", 108 | "command": "dec_hex" 109 | }, 110 | { 111 | "caption": "StringEncode: Hexadecimal to Decimal", 112 | "command": "hex_dec" 113 | }, 114 | { 115 | "caption": "StringEncode: Unicode to Hexadecimal", 116 | "command": "unicode_hex" 117 | }, 118 | { 119 | "caption": "StringEncode: Decode Escaped Unicode", 120 | "command": "unicode_escape" 121 | }, 122 | { 123 | "caption": "StringEncode: Hexadecimal to Unicode", 124 | "command": "hex_unicode" 125 | }, 126 | { 127 | "caption": "StringEncode: GZip and Base64 Encode", 128 | "command": "gzip64_encode" 129 | }, 130 | { 131 | "caption": "StringEncode: GZip and Base64 Decode", 132 | "command": "gzip64_decode" 133 | } 134 | ] 135 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | StringEncode 2 | ============ 3 | 4 | Encodes text. "Encode" in this context refers to HTML entities or URL encoding, not character encodings. Most of these commands work in both directions (e.g. you can encode *to* html entities, or *from* html entities). 5 | 6 | - Html entities 7 | - Css (e.g. unicode characters) 8 | - Xml entities 9 | - Json strings 10 | - Urls 11 | - Base64 encoding 12 | - Hash: Md5, Sha256, Sha512 13 | - Regex escape 14 | - SQL 'LIKE' escape 15 | - Hexadecimal / Decimal 16 | - Unicode Hexadecimal representation 17 | 18 | This plugin was intended to be used with selections, but if you *don't* have any text selected, it will act on *the entire document*. This can be handy (if you're base64-encoding a file, for instance), but also have unintended consequences. For instance, you probably should not use `URL Decode` on an entire text document. 19 | 20 | You can also encode the clipboard, use the `string_encode_paste` command, and you will be presented with a menu to choose the encoding, and the clipboard will be encoded and inserted. 21 | 22 | Installation 23 | ------------ 24 | 25 | Using Package Control, install "StringEncode" or clone this repo in your packages folder. 26 | 27 | I recommended you add key bindings for the commands. I've included my preferred bindings below. 28 | Copy them to your key bindings file (⌘⇧,). 29 | 30 | Commands 31 | -------- 32 | 33 | This list continues to grow, see [Default.sublime-commands](https://github.com/colinta/SublimeStringEncode/blob/master/Default.sublime-commands#L1) for the entire list. 34 | 35 | `string_encode_paste`: Converts the clipboard to the desired encoding. 36 | 37 | `html_entitize`: Converts characters to their HTML entity 38 | 39 | `html_deentitize`: Converts HTML entities to a character 40 | 41 | `url_encode`: Uses urllib.quote to escape special URL characters. 42 | - Accepts an `old_school` argument (default: `True`). Setting it to `False` 43 | will return `%20` instead of `+` when encoding spaces. 44 | 45 | `url_decode`: Uses urllib.unquote to convert escaped URL characters 46 | 47 | `json_escape`: Escapes a string and surrounds it in quotes, according to the JSON encoding. 48 | 49 | `json_unescape`: Unescapes a string (include the quotes!) according to JSON encoding. 50 | 51 | `base64_encode` (also `base16`, `base32`): Uses base16/32/64 to encode into base64 52 | 53 | `base64_decode`: Decodes from base16/32/64 54 | 55 | `gzip64_encode`: Gzip and then base64 encode 56 | 57 | `gzip64_decode`: Base64 decode and then Gunzip 58 | 59 | `md5_encode`: Uses sha package to create md5 hash 60 | 61 | `sha256_encode` (also `sha1`, `sha384`, `sha512`): Uses sha package to create sha1/256/384/512 hash 62 | 63 | `escape_regex`: Escapes regex meta characters 64 | 65 | `escape_like`: Escapes SQL-LIKE meta characters 66 | 67 | `safe_html_entitize`: Converts characters to their HTML entity, but preserves HTML reserved characters 68 | 69 | `safe_html_deentitize`: Converts HTML entities to a character, but preserves HTML reserved characters 70 | 71 | `xml_entitize`: Converts characters to their XML entity 72 | 73 | `xml_deentitize`: Converts XML entities to a character 74 | 75 | Key Bindings 76 | ------------ 77 | 78 | Copy these to your user key bindings file. 79 | 80 | 81 | { "keys": ["super+shift+7"], "command": "xml_entitize", "scope": "text.xml" }, 82 | { "keys": ["super+ctrl+7"], "command": "xml_deentitize", "scope": "text.xml" }, 83 | { "keys": ["super+shift+7"], "command": "html_entitize" }, 84 | { "keys": ["super+ctrl+7"], "command": "html_deentitize" }, 85 | { "keys": ["super+shift+8"], "command": "json_escape" }, 86 | { "keys": ["super+ctrl+8"], "command": "json_unescape" }, 87 | { "keys": ["super+shift+6"], "command": "base64_encode" }, 88 | { "keys": ["super+ctrl+6"], "command": "base64_decode" }, 89 | { "keys": ["super+shift+5"], "command": "url_encode" }, 90 | // { "keys": ["super+shift+5"], "command": "url_encode", "args": {"old_school": true} }, 91 | { "keys": ["super+ctrl+5"], "command": "url_decode" }, 92 | { "keys": ["ctrl+shift+r"], "command": "escape_regex" }, 93 | { "keys": ["ctrl+shift+u"], "command": "unicode_escape" }, 94 | { "keys": ["super+ctrl+3"], "command": "hex_dec" }, 95 | 96 | -------------------------------------------------------------------------------- /Main.sublime-menu: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "edit", 4 | "children": [ 5 | { 6 | "caption": "-", 7 | "id": "stringencode" 8 | }, 9 | { 10 | "caption": "StringEncode", 11 | "children": [ 12 | { 13 | "caption": "Paste Encoded...", 14 | "command": "string_encode_paste" 15 | }, 16 | { 17 | "caption": "-" 18 | }, 19 | { 20 | "caption": "Decode Escaped Unicode", 21 | "command": "unicode_escape" 22 | }, 23 | { 24 | "caption": "HTML Entitize", 25 | "command": "html_entitize" 26 | }, 27 | { 28 | "caption": "HTML Deentitize", 29 | "command": "html_deentitize" 30 | }, 31 | { 32 | "caption": "CSS Escape", 33 | "command": "css_escape" 34 | }, 35 | { 36 | "caption": "CSS Unescape", 37 | "command": "css_unescape" 38 | }, 39 | { 40 | "caption": "XML Entitize", 41 | "command": "xml_entitize" 42 | }, 43 | { 44 | "caption": "XML Deentitize", 45 | "command": "xml_deentitize" 46 | }, 47 | { 48 | "caption": "Safe HTML Entitize", 49 | "command": "safe_html_entitize" 50 | }, 51 | { 52 | "caption": "Safe HTML Deentitize", 53 | "command": "safe_html_deentitize" 54 | }, 55 | { 56 | "caption": "-" 57 | }, 58 | { 59 | "caption": "URL Encode", 60 | "command": "url_encode" 61 | }, 62 | { 63 | "caption": "URL Decode", 64 | "command": "url_decode" 65 | }, 66 | { 67 | "caption": "Base16 Encode", 68 | "command": "base16_encode" 69 | }, 70 | { 71 | "caption": "Base16 Decode", 72 | "command": "base16_decode" 73 | }, 74 | { 75 | "caption": "Base32 Encode", 76 | "command": "base32_encode" 77 | }, 78 | { 79 | "caption": "Base32 Decode", 80 | "command": "base32_decode" 81 | }, 82 | { 83 | "caption": "Base64 Encode", 84 | "command": "base64_encode" 85 | }, 86 | { 87 | "caption": "Base64 Decode", 88 | "command": "base64_decode" 89 | }, 90 | { 91 | "caption": "MD5 Encode", 92 | "command": "md5_encode" 93 | }, 94 | { 95 | "caption": "Sha1 Encode", 96 | "command": "sha1_encode" 97 | }, 98 | { 99 | "caption": "Sha256 Encode", 100 | "command": "sha256_encode" 101 | }, 102 | { 103 | "caption": "Sha384 Encode", 104 | "command": "sha384_encode" 105 | }, 106 | { 107 | "caption": "Sha512 Encode", 108 | "command": "sha512_encode" 109 | }, 110 | { 111 | "caption": "-" 112 | }, 113 | { 114 | "caption": "JSON Escape", 115 | "command": "json_escape" 116 | }, 117 | { 118 | "caption": "JSON Unescape", 119 | "command": "json_unescape" 120 | }, 121 | { 122 | "caption": "Escape regex", 123 | "command": "escape_regex" 124 | }, 125 | { 126 | "caption": "Escape LIKE", 127 | "command": "escape_like" 128 | }, 129 | { 130 | "caption": "-" 131 | }, 132 | { 133 | "caption": "Decimal to Hexadecimal", 134 | "command": "dec_hex" 135 | }, 136 | { 137 | "caption": "Hexadecimal to Decimal", 138 | "command": "hex_dec" 139 | }, 140 | { 141 | "caption": "Unicode to Hexadecimal", 142 | "command": "unicode_hex" 143 | }, 144 | { 145 | "caption": "Hexadecimal to Unicode", 146 | "command": "hex_unicode" 147 | } 148 | ] 149 | } 150 | ] 151 | } 152 | ] 153 | -------------------------------------------------------------------------------- /stringencode/escape_table.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | 3 | html_escape_table = { 4 | u"\"": """, 5 | u"'": "'", 6 | u"<": "<", 7 | u">": ">", 8 | u"¡": "¡", 9 | u"¢": "¢", 10 | u"£": "£", 11 | u"¤": "¤", 12 | u"¥": "¥", 13 | u"¦": "¦", 14 | u"§": "§", 15 | u"¨": "¨", 16 | u"©": "©", 17 | u"ª": "ª", 18 | u"«": "«", 19 | u"¬": "¬", 20 | u"®": "®", 21 | u"¯": "¯", 22 | u"°": "°", 23 | u"±": "±", 24 | u"²": "²", 25 | u"³": "³", 26 | u"´": "´", 27 | u"µ": "µ", 28 | u"¶": "¶", 29 | u"·": "·", 30 | u"¸": "¸", 31 | u"¹": "¹", 32 | u"º": "º", 33 | u"»": "»", 34 | u"¼": "¼", 35 | u"½": "½", 36 | u"¾": "¾", 37 | u"¿": "¿", 38 | u"À": "À", 39 | u"Á": "Á", 40 | u"Â": "Â", 41 | u"Ã": "Ã", 42 | u"Ä": "Ä", 43 | u"Å": "Å", 44 | u"Æ": "Æ", 45 | u"Ç": "Ç", 46 | u"È": "È", 47 | u"É": "É", 48 | u"Ê": "Ê", 49 | u"Ë": "Ë", 50 | u"Ì": "Ì", 51 | u"Í": "Í", 52 | u"Î": "Î", 53 | u"Ï": "Ï", 54 | u"Ð": "Ð", 55 | u"Ñ": "Ñ", 56 | u"Ò": "Ò", 57 | u"Ó": "Ó", 58 | u"Ô": "Ô", 59 | u"Õ": "Õ", 60 | u"Ö": "Ö", 61 | u"×": "×", 62 | u"Ø": "Ø", 63 | u"Ù": "Ù", 64 | u"Ú": "Ú", 65 | u"Û": "Û", 66 | u"Ü": "Ü", 67 | u"Ý": "Ý", 68 | u"Þ": "Þ", 69 | u"ß": "ß", 70 | u"à": "à", 71 | u"á": "á", 72 | u"â": "â", 73 | u"ã": "ã", 74 | u"ä": "ä", 75 | u"å": "å", 76 | u"æ": "æ", 77 | u"ç": "ç", 78 | u"è": "è", 79 | u"é": "é", 80 | u"ê": "ê", 81 | u"ë": "ë", 82 | u"ì": "ì", 83 | u"í": "í", 84 | u"î": "î", 85 | u"ï": "ï", 86 | u"ð": "ð", 87 | u"ñ": "ñ", 88 | u"ò": "ò", 89 | u"ó": "ó", 90 | u"ô": "ô", 91 | u"õ": "õ", 92 | u"ö": "ö", 93 | u"÷": "÷", 94 | u"ø": "ø", 95 | u"ù": "ù", 96 | u"ú": "ú", 97 | u"û": "û", 98 | u"ü": "ü", 99 | u"ý": "ý", 100 | u"þ": "þ", 101 | u"ÿ": "ÿ", 102 | u"Œ": "Œ", 103 | u"œ": "œ", 104 | u"Š": "Š", 105 | u"š": "š", 106 | u"Ÿ": "Ÿ", 107 | u"ƒ": "ƒ", 108 | u"ˆ": "ˆ", 109 | u"˜": "˜", 110 | u"Α": "Α", 111 | u"Β": "Β", 112 | u"Γ": "Γ", 113 | u"Δ": "Δ", 114 | u"Ε": "Ε", 115 | u"Ζ": "Ζ", 116 | u"Η": "Η", 117 | u"Θ": "Θ", 118 | u"Ι": "Ι", 119 | u"Κ": "Κ", 120 | u"Λ": "Λ", 121 | u"Μ": "Μ", 122 | u"Ν": "Ν", 123 | u"Ξ": "Ξ", 124 | u"Ο": "Ο", 125 | u"Π": "Π", 126 | u"Ρ": "Ρ", 127 | u"Σ": "Σ", 128 | u"Τ": "Τ", 129 | u"Υ": "Υ", 130 | u"Φ": "Φ", 131 | u"Χ": "Χ", 132 | u"Ψ": "Ψ", 133 | u"Ω": "Ω", 134 | u"α": "α", 135 | u"β": "β", 136 | u"γ": "γ", 137 | u"δ": "δ", 138 | u"ε": "ε", 139 | u"ζ": "ζ", 140 | u"η": "η", 141 | u"θ": "θ", 142 | u"ι": "ι", 143 | u"κ": "κ", 144 | u"λ": "λ", 145 | u"μ": "μ", 146 | u"ν": "ν", 147 | u"ξ": "ξ", 148 | u"ο": "ο", 149 | u"π": "π", 150 | u"ρ": "ρ", 151 | u"ς": "ς", 152 | u"σ": "σ", 153 | u"τ": "τ", 154 | u"υ": "υ", 155 | u"φ": "φ", 156 | u"χ": "χ", 157 | u"ψ": "ψ", 158 | u"ω": "ω", 159 | u"ϑ": "ϑ", 160 | u"ϒ": "ϒ", 161 | u"ϖ": "ϖ", 162 | u"–": "–", 163 | u"—": "—", 164 | u"‘": "‘", 165 | u"’": "’", 166 | u"‚": "‚", 167 | u"“": "“", 168 | u"”": "”", 169 | u"„": "„", 170 | u"†": "†", 171 | u"‡": "‡", 172 | u"•": "•", 173 | u"…": "…", 174 | u"‰": "‰", 175 | u"′": "′", 176 | u"″": "″", 177 | u"‹": "‹", 178 | u"›": "›", 179 | u"‾": "‾", 180 | u"⁄": "⁄", 181 | u"€": "€", 182 | u"ℑ": "ℑ", 183 | u"℘": "℘", 184 | u"ℜ": "ℜ", 185 | u"™": "™", 186 | u"ℵ": "ℵ", 187 | u"←": "←", 188 | u"↑": "↑", 189 | u"→": "→", 190 | u"↓": "↓", 191 | u"↔": "↔", 192 | u"↵": "↵", 193 | u"⇐": "⇐", 194 | u"⇑": "⇑", 195 | u"⇒": "⇒", 196 | u"⇓": "⇓", 197 | u"⇔": "⇔", 198 | u"∀": "∀", 199 | u"∂": "∂", 200 | u"∃": "∃", 201 | u"∅": "∅", 202 | u"∇": "∇", 203 | u"∈": "∈", 204 | u"∉": "∉", 205 | u"∋": "∋", 206 | u"∏": "∏", 207 | u"∑": "∑", 208 | u"−": "−", 209 | u"∗": "∗", 210 | u"√": "√", 211 | u"∝": "∝", 212 | u"∞": "∞", 213 | u"∠": "∠", 214 | u"∧": "∧", 215 | u"∨": "∨", 216 | u"∩": "∩", 217 | u"∪": "∪", 218 | u"∫": "∫", 219 | u"∴": "∴", 220 | u"∼": "∼", 221 | u"≅": "≅", 222 | u"≈": "≈", 223 | u"≠": "≠", 224 | u"≡": "≡", 225 | u"≤": "≤", 226 | u"≥": "≥", 227 | u"⊂": "⊂", 228 | u"⊃": "⊃", 229 | u"⊄": "⊄", 230 | u"⊆": "⊆", 231 | u"⊇": "⊇", 232 | u"⊕": "⊕", 233 | u"⊗": "⊗", 234 | u"⊥": "⊥", 235 | u"⋅": "⋅", 236 | u"⌈": "⌈", 237 | u"⌉": "⌉", 238 | u"⌊": "⌊", 239 | u"⌋": "⌋", 240 | u"〈": "⟨", 241 | u"〉": "⟩", 242 | u"◊": "◊", 243 | u"♠": "♠", 244 | u"♣": "♣", 245 | u"♥": "♥", 246 | u"♦": "♦", 247 | u"\xa0": " ", 248 | } 249 | 250 | html5_escape_table = { 251 | u"'": "'", 252 | } 253 | 254 | xml_escape_table = { 255 | u"\"": """, 256 | u"'": "'", 257 | u"<": "<", 258 | u">": ">" 259 | } 260 | 261 | html_reserved_list = ( 262 | u"\"", 263 | u"'", 264 | u"<", 265 | u">", 266 | u"&" 267 | ) 268 | -------------------------------------------------------------------------------- /string_encode.py: -------------------------------------------------------------------------------- 1 | # coding: utf8 2 | import base64 3 | import codecs 4 | import gzip 5 | import hashlib 6 | import json 7 | import re 8 | import sublime 9 | import sublime_plugin 10 | import sys 11 | 12 | from .stringencode.escape_table import ( 13 | html_escape_table, 14 | html5_escape_table, 15 | html_reserved_list, 16 | xml_escape_table 17 | ) 18 | 19 | import urllib.parse 20 | quote_plus = urllib.parse.quote_plus 21 | unquote_plus = urllib.parse.unquote_plus 22 | 23 | __all__ = [ 24 | "StringEncodePaste", 25 | "Gzip64EncodeCommand", 26 | "Gzip64DecodeCommand", 27 | "UnicodeEscapeCommand", 28 | "HtmlEntitizeCommand", 29 | "HtmlDeentitizeCommand", 30 | "CssEscapeCommand", 31 | "CssUnescapeCommand", 32 | "SafeHtmlEntitizeCommand", 33 | "SafeHtmlDeentitizeCommand", 34 | "XmlEntitizeCommand", 35 | "XmlDeentitizeCommand", 36 | "JsonEscapeCommand", 37 | "JsonUnescapeCommand", 38 | "UrlEncodeCommand", 39 | "UrlDecodeCommand", 40 | "Base16EncodeCommand", 41 | "Base16DecodeCommand", 42 | "Base32EncodeCommand", 43 | "Base32DecodeCommand", 44 | "Base64EncodeCommand", 45 | "Base64DecodeCommand", 46 | "Md5EncodeCommand", 47 | "Sha1EncodeCommand", 48 | "Sha384EncodeCommand", 49 | "Sha256EncodeCommand", 50 | "Sha512EncodeCommand", 51 | "EscapeRegexCommand", 52 | "EscapeLikeCommand", 53 | "HexDecCommand", 54 | "DecHexCommand", 55 | "UnicodeHexCommand", 56 | "HexUnicodeCommand", 57 | ] 58 | 59 | def pad64(value): 60 | mod = len(value) % 4 61 | if mod == 3: 62 | return value + '=' 63 | elif mod == 2: 64 | return value + '==' 65 | return value 66 | 67 | 68 | class StringEncodePaste(sublime_plugin.WindowCommand): 69 | def run(self, **kwargs): 70 | items = [ 71 | ('Html Entitize', 'html_entitize'), 72 | ('Html Deentitize', 'html_deentitize'), 73 | ('Unicode Escape', 'unicode_escape'), 74 | ('Css Escape', 'css_escape'), 75 | ('Css Unescape', 'css_unescape'), 76 | ('Safe Html Entitize', 'safe_html_entitize'), 77 | ('Safe Html Deentitize', 'safe_html_deentitize'), 78 | ('Xml Entitize', 'xml_entitize'), 79 | ('Xml Deentitize', 'xml_deentitize'), 80 | ('Json Escape', 'json_escape'), 81 | ('Json Unescape', 'json_unescape'), 82 | ('Url Encode', 'url_encode'), 83 | ('Url Decode', 'url_decode'), 84 | ('Base16 Encode', 'base16_encode'), 85 | ('Base16 Decode', 'base16_decode'), 86 | ('Base32 Encode', 'base32_encode'), 87 | ('Base32 Decode', 'base32_decode'), 88 | ('Base64 Encode', 'base64_encode'), 89 | ('Base64 Decode', 'base64_decode'), 90 | ('Md5 Encode', 'md5_encode'), 91 | ('Sha1 Encode', 'sha1_encode'), 92 | ('Sha256 Encode', 'sha256_encode'), 93 | ('Sha384 Encode', 'sha384_encode'), 94 | ('Sha512 Encode', 'sha512_encode'), 95 | ('Escape Regex', 'escape_regex'), 96 | ('Escape Like', 'escape_like'), 97 | ('Hex Dec', 'hex_dec'), 98 | ('Dec Hex', 'dec_hex'), 99 | ('Unicode Hex', 'unicode_hex'), 100 | ('Hex Unicode', 'hex_unicode'), 101 | ('Gzip64 Encode', 'gzip64_encode'), 102 | ('Gzip64 Decode', 'gzip64_decode'), 103 | ] 104 | 105 | lines = list(map(lambda line: line[0], items)) 106 | commands = list(map(lambda line: line[1], items)) 107 | view = self.window.active_view() 108 | if not view: 109 | return 110 | 111 | def on_done(item): 112 | if item == -1: 113 | return 114 | view.run_command(commands[item], {'source': 'clipboard'}) 115 | 116 | self.window.show_quick_panel(lines, on_done) 117 | 118 | 119 | class StringEncode(sublime_plugin.TextCommand): 120 | def run(self, edit, **kwargs): 121 | regions = self.view.sel() 122 | 123 | if kwargs.get('source') == 'clipboard': 124 | del kwargs['source'] 125 | text = sublime.get_clipboard() 126 | replacement = self.encode(text, **kwargs) 127 | for region in regions: 128 | if region.empty(): 129 | self.view.insert(edit, region.begin(), replacement) 130 | else: 131 | self.view.replace(edit, region, replacement) 132 | return 133 | 134 | elif 'source' in kwargs: 135 | self.view.show_popup('Unsupported source {0!r}'.format(kwargs['source'])) 136 | return 137 | 138 | if any(map(lambda region: region.empty(), regions)): 139 | regions = [sublime.Region(0, self.view.size())] 140 | for region in regions: 141 | text = self.view.substr(region) 142 | replacement = self.encode(text, **kwargs) 143 | self.view.replace(edit, region, replacement) 144 | 145 | 146 | class Gzip64EncodeCommand(StringEncode): 147 | 148 | def encode(self, text): 149 | return str(base64.b64encode(gzip.compress(bytes(text, 'utf-8'))), 'ascii') 150 | 151 | 152 | class Gzip64DecodeCommand(StringEncode): 153 | 154 | def encode(self, text): 155 | return str(gzip.decompress(base64.b64decode(pad64(text))), 'utf-8') 156 | 157 | 158 | class UnicodeEscapeCommand(StringEncode): 159 | 160 | def encode(self, text): 161 | return codecs.decode(text, 'unicode-escape') 162 | 163 | 164 | class HtmlEntitizeCommand(StringEncode): 165 | 166 | def encode(self, text): 167 | text = text.replace('&', '&') 168 | for k in html_escape_table: 169 | v = html_escape_table[k] 170 | text = text.replace(k, v) 171 | ret = '' 172 | for i, c in enumerate(text): 173 | if ord(c) > 127: 174 | ret += hex(ord(c)).replace('0x', '&#x') + ';' 175 | else: 176 | ret += c 177 | return ret 178 | 179 | 180 | class HtmlDeentitizeCommand(StringEncode): 181 | 182 | def encode(self, text): 183 | for k in html_escape_table: 184 | v = html_escape_table[k] 185 | text = text.replace(v, k) 186 | for k in html5_escape_table: 187 | v = html5_escape_table[k] 188 | text = text.replace(v, k) 189 | while re.search(r'&#[xX][a-fA-F0-9]+;', text): 190 | match = re.search(r'&#[xX]([a-fA-F0-9]+);', text) 191 | text = text.replace( 192 | match.group(0), chr(int('0x' + match.group(1), 16))) 193 | while re.search(r'&#[0-9]+;', text): 194 | match = re.search(r'&#([0-9]+);', text) 195 | text = text.replace( 196 | match.group(0), chr(int(match.group(1), 10))) 197 | text = text.replace('&', '&') 198 | return text 199 | 200 | 201 | class CssEscapeCommand(StringEncode): 202 | 203 | def encode(self, text): 204 | ret = '' 205 | for i, c in enumerate(text): 206 | if ord(c) > 127: 207 | ret += hex(ord(c)).replace('0x', '\\') 208 | else: 209 | ret += c 210 | return ret 211 | 212 | 213 | class CssUnescapeCommand(StringEncode): 214 | 215 | def encode(self, text): 216 | while re.search(r'\\[a-fA-F0-9]+', text): 217 | match = re.search(r'\\([a-fA-F0-9]+)', text) 218 | text = text.replace( 219 | match.group(0), chr(int('0x' + match.group(1), 16))) 220 | return text 221 | 222 | 223 | class SafeHtmlEntitizeCommand(StringEncode): 224 | 225 | def encode(self, text): 226 | for k in html_escape_table: 227 | # skip HTML reserved characters 228 | if k in html_reserved_list: 229 | continue 230 | v = html_escape_table[k] 231 | text = text.replace(k, v) 232 | ret = '' 233 | for i, c in enumerate(text): 234 | if ord(c) > 127: 235 | ret += hex(ord(c)).replace('0x', '&#x') + ';' 236 | else: 237 | ret += c 238 | return ret 239 | 240 | 241 | class SafeHtmlDeentitizeCommand(StringEncode): 242 | 243 | def encode(self, text): 244 | for k in html_escape_table: 245 | # skip HTML reserved characters 246 | if k in html_reserved_list: 247 | continue 248 | v = html_escape_table[k] 249 | text = text.replace(v, k) 250 | while re.search(r'&#[xX][a-fA-F0-9]+;', text): 251 | match = re.search(r'&#[xX]([a-fA-F0-9]+);', text) 252 | text = text.replace( 253 | match.group(0), chr(int('0x' + match.group(1), 16))) 254 | while re.search(r'&#[0-9]+;', text): 255 | match = re.search(r'&#([0-9]+);', text) 256 | text = text.replace( 257 | match.group(0), chr(int(match.group(1), 10))) 258 | text = text.replace('&', '&') 259 | return text 260 | 261 | 262 | class XmlEntitizeCommand(StringEncode): 263 | 264 | def encode(self, text): 265 | text = text.replace('&', '&') 266 | for k in xml_escape_table: 267 | v = xml_escape_table[k] 268 | text = text.replace(k, v) 269 | ret = '' 270 | for i, c in enumerate(text): 271 | if ord(c) > 127: 272 | ret += hex(ord(c)).replace('0x', '&#x') + ';' 273 | else: 274 | ret += c 275 | return ret 276 | 277 | 278 | class XmlDeentitizeCommand(StringEncode): 279 | 280 | def encode(self, text): 281 | for k in xml_escape_table: 282 | v = xml_escape_table[k] 283 | text = text.replace(v, k) 284 | text = text.replace('&', '&') 285 | return text 286 | 287 | 288 | class JsonEscapeCommand(StringEncode): 289 | 290 | def encode(self, text): 291 | return json.dumps(text) 292 | 293 | 294 | class JsonUnescapeCommand(StringEncode): 295 | 296 | def encode(self, text): 297 | if text[:1] == "'" and text[-1:] == "'": 298 | return self.encode(text[1:-1]) 299 | if text[:1] != '"' and text[-1:] != '"': 300 | return self.encode('"' + text.replace('"', '\\"') + '"') 301 | return json.loads(text) 302 | 303 | 304 | class UrlEncodeCommand(StringEncode): 305 | 306 | def encode(self, text, old_school=True): 307 | quoted = quote_plus(text) 308 | if old_school: 309 | return quoted.replace("+", "%20") 310 | return quoted 311 | 312 | 313 | class UrlDecodeCommand(StringEncode): 314 | 315 | def encode(self, text): 316 | return unquote_plus(text) 317 | 318 | 319 | class Base16EncodeCommand(StringEncode): 320 | 321 | def encode(self, text): 322 | return str(base64.b16encode(bytes(text, 'utf-8')), 'ascii') 323 | 324 | 325 | class Base16DecodeCommand(StringEncode): 326 | 327 | def encode(self, text): 328 | return str(base64.b16decode(text), 'utf-8') 329 | 330 | 331 | class Base32EncodeCommand(StringEncode): 332 | 333 | def encode(self, text): 334 | return str(base64.b32encode(bytes(text, 'utf-8')), 'ascii') 335 | 336 | 337 | class Base32DecodeCommand(StringEncode): 338 | 339 | def encode(self, text): 340 | return str(base64.b32decode(text), 'utf-8') 341 | 342 | 343 | class Base64EncodeCommand(StringEncode): 344 | 345 | def encode(self, text): 346 | return str(base64.b64encode(bytes(text, 'utf-8')), 'ascii') 347 | 348 | 349 | class Base64DecodeCommand(StringEncode): 350 | 351 | def encode(self, text): 352 | return str(base64.b64decode(pad64(text)), 'utf-8') 353 | 354 | 355 | class Md5EncodeCommand(StringEncode): 356 | 357 | def encode(self, text): 358 | return hashlib.md5(bytes(text, 'utf-8')).hexdigest() 359 | 360 | 361 | class Sha1EncodeCommand(StringEncode): 362 | 363 | def encode(self, text): 364 | return hashlib.sha1(bytes(text, 'utf-8')).hexdigest() 365 | 366 | 367 | class Sha256EncodeCommand(StringEncode): 368 | 369 | def encode(self, text): 370 | return hashlib.sha256(bytes(text, 'utf-8')).hexdigest() 371 | 372 | 373 | class Sha384EncodeCommand(StringEncode): 374 | 375 | def encode(self, text): 376 | return hashlib.sha384(bytes(text, 'utf-8')).hexdigest() 377 | 378 | 379 | class Sha512EncodeCommand(StringEncode): 380 | 381 | def encode(self, text): 382 | return hashlib.sha512(bytes(text, 'utf-8')).hexdigest() 383 | 384 | 385 | class EscapeRegexCommand(StringEncode): 386 | regex = re.compile(r'(?= 0xd800 and tmp <= 0xdbff: 438 | char_index += 1 439 | else: 440 | hex_text += '\\u' + '{0:04x}'.format(tmp) 441 | char_index = 0 442 | elif char_index == 2: 443 | c3 = c 444 | char_index += 1 445 | elif char_index == 3: 446 | c4 = c 447 | if endian == 'little': 448 | c3, c4 = c4, c3 449 | tmp1 = ((c1 << 8) + c2) - 0xd800 450 | tmp2 = ((c3 << 8) + c4) - 0xdc00 451 | tmp = (tmp1 * 0x400) + tmp2 + 0x10000 452 | hex_text += '\\U' + '{0:08x}'.format(tmp) 453 | char_index = 0 454 | return hex_text 455 | 456 | 457 | class HexUnicodeCommand(StringEncode): 458 | 459 | def encode(self, text): 460 | uni_text = text 461 | 462 | endian = sys.byteorder 463 | 464 | r = re.compile(r'\\u([0-9a-fA-F]{2})([0-9a-fA-F]{2})') 465 | rr = r.search(uni_text) 466 | while rr: 467 | first_byte = int(rr.group(1), 16) 468 | 469 | if first_byte >= 0xd8 and first_byte <= 0xdf: 470 | # Surrogate pair 471 | pass 472 | else: 473 | if endian == 'little': 474 | b1 = int(rr.group(2), 16) 475 | b2 = int(rr.group(1), 16) 476 | else: 477 | b1 = int(rr.group(1), 16) 478 | b2 = int(rr.group(2), 16) 479 | 480 | ch = bytes([b1, b2]).decode('utf-16') 481 | 482 | uni_text = uni_text.replace(rr.group(0), ch) 483 | rr = r.search(uni_text, rr.start(0) + 1) 484 | 485 | # Surrogate pair (2 bytes + 2 bytes) 486 | r = re.compile( 487 | r'\\u([0-9a-fA-F]{2})([0-9a-fA-F]{2})\\u([0-9a-fA-F]{2})([0-9a-fA-F]{2})') 488 | rr = r.search(uni_text) 489 | while rr: 490 | if endian == 'little': 491 | b1 = int(rr.group(2), 16) 492 | b2 = int(rr.group(1), 16) 493 | b3 = int(rr.group(4), 16) 494 | b4 = int(rr.group(3), 16) 495 | else: 496 | b1 = int(rr.group(1), 16) 497 | b2 = int(rr.group(2), 16) 498 | b3 = int(rr.group(3), 16) 499 | b4 = int(rr.group(4), 16) 500 | 501 | ch = bytes([b1, b2, b3, b4]).decode('utf-16') 502 | 503 | uni_text = uni_text.replace(rr.group(0), ch) 504 | rr = r.search(uni_text) 505 | 506 | # Surrogate pair (4 bytes) 507 | r = re.compile( 508 | r'\\U([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})') 509 | rr = r.search(uni_text) 510 | while rr: 511 | tmp = (int(rr.group(1), 16) << 24) \ 512 | + (int(rr.group(2), 16) << 16) \ 513 | + (int(rr.group(3), 16) << 8) \ 514 | + (int(rr.group(4), 16)) 515 | 516 | if (tmp <= 0xffff): 517 | ch = chr(tmp) 518 | else: 519 | tmp -= 0x10000 520 | c1 = 0xd800 + int(tmp / 0x400) 521 | c2 = 0xdc00 + int(tmp % 0x400) 522 | if endian == 'little': 523 | b1 = c1 & 0xff 524 | b2 = c1 >> 8 525 | b3 = c2 & 0xff 526 | b4 = c2 >> 8 527 | else: 528 | b1 = c1 >> 8 529 | b2 = c1 & 0xff 530 | b3 = c2 >> 8 531 | b4 = c2 & 0xff 532 | 533 | ch = bytes([b1, b2, b3, b4]).decode('utf-16') 534 | 535 | uni_text = uni_text.replace(rr.group(0), ch) 536 | rr = r.search(uni_text) 537 | 538 | return uni_text 539 | --------------------------------------------------------------------------------