🐍🐍🐍
at dev 366 lines 11 kB view raw
1<script lang="ts"> 2 // @ts-nocheck 3 // typescript hates what we're doing here 4 5 import { onMount } from 'svelte'; 6 import Transmitter from '../../wiring/transmitter.svelte'; 7 8 Prism.languages['fictionscript'] = { 9 'fic-comment': /^\s*#.*/, 10 'message': { 11 pattern: /^\s*(.|\n)*/, 12 inside: { 13 'method-no-at': { 14 pattern: /<[^@]*>.*/, 15 inside: { 16 'fic-variable': { 17 pattern: /(<)[^\+-\?>]*/, 18 lookbehind: true, 19 }, 20 //'operator': /(\+\+|--|\+|-|\?|\?\?)\s*>/, 21 'fic-brackets': /(<|@|(|\+\+|--|\+|-|\?|\?\?)\s*>)/, 22 } 23 }, 24 'method-at': { 25 pattern: /<.*@.*>.*/, 26 inside: { 27 'fic-variable': { 28 pattern: /(@)[^\+-\?>@]*/, 29 lookbehind: true, 30 }, 31 'fic-method': { 32 pattern: /(<)[^@>]*(?<![@>])/, 33 lookbehind: true, 34 }, 35 //'operator': /(\+\+|--|\+|-|\?|\?\?)\s*>/, 36 'fic-brackets': /(<|@|(|\+\+|--|\+|-|\?|\?\?)\s*>)/, 37 } 38 }, 39 'set-literal': { 40 pattern: /(\.|arg|var|insert).*?:=.*/, 41 inside: { 42 'fic-string': { 43 pattern: /(\:=).*/, 44 lookbehind: true, 45 }, 46 'fic-variable': { 47 pattern: /(\.)[^.:]*/, 48 lookbehind: true, 49 greedy: true, 50 }, 51 'fic-accessors': /(\.|args?|var|insert|:=)/, 52 } 53 }, 54 'set': { 55 pattern: /(\.|arg|var|insert).*?=.*/, 56 inside: { 57 'expression': { 58 pattern: /(\=).*/, 59 lookbehind: true, 60 inside: null 61 }, 62 'fic-variable': { 63 pattern: /(\.)[^.=]*/, 64 lookbehind: true, 65 greedy: true, 66 }, 67 'fic-accessors': /(\.|arg|var|insert|=)/, 68 } 69 }, 70 'get': { 71 pattern: /(\.|args?|retrieve).*/, 72 inside: { 73 'fic-variable': { 74 pattern: /(\.)(\s|[^.(\?\s*$)])*/, 75 lookbehind: true, 76 greedy: true, 77 }, 78 'fic-accessors': /(\.|args?|retrieve|=|\?)/, 79 } 80 } 81 } 82 } 83 //'number': /(\.|arg|var|insert|retrieve)(?:\s*(.*?)\s*(\.))*?\s*(.*?)\s*(:=).*\n.*/, 84 }; 85 86 Prism.languages['fictionscript']['message'].inside['set'].inside['expression'].inside = Prism.languages['fictionscript']['message'].inside; 87 88 /** 89 * @param {string} text 90 */ 91 function update(text) { 92 // Handle final newlines (see article) 93 if(text[text.length-1] == "\n") { 94 text += " "; 95 } 96 // Update code 97 code_display_content_element.innerHTML = text.replace(new RegExp("&", "g"), "&amp;").replace(new RegExp("<", "g"), "&lt;"); /* Global RegExp */ 98 // Syntax Highlight 99 Prism.highlightElement(code_display_content_element); 100 } 101 102 /** 103 * @param {HTMLTextAreaElement} element 104 */ 105 function sync_scroll(element) { 106 /* Scroll result to scroll coords of event - sync with textarea */ 107 // Get and set x and y 108 code_display_element.scrollTop = element.scrollTop; 109 code_display_element.scrollLeft = element.scrollLeft; 110 } 111 112 /** 113 * @param {{ value: string; selectionStart: number; selectionEnd: number; }} element 114 * @param {{ key: string; preventDefault: () => void; }} event 115 */ 116 function on_key(element, event) { 117 let code = element.value; 118 if(event.key == "Tab") { 119 /* Tab key pressed */ 120 event.preventDefault(); // stop normal 121 let before_tab = code.slice(0, element.selectionStart); // text before tab 122 let after_tab = code.slice(element.selectionEnd, element.value.length); // text after tab 123 let cursor_pos = element.selectionStart + 1; // where cursor moves after tab - moving forward by 1 char to after tab 124 element.value = before_tab + "\t" + after_tab; // add tab char 125 // move cursor 126 element.selectionStart = cursor_pos; 127 element.selectionEnd = cursor_pos; 128 update(element.value); // Update text to include indent 129 } 130 131 if(!event.shiftKey && (event.key === "Enter" || event.keyCode === 13)) { 132 onSubmit(code); 133 //transmitter.send({ schema: "script", code: code, language: "fictionscript" }); 134 transmitter.send({ schema: "command", command: code }); 135 element.value = ""; 136 update(""); 137 event.preventDefault(); 138 } 139 } 140 141 /** 142 * @type {HTMLTextAreaElement} 143 */ 144 let code_input_element; 145 146 /** 147 * @type {HTMLPreElement} 148 */ 149 let code_display_element; 150 151 /** 152 * @type {HTMLCodeElement} 153 */ 154 let code_display_content_element; 155 156 let transmitter; 157 158 export let width = "100%"; 159 export let height = "100%"; 160 export let padding = "10"; 161 162 export let font_size = "1em"; 163 164 export let code = ""; 165 166 export let onSubmit = (value) => {}; 167 168 let code_style = `width: calc(100% - ${padding * 2}px); height: calc(100% - ${padding * 2}px); top: ${padding}px; left: ${padding}px; font-size: ${font_size} !important;`; 169 170 onMount(async () => { 171 update(code); 172 }); 173</script> 174 175<div class=wrapper 176 style="width: {width}; height: {height}; font-size: {font_size} !important;"> 177 <div class=backdrop></div> 178 <textarea 179 placeholder="..." 180 class=code-input 181 style={code_style} 182 spellcheck=false 183 bind:this={code_input_element} 184 bind:value={code} 185 on:input={() => {update(code_input_element.value); sync_scroll(code_input_element);}} 186 on:scroll={() => {sync_scroll(code_input_element);}} 187 on:keydown={(event) => {on_key(code_input_element, event);}} 188 /> 189 190 <pre class="code-display" style={code_style} aria-hidden=true bind:this={code_display_element}> 191 <code class="language-fictionscript code-display-content" 192 bind:this={code_display_content_element} /> 193 </pre> 194 195 <Transmitter schemas={["command"]} bind:this={transmitter} style="right: -1.5em;" /> 196</div> 197 198<svelte:head> 199 <link rel="stylesheet" href="css/prism.css" /> 200 <link rel="stylesheet" href="css/syntax.css" /> 201</svelte:head> 202 203<style> 204 /* adapted from https://css-tricks.com/creating-an-editable-textarea-that-supports-syntax-highlighted-code/ */ 205 206 .wrapper { 207 cursor: default; 208 position: relative; 209 display: flex; 210 } 211 212 .backdrop { 213 position: absolute; 214 top: 0; 215 left: 0; 216 margin: 0; 217 padding: 0; 218 border: 0; 219 width: 100%; 220 height: 100%; 221 background-color: var(--code-editor-background); 222 } 223 224 .code-input, .code-display { 225 /* Both elements need the same text and space styling so they are directly on top of each other */ 226 margin: 0; 227 padding: 0; 228 border: 0; 229 border-radius: 0; 230 } 231 232 pre { 233 box-shadow: none; 234 background-color: none !important; 235 } 236 237 code { 238 background-color: none !important; 239 } 240 241 .code-input:focus { 242 border: 0; 243 outline: 0; 244 } 245 246 ::-webkit-scrollbar { 247 width: 10px; 248 position: absolute; 249 right: 10px; 250 } 251 252 ::-webkit-scrollbar-track { 253 background: #344; 254 cursor: pointer !important; 255 } 256 257 ::-webkit-scrollbar-thumb { 258 background: #788; 259 cursor: pointer !important; 260 } 261 262 ::-webkit-scrollbar-track:hover, ::-webkit-scrollbar-thumb:hover { 263 cursor: pointer !important; 264 user-select: none; 265 } 266 267 .code-input, .code-display, .code-display * { 268 /* Also add text styles to highlighing tokens */ 269 font-family: var(--code-font); 270 line-height: 1.5em !important; 271 tab-size: 4; 272 } 273 274 .code-input, .code-display { 275 /* In the same place */ 276 position: absolute; 277 } 278 279 /* Move the textarea in front of the result */ 280 281 .code-input { 282 z-index: 2; 283 } 284 .code-display { 285 z-index: 1; 286 } 287 .backdrop { 288 z-index: 0; 289 } 290 291 292 /* Make textarea almost completely transparent */ 293 294 .code-input { 295 color: transparent; 296 background: transparent; 297 caret-color: white; 298 299 /* Can be scrolled */ 300 overflow: auto; 301 white-space: pre-wrap; 302 word-wrap: break-word; 303 304 /* Fix cursor of scrollbar */ 305 cursor: auto; 306 } 307 308 .code-display { 309 overflow: auto; 310 white-space: normal; 311 word-wrap: break-word; 312 user-select: none; 313 } 314 315 /* No resize on textarea */ 316 .code-input { 317 resize: none; 318 } 319 320 /* Paragraphs; First Image */ 321 * { 322 font-family: var(--code-font); 323 } 324 325 /* Syntax Highlighting from prism.js starts below, partly modified: */ 326 327 /* PrismJS 1.23.0 328 https://prismjs.com/download.html#themes=prism-funky&languages=markup */ 329 /** 330 * prism.js Funky theme 331 * Based on “Polyfilling the gaps” talk slides http://lea.verou.me/polyfilling-the-gaps/ 332 * @author Lea Verou 333 */ 334 335 code[class*="language-"] { 336 font-family: var(--code-font); 337 text-align: left; 338 white-space: pre-wrap; 339 word-spacing: normal; 340 word-break: normal; 341 word-wrap: break-word; 342 line-height: 1.5; 343 344 border: 0; 345 346 -moz-tab-size: 4; 347 -o-tab-size: 4; 348 tab-size: 4; 349 350 -webkit-hyphens: none; 351 -moz-hyphens: none; 352 -ms-hyphens: none; 353 hyphens: none; 354 } 355 356 /* Inline code */ 357 :not(pre) > code[class*="language-"] { 358 padding: .2em; 359 border-radius: .3em; 360 white-space: normal; 361 } 362 363 pre[class*="code-display"] { 364 background-color: transparent; 365 } 366</style>