From fef375a5942402591671f1b6c3052a407b9802af Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Wed, 30 Sep 2020 10:01:10 -0400 Subject: [PATCH] Minor improvements to syntax highlight of static filters Double-cliking on a URL will cause the whole URL to be selected, thus making it easier to navigate to this URL (through your browser "Open in new tab" entry in contextual menu). Unrecognized scriptlet names will be highlighted so as to warn that the filter is not going to be effective. --- src/css/codemirror.css | 12 +++- src/css/themes/default.css | 4 ++ src/js/codemirror/ubo-static-filtering.js | 82 ++++++++++++++++++++--- 3 files changed, 88 insertions(+), 10 deletions(-) diff --git a/src/css/codemirror.css b/src/css/codemirror.css index f784f6069..7e95eb5ce 100644 --- a/src/css/codemirror.css +++ b/src/css/codemirror.css @@ -24,7 +24,7 @@ /* CodeMirror theme overrides */ .cm-s-default .cm-value { color: #930; } -.cm-s-default .cm-comment { color: #777; } +.cm-s-default .cm-comment { color: var(--sf-comment-ink); } .cm-s-default .cm-keyword { color: #90b; } .cm-s-default .cm-regex { text-underline-position: under; @@ -41,6 +41,16 @@ text-decoration: underline red; text-underline-position: under; } +.cm-s-default .cm-warning { + text-decoration: underline var(--sf-warning-ink); + text-underline-position: under; + } +.cm-s-default .cm-link { + text-decoration: none; + } +.cm-s-default .cm-link:hover { + color: var(--link-ink); + } .cm-directive { color: #333; font-weight: bold; } .cm-staticext { color: #008; } diff --git a/src/css/themes/default.css b/src/css/themes/default.css index de3f8f644..9f271e076 100644 --- a/src/css/themes/default.css +++ b/src/css/themes/default.css @@ -159,6 +159,10 @@ --bg-popup-cell-block-own: hsla(0, 100%, 40%, 1); --bg-popup-cell-label-mixed: hsla(45, 100%, 38%, 1); --popup-icon-x-ink: var(--red-60); + + /* syntax highlight: static filtering */ + --sf-comment-ink: var(--light-gray-90); + --sf-warning-ink: var(--yellow-50); } /** diff --git a/src/js/codemirror/ubo-static-filtering.js b/src/js/codemirror/ubo-static-filtering.js index 24d257d60..7f6299dfe 100644 --- a/src/js/codemirror/ubo-static-filtering.js +++ b/src/js/codemirror/ubo-static-filtering.js @@ -44,22 +44,33 @@ CodeMirror.defineMode('ubo-static-filtering', function() { if ( StaticFilteringParser instanceof Object === false ) { return; } const parser = new StaticFilteringParser({ interactive: true }); + const reURL = /\bhttps?:\/\/\S+/; const rePreparseDirectives = /^!#(?:if|endif|include )\b/; const rePreparseIfDirective = /^(!#if ?)(.*)$/; let parserSlot = 0; let netOptionValueMode = false; const colorCommentSpan = function(stream) { - if ( rePreparseDirectives.test(stream.string) === false ) { + const { string, pos } = stream; + if ( rePreparseDirectives.test(string) === false ) { + const match = reURL.exec(string.slice(pos)); + if ( match !== null ) { + if ( match.index === 0 ) { + stream.pos += match[0].length; + return 'comment link'; + } + stream.pos += match.index; + return 'comment'; + } stream.skipToEnd(); return 'comment'; } - const match = rePreparseIfDirective.exec(stream.string); + const match = rePreparseIfDirective.exec(string); if ( match === null ) { stream.skipToEnd(); return 'variable strong'; } - if ( stream.pos < match[1].length ) { + if ( pos < match[1].length ) { stream.pos += match[1].length; return 'variable strong'; } @@ -95,19 +106,29 @@ CodeMirror.defineMode('ubo-static-filtering', function() { }; const colorExtScriptletPatternSpan = function(stream) { + const { pos, string } = stream; const { i, len } = parser.patternSpan; - if ( stream.pos === parser.slices[i+1] ) { - stream.pos += 4; + const patternBeg = parser.slices[i+1]; + if ( pos === patternBeg ) { + stream.pos = pos + 4; return 'def'; } if ( len > 3 ) { + if ( pos === patternBeg + 4 ) { + const match = /^[^,)]+/.exec(string.slice(pos)); + const token = match && match[0].trim(); + if ( token && scriptletNames.has(token) === false ) { + stream.pos = pos + match[0].length; + return 'warning'; + } + } const r = parser.slices[i+len+1] - 1; - if ( stream.pos < r ) { + if ( pos < r ) { stream.pos = r; return 'variable'; } - if ( stream.pos === r ) { - stream.pos += 1; + if ( pos === r ) { + stream.pos = pos + 1; return 'def'; } } @@ -483,7 +504,6 @@ const initHints = function() { /******************************************************************************/ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => { - const foldIfEndif = function(startLineNo, startLine, cm) { const lastLineNo = cm.lastLine(); let endLineNo = startLineNo; @@ -539,6 +559,50 @@ CodeMirror.registerHelper('fold', 'ubo-static-filtering', (( ) => { /******************************************************************************/ +// Enhanced word selection + +{ + const Pass = CodeMirror.Pass; + + const selectWordAt = function(cm, pos) { + const { line, ch } = pos; + + // Leave current selection alone + if ( cm.somethingSelected() ) { + const from = cm.getCursor('from'); + const to = cm.getCursor('to'); + if ( + (line > from.line || line === from.line && ch > from.ch) && + (line < to.line || line === to.line && ch < to.ch) + ) { + return Pass; + } + } + const s = cm.getLine(line); + + // Select URL + let lmatch = /\bhttps?:\/\/\S+$/.exec(s.slice(0, ch)); + let rmatch = /^\S+/.exec(s.slice(ch)); + + // TODO: add more convenient word-matching cases here + // if ( lmatch === null || rmatch === null ) { ... } + + if ( lmatch === null || rmatch === null ) { return Pass; } + cm.setSelection( + { line, ch: lmatch.index }, + { line, ch: ch + rmatch.index + rmatch[0].length } + ); + }; + + CodeMirror.defineInitHook(cm => { + cm.addKeyMap({ + 'LeftDoubleClick': selectWordAt, + }); + }); +} + +/******************************************************************************/ + // <<<<< end of local scope }