properly escape/unescape regexp in applies-to widgets

This commit is contained in:
tophf 2017-12-03 21:34:01 +03:00
parent 489546e35c
commit e905e4e079
4 changed files with 24 additions and 2 deletions

View File

@ -742,6 +742,10 @@
"message": "Invalid regexps skipped", "message": "Invalid regexps skipped",
"description": "RegExp test report: label for the invalid expressions" "description": "RegExp test report: label for the invalid expressions"
}, },
"styleRegexpTestNote": {
"message": "Note: use a single \\ for escaping in the regexp input field, which will be automatically converted to \\\\ in the style code as per specification for quoted strings in CSS.",
"description": "RegExp test report: a note displayed at the bottom of the dialog"
},
"styleRegexpPartialExplanation": { "styleRegexpPartialExplanation": {
"message": "This style uses partially matching regexps in violation of <a href='https://developer.mozilla.org/docs/Web/CSS/@document'>CSS4 @document specification</a> which requires a full URL match. The affected CSS sections were not applied to the page. This style was probably created in Stylish-for-Chrome which incorrectly checks 'regexp()' rules since the very first version (known bug)." "message": "This style uses partially matching regexps in violation of <a href='https://developer.mozilla.org/docs/Web/CSS/@document'>CSS4 @document specification</a> which requires a full URL match. The affected CSS sections were not applied to the page. This style was probably created in Stylish-for-Chrome which incorrectly checks 'regexp()' rules since the very first version (known bug)."
}, },

View File

@ -435,9 +435,13 @@ function createAppliesToLineWidget(cm) {
} }
function changeItem(apply, part, newText) { function changeItem(apply, part, newText) {
if (!apply) {
return;
}
part = apply[part]; part = apply[part];
const range = part.mark.find(); const range = part.mark.find();
part.mark.clear(); part.mark.clear();
newText = newText.replace(/\\/g, '\\\\');
cm.replaceRange(newText, range.from, range.to, 'appliesTo'); cm.replaceRange(newText, range.from, range.to, 'appliesTo');
part.mark = cm.markText( part.mark = cm.markText(
range.from, range.from,
@ -467,12 +471,14 @@ function createAppliesToLineWidget(cm) {
} }
function createApply(pos, typeText, valueText, isQuoted = false) { function createApply(pos, typeText, valueText, isQuoted = false) {
typeText = typeText.toLowerCase();
const start = pos; const start = pos;
const typeStart = start; const typeStart = start;
const typeEnd = typeStart + typeText.length; const typeEnd = typeStart + typeText.length;
const valueStart = typeEnd + 1 + Number(isQuoted); const valueStart = typeEnd + 1 + Number(isQuoted);
const valueEnd = valueStart + valueText.length; const valueEnd = valueStart + valueText.length;
const end = valueEnd + Number(isQuoted) + 1; const end = valueEnd + Number(isQuoted) + 1;
const hasSingleEscapes = /([^\\]|^)\\([^\\]|$)/.test(valueText);
return { return {
start, start,
type: { type: {
@ -481,7 +487,7 @@ function createAppliesToLineWidget(cm) {
end: typeEnd, end: typeEnd,
}, },
value: { value: {
text: valueText, text: hasSingleEscapes ? valueText : valueText.replace(/\\\\/g, '\\'),
start: valueStart, start: valueStart,
end: valueEnd, end: valueEnd,
}, },

View File

@ -385,6 +385,12 @@ body[data-match-highlight="selection"] .CodeMirror-selection-highlight-scrollbar
margin-left: -20px; margin-left: -20px;
margin-top: -1px; margin-top: -1px;
} }
.regexp-report-note {
font-size: 90%;
margin: 1em 1em .5em;
color: #999;
}
/************ help popup ************/ /************ help popup ************/
#help-popup { #help-popup {
top: 3rem; top: 3rem;

View File

@ -165,9 +165,15 @@ var regExpTester = (() => {
} }
} }
} }
report.appendChild($element({
tag: 'p',
className: 'regexp-report-note',
appendChild: t('styleRegexpTestNote').split(/(\\+)/)
.map(s => s.startsWith('\\') ? $element({tag: 'code', textContent: s}) : s),
}));
showHelp(t('styleRegexpTestTitle'), report); showHelp(t('styleRegexpTestTitle'), report);
$('.regexp-report').onclick = event => { report.onclick = event => {
const target = event.target.closest('a, .regexp-report div'); const target = event.target.closest('a, .regexp-report div');
if (target) { if (target) {
openURL({ openURL({