diff --git a/README.md b/README.md index f5c255c..20892ee 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@
- + @@ -45,21 +45,47 @@ - Поддержка Tab и Shift+Tab - Поддержка undo/redo. Хоть и за счёт использования execCommand, который считается deprecated. Однако аналогов пока нет (подробнее). А свою микросистему с памятью через самодельный стек сомнительно внедрять... - Поддержка resize (убери resize:none из css) -- Богатая система типов: +- Богатая система классификации: | Класс | Что это | Пример(sql) | |------------------|---------------------|-------------| | `.comment` | Комментарии | --abc | | `.string` | Строки | "abc" | | `.keyword` | Ключевые слова | SELECT | -| `.variable` | Переменные | @age | -| `.punctuation` | Пунктуация | () | +| `.punctuation` | Пунктуация | ; | | `.number` | Числа | 123 | | `.operator` | Оператор | BETWEEN | | `.boolean` | Булевое значение | FALSE | | `.function` | Функции | COUNT( | + +❗️ Но есть и множество других классов, например + +```css +.url, .property, .selector, .rule {/* в css, например */} + +.class-name, .regex {/* js. у regex кста есть и вложенные классы :) */} + +.attr-value, .attr-name {/* svg */} +``` + +> [!IMPORTANT] +> js подключается как clike+js +> ```html +> +> +> ``` +> +> об этом можно легко понять глянув на первые символы `prism-javascript.min.js`: +> ```text +> Prism.languages.javascript=Prism.languages.extend("clike"...) +> ``` + +--- + +так как sql модуль prism это не про postgresql, то создал свой модуль `pgSQL.js` с регулярками через chatgpt, ошибки вроде выправил, но не факт что всё 100% идеально. + --- ###### js uglified via https://www.uglifyjs.net/ -###### css minified via https://www.uglifycss.com/ +###### css minified via https://www.uglifycss.com/ \ No newline at end of file diff --git a/img/size.png b/img/size.png index 7ad1079..35e0a29 100644 Binary files a/img/size.png and b/img/size.png differ diff --git a/index.html b/index.html index 4af2a21..610642c 100644 --- a/index.html +++ b/index.html @@ -1,16 +1,67 @@ - - + + +
- +
- + + + + \ No newline at end of file diff --git a/pgSQL.js b/pgSQL.js new file mode 100644 index 0000000..61ba3e6 --- /dev/null +++ b/pgSQL.js @@ -0,0 +1,9 @@ +Prism.languages.sql = { + comment: /(^|[^\\])(\/\*[\w\W]*?\*\/|((--)|#).*?(\r?\n|$))/, + string: /("|')(\\?[\s\S])*?\1/, + function: /\b(?:COUNT|SUM|AVG|MIN|MAX|NOW|COALESCE|GREATEST|LEAST|LENGTH|SUBSTRING|UPPER|LOWER|TO_CHAR|TO_DATE|AGE|EXTRACT|ROUND|TRIM|RANK|DENSE_RANK|ROW_NUMBER|ARRAY_AGG|STRING_AGG|DATE_PART|DATE_TRUNC|RANDOM|GENERATE_SERIES|SETVAL|NEXTVAL|CURRVAL|ARRAY_LENGTH)(?=\s*\()/i, + boolean: /\b(?:TRUE|FALSE|NULL)\b/i, + keyword: /\b(?:ABORT|ABS|ACTION|ADD|AFTER|AGGREGATE|ALTER|ANALYZE|ARRAY|AS|ASC|ASSERT|ASSIGN|AT|BEGIN|BETWEEN|BIGINT|BOOLEAN|BY|CASE|CAST|CHECK|COLLATE|COLUMN|COMMENT|COMMIT|CONFLICT|CONSTRAINT|CREATE|CROSS|CURRENT|CURRENT_CATALOG|CURRENT_DATE|CURRENT_ROLE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|DECLARE|DEFAULT|DELETE|DESC|DISTINCT|DO|DROP|ELSE|END|EXCEPT|EXISTS|EXPLAIN|FETCH|FILTER|FOR|FOREIGN|FROM|FULL|GRANT|GROUP|HAVING|INNER|INSERT|INT|INTO|JOIN|LEFT|LIMIT|LOCK|LOOP|NATURAL|OFFSET|ON|ORDER|OUTER|OVER|PERFORM|PLPGSQL|PRIMARY|RAISE|REFERENCES|RETURN|RETURNING|REVOKE|RIGHT|ROLLBACK|ROW|ROWS|SELECT|SERIAL|SET|SHOW|SIMILAR|SOME|TABLE|THEN|TO|TRANSACTION|TRUNCATE|UNION|UNIQUE|UNNEST|UPDATE|USING|VALUES|VARIADIC|VIEW|WHEN|WHERE|WITH|WORK|WINDOW|LANGUAGE|VOLATILE|STABLE|IMMUTABLE|STRICT|SECURITY|DEFINER|INVOKER|EXECUTE|USAGE|SMALLINT|DECIMAL|NUMERIC|REAL|DOUBLE PRECISION|VARCHAR|TEXT|DATE|TIME|TIMESTAMP|INTERVAL|UUID|JSON|XML)\b/i, number: /\b-?(0x)?\d*\.?[\da-f]+\b/, + operator: /:=|::|\|\||->>|->|@>|<@|&&|\?|:=|->>|->|\+|-|\*|\/|%|~|!=|<>|<=|>=|<|>|\^|&|\||!|\b(?:IS DISTINCT FROM|IS NOT DISTINCT FROM|LIKE|ILIKE|NOT|SIMILAR TO|IS|IN|ANY|ALL|AND|OR|BETWEEN|OVERLAPS|UNIQUE)\b/i, + punctuation: /[;[\]()`,.]/ +}; diff --git a/pgSQL.min.js b/pgSQL.min.js new file mode 100644 index 0000000..34cc898 --- /dev/null +++ b/pgSQL.min.js @@ -0,0 +1 @@ +Prism.languages.sql={comment:/(^|[^\\])(\/\*[\w\W]*?\*\/|((--)|#).*?(\r?\n|$))/,string:/("|')(\\?[\s\S])*?\1/,"function":/\b(?:COUNT|SUM|AVG|MIN|MAX|NOW|COALESCE|GREATEST|LEAST|LENGTH|SUBSTRING|UPPER|LOWER|TO_CHAR|TO_DATE|AGE|EXTRACT|ROUND|TRIM|RANK|DENSE_RANK|ROW_NUMBER|ARRAY_AGG|STRING_AGG|DATE_PART|DATE_TRUNC|RANDOM|GENERATE_SERIES|SETVAL|NEXTVAL|CURRVAL|ARRAY_LENGTH)(?=\s*\()/i,"boolean":/\b(?:TRUE|FALSE|NULL)\b/i,keyword:/\b(?:ABORT|ABS|ACTION|ADD|AFTER|AGGREGATE|ALTER|ANALYZE|ARRAY|AS|ASC|ASSERT|ASSIGN|AT|BEGIN|BETWEEN|BIGINT|BOOLEAN|BY|CASE|CAST|CHECK|COLLATE|COLUMN|COMMENT|COMMIT|CONFLICT|CONSTRAINT|CREATE|CROSS|CURRENT|CURRENT_CATALOG|CURRENT_DATE|CURRENT_ROLE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|DECLARE|DEFAULT|DELETE|DESC|DISTINCT|DO|DROP|ELSE|END|EXCEPT|EXISTS|EXPLAIN|FETCH|FILTER|FOR|FOREIGN|FROM|FULL|GRANT|GROUP|HAVING|INNER|INSERT|INT|INTO|JOIN|LEFT|LIMIT|LOCK|LOOP|NATURAL|OFFSET|ON|ORDER|OUTER|OVER|PERFORM|PLPGSQL|PRIMARY|RAISE|REFERENCES|RETURN|RETURNING|REVOKE|RIGHT|ROLLBACK|ROW|ROWS|SELECT|SERIAL|SET|SHOW|SIMILAR|SOME|TABLE|THEN|TO|TRANSACTION|TRUNCATE|UNION|UNIQUE|UNNEST|UPDATE|USING|VALUES|VARIADIC|VIEW|WHEN|WHERE|WITH|WORK|WINDOW|LANGUAGE|VOLATILE|STABLE|IMMUTABLE|STRICT|SECURITY|DEFINER|INVOKER|EXECUTE|USAGE|SMALLINT|DECIMAL|NUMERIC|REAL|DOUBLE PRECISION|VARCHAR|TEXT|DATE|TIME|TIMESTAMP|INTERVAL|UUID|JSON|XML)\b/i,number:/\b-?(0x)?\d*\.?[\da-f]+\b/,operator:/:=|::|\|\||->>|->|@>|<@|&&|\?|:=|->>|->|\+|-|\*|\/|%|~|!=|<>|<=|>=|<|>|\^|&|\||!|\b(?:IS DISTINCT FROM|IS NOT DISTINCT FROM|LIKE|ILIKE|NOT|SIMILAR TO|IS|IN|ANY|ALL|AND|OR|BETWEEN|OVERLAPS|UNIQUE)\b/i,punctuation:/[;[\]()`,.]/}; \ No newline at end of file diff --git a/script.js b/script.js index 444dca2..2638e29 100644 --- a/script.js +++ b/script.js @@ -1,5 +1,9 @@ +/* +подсветка sql кода самодельная +*/ + document.addEventListener('DOMContentLoaded', () => { - const textarea = document.getElementById('textarea'); + const textarea = document.getElementById('codeInput'); const container = document.getElementById('container'); const mirror = document.createElement('pre'); @@ -9,7 +13,7 @@ document.addEventListener('DOMContentLoaded', () => { // Функция копирования стилей const copyStyles = () => { const styles = window.getComputedStyle(textarea); - ['border', 'fontFamily', 'fontSize', 'fontWeight', 'lineHeight', 'padding', 'margin', 'whiteSpace', 'wordWrap'] + ['border', 'fontFamily', 'fontSize', 'fontWeight', 'lineHeight', 'padding', 'borderRadius', 'wordWrap', 'whiteSpace'] .forEach((prop) => mirror.style[prop] = styles[prop]); mirror.style.borderColor = 'transparent'; }; @@ -122,8 +126,11 @@ document.addEventListener('DOMContentLoaded', () => { // Подписка на события textarea.addEventListener('input', syncMirror); - textarea.addEventListener('scroll', () => mirror.scrollTop = textarea.scrollTop); - + textarea.addEventListener('scroll', () => { + mirror.scrollTop = textarea.scrollTop; // Синхронизация вертикальной прокрутки + mirror.scrollLeft = textarea.scrollLeft; // Синхронизация горизонтальной прокрутки + syncMirror(); // Обновление подсветки + }); // Инициализация diff --git a/script.min.js b/script.min.js index 73b8a2c..a3585c9 100644 --- a/script.min.js +++ b/script.min.js @@ -1 +1 @@ -document.addEventListener("DOMContentLoaded",()=>{const p=document.getElementById("textarea");const e=document.getElementById("container");const n=document.createElement("pre");n.classList.add("mirror");e.appendChild(n);const t=()=>{const t=window.getComputedStyle(p);["border","fontFamily","fontSize","fontWeight","lineHeight","padding","margin","whiteSpace","wordWrap"].forEach(e=>n.style[e]=t[e]);n.style.borderColor="transparent"};const u=()=>{n.innerHTML=Prism.highlight(p.value+"\n",Prism.languages.sql,"sql");n.scrollTop=p.scrollTop};const h=e=>{document.execCommand("insertText",false,e)};const l=()=>{const e=p.selectionStart;const t=p.selectionEnd;const n=p.value;const s=n.substring(0,e);console.log("beforeCursor:",s);const o=n.substring(t);const c=s.lastIndexOf("\n")+1;const l=o.indexOf("\n")!==-1?t+o.indexOf("\n"):n.length;const i=n.substring(c,l).split("\n");const r=i.every(e=>e.trim().startsWith("--"));let a="";let d=0;if(r){a=i.map(e=>e.replace(/^--\s?/,"")).join("\n");d=-3}else{a=i.map(e=>`-- ${e}`).join("\n");d=3}const g=i.length;p.setSelectionRange(c,l);h(a);const f=e+d;const m=t+d*g;p.setSelectionRange(f,m);u()};p.addEventListener("keydown",e=>{if((e.ctrlKey||e.metaKey)&&e.key==="/"){e.preventDefault();l()}if(e.key==="Tab"&&!e.shiftKey){e.preventDefault();const t=p.selectionStart;const n=p.selectionEnd;h(" ");p.setSelectionRange(t+4,t+4)}if(e.key==="Tab"&&e.shiftKey){e.preventDefault();const t=p.selectionStart;const s=p.value;const o=s.lastIndexOf("\n",t-1)+1;const c=s.substring(o,t);if(c.endsWith(" ")){p.setSelectionRange(o+c.length-4,t);h("");p.setSelectionRange(t-4,t-4)}}});p.addEventListener("input",u);p.addEventListener("scroll",()=>n.scrollTop=p.scrollTop);t();u()}); \ No newline at end of file +document.addEventListener("DOMContentLoaded",()=>{const u=document.getElementById("codeInput");const e=document.getElementById("container");const n=document.createElement("pre");n.classList.add("mirror");e.appendChild(n);const t=()=>{const t=window.getComputedStyle(u);["border","fontFamily","fontSize","fontWeight","lineHeight","padding","borderRadius","wordWrap","whiteSpace"].forEach(e=>n.style[e]=t[e]);n.style.borderColor="transparent"};const m=()=>{n.innerHTML=Prism.highlight(u.value+"\n",Prism.languages.sql,"sql");n.scrollTop=u.scrollTop};const h=e=>{document.execCommand("insertText",false,e)};const c=()=>{const e=u.selectionStart;const t=u.selectionEnd;const n=u.value;const s=n.substring(0,e);console.log("beforeCursor:",s);const o=n.substring(t);const l=s.lastIndexOf("\n")+1;const c=o.indexOf("\n")!==-1?t+o.indexOf("\n"):n.length;const i=n.substring(l,c).split("\n");const r=i.every(e=>e.trim().startsWith("--"));let a="";let d=0;if(r){a=i.map(e=>e.replace(/^--\s?/,"")).join("\n");d=-3}else{a=i.map(e=>`-- ${e}`).join("\n");d=3}const g=i.length;u.setSelectionRange(l,c);h(a);const f=e+d;const p=t+d*g;u.setSelectionRange(f,p);m()};u.addEventListener("keydown",e=>{if((e.ctrlKey||e.metaKey)&&e.key==="/"){e.preventDefault();c()}if(e.key==="Tab"&&!e.shiftKey){e.preventDefault();const t=u.selectionStart;const n=u.selectionEnd;h(" ");u.setSelectionRange(t+4,t+4)}if(e.key==="Tab"&&e.shiftKey){e.preventDefault();const t=u.selectionStart;const s=u.value;const o=s.lastIndexOf("\n",t-1)+1;const l=s.substring(o,t);if(l.endsWith(" ")){u.setSelectionRange(o+l.length-4,t);h("");u.setSelectionRange(t-4,t-4)}}});u.addEventListener("input",m);u.addEventListener("scroll",()=>{n.scrollTop=u.scrollTop;n.scrollLeft=u.scrollLeft;m()});t();m()}); \ No newline at end of file diff --git a/style.css b/style.css index 5769bc3..590cf4a 100644 --- a/style.css +++ b/style.css @@ -4,25 +4,44 @@ .container { position: relative; - width: fit-content; + width: 600px; + height: 200px; } .code { resize: none; - width: 500px; - height: 150px; + width: 100%; + height: 100%; margin: 0; padding: 15px; line-height: 1.5rem; - font-size: 1.25rem; - white-space: pre-wrap; + font-size: 1.1rem; + white-space: pre; /* Запрещаем перенос строк */ + word-wrap: normal; /* Предотвращаем автоматический перенос слов */ - background: transparent; - position: relative; + /* Отключаем эластичную прокрутку (оттягивание после максимальной прокрутки) */ + overscroll-behavior: contain; + /* Отключаем плавную прокрутку. хотя вроде не влияет, но пусть будет */ + scroll-behavior:auto; + /* на firefox (на macos) прокрутка от краёв никак не синхронизируется и чисто визуально расходится */ + /* так что решение №1) color: transparent; caret-color: xxx. Минус: цвет каретки под dark-reader и light/dark темы фиг подгонишь */ + /* color: transparent; */ + /* caret-color: #ffcc00; */ + + /* решение №2) говорим firefox что заполнение текст не будет. минусов не будет. */ + -webkit-text-fill-color: transparent; + + + outline: none; + border: 1px solid #80808040; + + &:focus { + box-shadow: 0 0 5px #80808080; /* Добавляем тень */ + } } -/* невимая часть, которая всё подсвечивает находясь над textarea */ +/* невидимая часть, которая всё подсвечивает находясь над textarea */ .mirror { position: absolute; top: 0; @@ -38,20 +57,22 @@ } & .string { - color: green; + color: #7ac44d; } & .keyword { - color:dodgerblue; + color: #478cd5; } & .function { - color:sandybrown; + color: #dd9672; } - & .variable {} - & .punctuation {} - & .number {} - & .operator {} - & .boolean {} + & .operator { + color:#BC89BD; + } + + & .boolean { + color: #e2c85d; + } } \ No newline at end of file diff --git a/style.min.css b/style.min.css index 8fed759..00cdd3b 100644 --- a/style.min.css +++ b/style.min.css @@ -1 +1 @@ -*{box-sizing:border-box}.container{position:relative;width:fit-content}.code{resize:none;width:500px;height:150px;margin:0;padding:15px;line-height:1.5rem;font-size:1.25rem;white-space:pre-wrap;background:transparent;position:relative}.mirror{position:absolute;top:0;left:0;height:100%;width:100%;margin:0;overflow:hidden;pointer-events:none;& .comment{color:grey}& .string{color:green}& .keyword{color:dodgerblue}& .function{color:sandybrown}} \ No newline at end of file +*{box-sizing:border-box}.container{position:relative;width:600px;height:200px}.code{resize:none;width:100%;height:100%;margin:0;padding:15px;line-height:1.5rem;font-size:1.1rem;white-space:pre;word-wrap:normal;overscroll-behavior:contain;scroll-behavior:auto;-webkit-text-fill-color:#fff0;outline:none;border:1px solid #80808040;&:focus{box-shadow:0 0 5px #80808080}}.mirror{position:absolute;top:0;left:0;height:100%;width:100%;margin:0;overflow:hidden;pointer-events:none;& .comment{color:grey}& .string{color:#7ac44d}& .keyword{color:#478cd5}& .function{color:#dd9672}& .operator{color:#BC89BD}& .boolean{color:#e2c85d}} \ No newline at end of file