mirror of
https://github.com/gorhill/uBlock.git
synced 2024-11-11 09:31:01 +01:00
Minor code review of static filtering parser
Rename `l` property to `len`, to avoid ambiguity as `l` could mean _left_ or _length_. Typically `l` is to be used for _left_ (whereas `r` is to be used for _right_). Additionally, add CodeMirror's bracket-matching and bracket auto-closing to _My filters_ pane and and bracket-matching to asset viewer page.
This commit is contained in:
parent
2523959f20
commit
6d8b310d94
9 changed files with 555 additions and 161 deletions
|
@ -38,6 +38,8 @@
|
|||
|
||||
<script src="lib/codemirror/lib/codemirror.js"></script>
|
||||
<script src="lib/codemirror/addon/display/panel.js"></script>
|
||||
<script src="lib/codemirror/addon/edit/closebrackets.js"></script>
|
||||
<script src="lib/codemirror/addon/edit/matchbrackets.js"></script>
|
||||
<script src="lib/codemirror/addon/scroll/annotatescrollbar.js"></script>
|
||||
<script src="lib/codemirror/addon/search/matchesonscrollbar.js"></script>
|
||||
<script src="lib/codemirror/addon/search/searchcursor.js"></script>
|
||||
|
|
|
@ -30,6 +30,7 @@ body {
|
|||
|
||||
<script src="lib/codemirror/lib/codemirror.js"></script>
|
||||
<script src="lib/codemirror/addon/display/panel.js"></script>
|
||||
<script src="lib/codemirror/addon/edit/matchbrackets.js"></script>
|
||||
<script src="lib/codemirror/addon/scroll/annotatescrollbar.js"></script>
|
||||
<script src="lib/codemirror/addon/search/matchesonscrollbar.js"></script>
|
||||
<script src="lib/codemirror/addon/search/searchcursor.js"></script>
|
||||
|
|
|
@ -48,6 +48,14 @@
|
|||
.cm-staticnetAllow { color: #004f00; }
|
||||
.cm-staticOpt { background-color: #ddd; font-weight: bold; }
|
||||
|
||||
div.CodeMirror span.CodeMirror-matchingbracket {
|
||||
color: unset;
|
||||
}
|
||||
.CodeMirror-matchingbracket {
|
||||
color: inherit !important;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.cm-search-widget {
|
||||
align-items: center;
|
||||
background-color: var(--bg-code);
|
||||
|
|
|
@ -29,15 +29,15 @@
|
|||
|
||||
/******************************************************************************/
|
||||
|
||||
const cmEditor = new CodeMirror(
|
||||
document.getElementById('userFilters'),
|
||||
{
|
||||
autofocus: true,
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
styleActiveLine: true,
|
||||
}
|
||||
);
|
||||
const cmEditor = new CodeMirror(document.getElementById('userFilters'), {
|
||||
autoCloseBrackets: true,
|
||||
autofocus: true,
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
matchBrackets: true,
|
||||
maxScanLines: 1,
|
||||
styleActiveLine: true,
|
||||
});
|
||||
|
||||
uBlockDashboard.patchCodeMirrorEditor(cmEditor);
|
||||
|
||||
|
|
|
@ -30,16 +30,15 @@
|
|||
const assetKey = params.get('url');
|
||||
if ( assetKey === null ) { return; }
|
||||
|
||||
const cmEditor = new CodeMirror(
|
||||
document.getElementById('content'),
|
||||
{
|
||||
autofocus: true,
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
readOnly: true,
|
||||
styleActiveLine: true,
|
||||
}
|
||||
);
|
||||
const cmEditor = new CodeMirror(document.getElementById('content'), {
|
||||
autofocus: true,
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
matchBrackets: true,
|
||||
maxScanLines: 1,
|
||||
readOnly: true,
|
||||
styleActiveLine: true,
|
||||
});
|
||||
|
||||
uBlockDashboard.patchCodeMirrorEditor(cmEditor);
|
||||
|
||||
|
|
|
@ -29,6 +29,48 @@ CodeMirror.defineMode("ubo-static-filtering", function() {
|
|||
let parserSlot = 0;
|
||||
let netOptionValueMode = false;
|
||||
|
||||
const colorExtHTMLPatternSpan = function(stream) {
|
||||
const { i } = parser.patternSpan;
|
||||
if ( stream.pos === parser.slices[i+1] ) {
|
||||
stream.pos += 1;
|
||||
return 'def';
|
||||
}
|
||||
stream.skipToEnd();
|
||||
return 'variable';
|
||||
};
|
||||
|
||||
const colorExtScriptletPatternSpan = function(stream) {
|
||||
const { i, len } = parser.patternSpan;
|
||||
if ( stream.pos === parser.slices[i+1] ) {
|
||||
stream.pos += 4;
|
||||
return 'def';
|
||||
}
|
||||
if ( len > 3 ) {
|
||||
const r = parser.slices[i+len+1] - 1;
|
||||
if ( stream.pos < r ) {
|
||||
stream.pos = r;
|
||||
return 'variable';
|
||||
}
|
||||
if ( stream.pos === r ) {
|
||||
stream.pos += 1;
|
||||
return 'def';
|
||||
}
|
||||
}
|
||||
stream.skipToEnd();
|
||||
return 'variable';
|
||||
};
|
||||
|
||||
const colorExtPatternSpan = function(stream) {
|
||||
if ( (parser.flavorBits & parser.BITFlavorExtScriptlet) !== 0 ) {
|
||||
return colorExtScriptletPatternSpan(stream);
|
||||
}
|
||||
if ( (parser.flavorBits & parser.BITFlavorExtHTML) !== 0 ) {
|
||||
return colorExtHTMLPatternSpan(stream);
|
||||
}
|
||||
stream.skipToEnd();
|
||||
return 'variable';
|
||||
};
|
||||
|
||||
const colorExtSpan = function(stream) {
|
||||
if ( parserSlot < parser.optionsAnchorSpan.i ) {
|
||||
const style = (parser.slices[parserSlot] & parser.BITComma) === 0
|
||||
|
@ -49,23 +91,25 @@ CodeMirror.defineMode("ubo-static-filtering", function() {
|
|||
parserSlot += 3;
|
||||
return `${style} strong`;
|
||||
}
|
||||
if ( parserSlot >= parser.patternSpan.i ) {
|
||||
stream.skipToEnd();
|
||||
return 'variable';
|
||||
if (
|
||||
parserSlot >= parser.patternSpan.i &&
|
||||
parserSlot < parser.rightSpaceSpan.i
|
||||
) {
|
||||
return colorExtPatternSpan(stream);
|
||||
}
|
||||
stream.skipToEnd();
|
||||
return '';
|
||||
return null;
|
||||
};
|
||||
|
||||
const colorNetSpan = function(stream) {
|
||||
if ( parserSlot < parser.exceptionSpan.i ) {
|
||||
stream.pos += parser.slices[parserSlot+2];
|
||||
parserSlot += 3;
|
||||
return '';
|
||||
return null;
|
||||
}
|
||||
if (
|
||||
parserSlot === parser.exceptionSpan.i &&
|
||||
parser.exceptionSpan.l !== 0
|
||||
parser.exceptionSpan.len !== 0
|
||||
) {
|
||||
stream.pos += parser.slices[parserSlot+2];
|
||||
parserSlot += 3;
|
||||
|
@ -73,9 +117,9 @@ CodeMirror.defineMode("ubo-static-filtering", function() {
|
|||
}
|
||||
if (
|
||||
parserSlot === parser.patternLeftAnchorSpan.i &&
|
||||
parser.patternLeftAnchorSpan.l !== 0 ||
|
||||
parser.patternLeftAnchorSpan.len !== 0 ||
|
||||
parserSlot === parser.patternRightAnchorSpan.i &&
|
||||
parser.patternRightAnchorSpan.l !== 0
|
||||
parser.patternRightAnchorSpan.len !== 0
|
||||
) {
|
||||
stream.pos += parser.slices[parserSlot+2];
|
||||
parserSlot += 3;
|
||||
|
@ -83,31 +127,30 @@ CodeMirror.defineMode("ubo-static-filtering", function() {
|
|||
}
|
||||
if (
|
||||
parserSlot >= parser.patternSpan.i &&
|
||||
parserSlot < parser.patternRightAnchorSpan.i
|
||||
parserSlot < parser.optionsAnchorSpan.i
|
||||
) {
|
||||
const isRegex = parser.patternIsRegex();
|
||||
if (
|
||||
(isRegex === false) &&
|
||||
(parser.slices[parserSlot] & (parser.BITAsterisk | parser.BITCaret)) !== 0
|
||||
) {
|
||||
if ( parser.patternIsRegex() ) {
|
||||
stream.pos = parser.slices[parser.optionsAnchorSpan.i+1];
|
||||
parserSlot += parser.optionsAnchorSpan.i;
|
||||
return 'variable regex';
|
||||
}
|
||||
if ( (parser.slices[parserSlot] & (parser.BITAsterisk | parser.BITCaret)) !== 0 ) {
|
||||
stream.pos += parser.slices[parserSlot+2];
|
||||
parserSlot += 3;
|
||||
return 'keyword strong';
|
||||
}
|
||||
let style = 'variable';
|
||||
if ( isRegex ) { style += ' regex'; }
|
||||
const nextSlot = parser.skipUntil(
|
||||
parserSlot,
|
||||
parserSlot + 3,
|
||||
parser.patternRightAnchorSpan.i,
|
||||
parser.BITAsterisk | parser.BITCaret
|
||||
);
|
||||
stream.pos = parser.slices[nextSlot+1];
|
||||
parserSlot = nextSlot;
|
||||
return style;
|
||||
return 'variable';
|
||||
}
|
||||
if (
|
||||
parserSlot === parser.optionsAnchorSpan.i &&
|
||||
parser.optionsAnchorSpan.l !== 0
|
||||
parserSlot < parser.optionsSpan.i !== 0
|
||||
) {
|
||||
stream.pos += parser.slices[parserSlot+2];
|
||||
parserSlot += 3;
|
||||
|
@ -115,7 +158,7 @@ CodeMirror.defineMode("ubo-static-filtering", function() {
|
|||
}
|
||||
if (
|
||||
parserSlot >= parser.optionsSpan.i &&
|
||||
parser.optionsSpan.l !== 0
|
||||
parserSlot < parser.rightSpaceSpan.i
|
||||
) {
|
||||
const bits = parser.slices[parserSlot];
|
||||
let style;
|
||||
|
@ -137,13 +180,13 @@ CodeMirror.defineMode("ubo-static-filtering", function() {
|
|||
}
|
||||
if (
|
||||
parserSlot >= parser.commentSpan.i &&
|
||||
parser.commentSpan.l !== 0
|
||||
parser.commentSpan.len !== 0
|
||||
) {
|
||||
stream.skipToEnd();
|
||||
return 'comment';
|
||||
}
|
||||
stream.skipToEnd();
|
||||
return '';
|
||||
return null;
|
||||
};
|
||||
|
||||
const colorSpan = function(stream) {
|
||||
|
@ -185,7 +228,7 @@ CodeMirror.defineMode("ubo-static-filtering", function() {
|
|||
parserSlot = 0;
|
||||
netOptionValueMode = false;
|
||||
}
|
||||
let style = colorSpan(stream);
|
||||
let style = colorSpan(stream) || '';
|
||||
if ( (parser.flavorBits & parser.BITFlavorError) !== 0 ) {
|
||||
style += ' line-background-error';
|
||||
}
|
||||
|
|
|
@ -129,7 +129,7 @@ const Parser = class {
|
|||
|
||||
analyze(raw) {
|
||||
this.slice(raw);
|
||||
let slot = this.leftSpaceSpan.l;
|
||||
let slot = this.leftSpaceSpan.len;
|
||||
if ( slot === this.rightSpaceSpan.i ) { return; }
|
||||
|
||||
// test for `!`, `#`, or `[`
|
||||
|
@ -158,7 +158,7 @@ const Parser = class {
|
|||
for (;;) {
|
||||
if ( hasBits(this.slices[hashSlot-3], BITSpace) ) {
|
||||
this.commentSpan.i = hashSlot-3;
|
||||
this.commentSpan.l = this.rightSpaceSpan.i - hashSlot;
|
||||
this.commentSpan.len = this.rightSpaceSpan.i - hashSlot;
|
||||
break;
|
||||
}
|
||||
hashSlot = this.findFirstMatch(hashSlot + 6, BITHash);
|
||||
|
@ -194,25 +194,25 @@ const Parser = class {
|
|||
analyzeExt(from) {
|
||||
let end = this.rightSpaceSpan.i;
|
||||
// Number of consecutive #s.
|
||||
const l = this.slices[from+2];
|
||||
const len = this.slices[from+2];
|
||||
// More than 3 #s is likely to be a comment in a hosts file.
|
||||
if ( l > 3 ) { return; }
|
||||
if ( l !== 1 ) {
|
||||
if ( len > 3 ) { return; }
|
||||
if ( len !== 1 ) {
|
||||
// If a space immediately follows 2 #s, assume a comment.
|
||||
if ( l === 2 ) {
|
||||
if ( len === 2 ) {
|
||||
if ( from+3 === end || hasBits(this.slices[from+3], BITSpace) ) {
|
||||
return;
|
||||
}
|
||||
} else /* l === 3 */ {
|
||||
} else /* len === 3 */ {
|
||||
this.splitSlot(from, 2);
|
||||
end = this.rightSpaceSpan.i;
|
||||
}
|
||||
this.optionsSpan.i = this.leftSpaceSpan.i + this.leftSpaceSpan.l;
|
||||
this.optionsSpan.l = from - this.optionsSpan.i;
|
||||
this.optionsSpan.i = this.leftSpaceSpan.i + this.leftSpaceSpan.len;
|
||||
this.optionsSpan.len = from - this.optionsSpan.i;
|
||||
this.optionsAnchorSpan.i = from;
|
||||
this.optionsAnchorSpan.l = 3;
|
||||
this.optionsAnchorSpan.len = 3;
|
||||
this.patternSpan.i = from + 3;
|
||||
this.patternSpan.l = this.rightSpaceSpan.i - this.patternSpan.i;
|
||||
this.patternSpan.len = this.rightSpaceSpan.i - this.patternSpan.i;
|
||||
this.category = CATStaticExtFilter;
|
||||
this.analyzeExtPattern();
|
||||
return;
|
||||
|
@ -257,12 +257,12 @@ const Parser = class {
|
|||
this.splitSlot(to, 1);
|
||||
}
|
||||
to += 3;
|
||||
this.optionsSpan.i = this.leftSpaceSpan.i + this.leftSpaceSpan.l;
|
||||
this.optionsSpan.l = from - this.optionsSpan.i;
|
||||
this.optionsSpan.i = this.leftSpaceSpan.i + this.leftSpaceSpan.len;
|
||||
this.optionsSpan.len = from - this.optionsSpan.i;
|
||||
this.optionsAnchorSpan.i = from;
|
||||
this.optionsAnchorSpan.l = to - this.optionsAnchorSpan.i;
|
||||
this.optionsAnchorSpan.len = to - this.optionsAnchorSpan.i;
|
||||
this.patternSpan.i = to;
|
||||
this.patternSpan.l = this.rightSpaceSpan.i - to;
|
||||
this.patternSpan.len = this.rightSpaceSpan.i - to;
|
||||
this.flavorBits = flavorBits;
|
||||
this.category = CATStaticExtFilter;
|
||||
this.analyzeExtPattern();
|
||||
|
@ -305,11 +305,11 @@ const Parser = class {
|
|||
}
|
||||
}
|
||||
|
||||
// Use in syntax highlighting contexts
|
||||
// Use in syntax highlighting contexts
|
||||
analyzeExtExtra() {
|
||||
if ( this.hasOptions() ) {
|
||||
const { i, l } = this.optionsSpan;
|
||||
this.analyzeDomainList(i, i + l, BITComma, 0b11);
|
||||
const { i, len } = this.optionsSpan;
|
||||
this.analyzeDomainList(i, i + len, BITComma, 0b11);
|
||||
}
|
||||
if ( hasBits(this.flavorBits, BITFlavorUnsupported) ) {
|
||||
this.markSpan(this.patternSpan, BITError);
|
||||
|
@ -341,19 +341,19 @@ const Parser = class {
|
|||
let islice = this.leftSpaceSpan.i;
|
||||
|
||||
// Assume no exception
|
||||
this.exceptionSpan.i = this.leftSpaceSpan.l;
|
||||
this.exceptionSpan.i = this.leftSpaceSpan.len;
|
||||
// Exception?
|
||||
if (
|
||||
islice < this.commentSpan.i &&
|
||||
hasBits(this.slices[islice], BITAt)
|
||||
) {
|
||||
const l = this.slices[islice+2];
|
||||
const len = this.slices[islice+2];
|
||||
// @@@*, ... => @@, @*, ...
|
||||
if ( l >= 2 ) {
|
||||
if ( l > 2 ) {
|
||||
if ( len >= 2 ) {
|
||||
if ( len > 2 ) {
|
||||
this.splitSlot(islice, 2);
|
||||
}
|
||||
this.exceptionSpan.l = 3;
|
||||
this.exceptionSpan.len = 3;
|
||||
islice += 3;
|
||||
this.flavorBits |= BITFlavorException;
|
||||
}
|
||||
|
@ -364,17 +364,17 @@ const Parser = class {
|
|||
|
||||
// Assume all is part of pattern
|
||||
this.patternSpan.i = islice;
|
||||
this.patternSpan.l = this.optionsAnchorSpan.i - islice;
|
||||
this.patternSpan.len = this.optionsAnchorSpan.i - islice;
|
||||
|
||||
let patternStartIsRegex =
|
||||
islice < this.optionsAnchorSpan.i &&
|
||||
hasBits(this.slices[islice], BITSlash);
|
||||
let patternIsRegex = patternStartIsRegex;
|
||||
if ( patternStartIsRegex ) {
|
||||
const { i, l } = this.patternSpan;
|
||||
const { i, len } = this.patternSpan;
|
||||
patternIsRegex = (
|
||||
l === 3 && this.slices[i+2] > 2 ||
|
||||
l > 3 && hasBits(this.slices[i+l-3], BITSlash)
|
||||
len === 3 && this.slices[i+2] > 2 ||
|
||||
len > 3 && hasBits(this.slices[i+len-3], BITSlash)
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -390,8 +390,8 @@ const Parser = class {
|
|||
optionsBits |= bits;
|
||||
}
|
||||
if ( i >= islice ) {
|
||||
const l = this.slices[i+2];
|
||||
if ( l > 1 ) {
|
||||
const len = this.slices[i+2];
|
||||
if ( len > 1 ) {
|
||||
// https://github.com/gorhill/uBlock/issues/952
|
||||
// AdGuard-specific `$$` filters => unsupported.
|
||||
if ( this.findFirstOdd(0, BITHostname | BITComma | BITAsterisk) === i ) {
|
||||
|
@ -400,22 +400,22 @@ const Parser = class {
|
|||
this.markSlices(i, i+3, BITError);
|
||||
}
|
||||
} else {
|
||||
this.splitSlot(i, l - 1);
|
||||
this.splitSlot(i, len - 1);
|
||||
i += 3;
|
||||
}
|
||||
}
|
||||
this.patternSpan.l = i - this.patternSpan.i;
|
||||
this.patternSpan.len = i - this.patternSpan.i;
|
||||
this.optionsAnchorSpan.i = i;
|
||||
this.optionsAnchorSpan.l = 3;
|
||||
this.optionsAnchorSpan.len = 3;
|
||||
i += 3;
|
||||
this.optionsSpan.i = i;
|
||||
this.optionsSpan.l = this.commentSpan.i - i;
|
||||
this.optionsSpan.len = this.commentSpan.i - i;
|
||||
this.optionsBits = optionsBits;
|
||||
if ( patternStartIsRegex ) {
|
||||
const { i, l } = this.patternSpan;
|
||||
const { i, len } = this.patternSpan;
|
||||
patternIsRegex = (
|
||||
l === 3 && this.slices[i+2] > 2 ||
|
||||
l > 3 && hasBits(this.slices[i+l-3], BITSlash)
|
||||
len === 3 && this.slices[i+2] > 2 ||
|
||||
len > 3 && hasBits(this.slices[i+len-3], BITSlash)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -437,19 +437,19 @@ const Parser = class {
|
|||
// `|`: anchor to start of URL
|
||||
// `||`: anchor to left of a hostname label
|
||||
if (
|
||||
this.patternSpan.l !== 0 &&
|
||||
this.patternSpan.len !== 0 &&
|
||||
hasBits(this.slices[this.patternSpan.i], BITPipe)
|
||||
) {
|
||||
this.patternLeftAnchorSpan.l = 3;
|
||||
const l = this.slices[this.patternSpan.i+2];
|
||||
this.patternLeftAnchorSpan.len = 3;
|
||||
const len = this.slices[this.patternSpan.i+2];
|
||||
// |||*, ... => ||, |*, ...
|
||||
if ( l > 2 ) {
|
||||
if ( len > 2 ) {
|
||||
this.splitSlot(this.patternSpan.i, 2);
|
||||
} else {
|
||||
this.patternSpan.l -= 3;
|
||||
this.patternSpan.len -= 3;
|
||||
}
|
||||
this.patternSpan.i += 3;
|
||||
this.flavorBits |= l === 1
|
||||
this.flavorBits |= len === 1
|
||||
? BITFlavorNetLeftURLAnchor
|
||||
: BITFlavorNetLeftHnAnchor;
|
||||
}
|
||||
|
@ -459,21 +459,21 @@ const Parser = class {
|
|||
// fulfilled:
|
||||
// the pattern is hostname-anchored on the left
|
||||
// the pattern is made only of hostname characters
|
||||
if ( this.patternSpan.l !== 0 ) {
|
||||
const lastPatternSlice = this.patternSpan.l > 3
|
||||
if ( this.patternSpan.len !== 0 ) {
|
||||
const lastPatternSlice = this.patternSpan.len > 3
|
||||
? this.patternRightAnchorSpan.i - 3
|
||||
: this.patternSpan.i;
|
||||
const bits = this.slices[lastPatternSlice];
|
||||
if ( (bits & BITPipe) !== 0 ) {
|
||||
this.patternRightAnchorSpan.i = lastPatternSlice;
|
||||
this.patternRightAnchorSpan.l = 3;
|
||||
const l = this.slices[this.patternRightAnchorSpan.i+2];
|
||||
this.patternRightAnchorSpan.len = 3;
|
||||
const len = this.slices[this.patternRightAnchorSpan.i+2];
|
||||
// ..., ||* => ..., |*, |
|
||||
if ( l > 1 ) {
|
||||
this.splitSlot(this.patternRightAnchorSpan.i, l - 1);
|
||||
if ( len > 1 ) {
|
||||
this.splitSlot(this.patternRightAnchorSpan.i, len - 1);
|
||||
this.patternRightAnchorSpan.i += 3;
|
||||
} else {
|
||||
this.patternSpan.l -= 3;
|
||||
this.patternSpan.len -= 3;
|
||||
}
|
||||
this.flavorBits |= BITFlavorNetRightURLAnchor;
|
||||
} else if (
|
||||
|
@ -487,8 +487,8 @@ const Parser = class {
|
|||
) === lastPatternSlice
|
||||
) {
|
||||
this.patternRightAnchorSpan.i = lastPatternSlice;
|
||||
this.patternRightAnchorSpan.l = 3;
|
||||
this.patternSpan.l -= 3;
|
||||
this.patternRightAnchorSpan.len = 3;
|
||||
this.patternSpan.len -= 3;
|
||||
this.flavorBits |= BITFlavorNetRightHnAnchor;
|
||||
}
|
||||
}
|
||||
|
@ -502,8 +502,8 @@ const Parser = class {
|
|||
// if a pattern contains space characters, the pattern will be only
|
||||
// the part following the last space occurrence.
|
||||
{
|
||||
const { i, l } = this.patternSpan;
|
||||
let j = l;
|
||||
const { i, len } = this.patternSpan;
|
||||
let j = len;
|
||||
for (;;) {
|
||||
if ( j === 0 ) { break; }
|
||||
j -= 3;
|
||||
|
@ -513,7 +513,7 @@ const Parser = class {
|
|||
}
|
||||
if ( j !== 0 ) {
|
||||
this.patternSpan.i += j + 3;
|
||||
this.patternSpan.l -= j + 3;
|
||||
this.patternSpan.len -= j + 3;
|
||||
if ( this.reIsLocalhostRedirect.test(this.getNetPattern()) ) {
|
||||
this.flavorBits |= BITFlavorIgnore;
|
||||
}
|
||||
|
@ -539,41 +539,41 @@ const Parser = class {
|
|||
// https://github.com/gorhill/uBlock/issues/3034
|
||||
// We can remove anchoring if we need to match all at the end.
|
||||
{
|
||||
let { i, l } = this.patternSpan;
|
||||
let { i, len } = this.patternSpan;
|
||||
// Pointless leading wildcard
|
||||
if (
|
||||
l > 3 &&
|
||||
len > 3 &&
|
||||
hasBits(this.slices[i], BITAsterisk) &&
|
||||
hasNoBits(this.slices[i+3], BITPatternToken)
|
||||
) {
|
||||
this.slices[i] |= BITIgnore;
|
||||
i += 3; l -= 3;
|
||||
i += 3; len -= 3;
|
||||
this.patternSpan.i = i;
|
||||
this.patternSpan.l = l;
|
||||
this.patternSpan.len = len;
|
||||
// We can ignore left-hand pattern anchor
|
||||
if ( this.patternLeftAnchorSpan.l !== 0 ) {
|
||||
if ( this.patternLeftAnchorSpan.len !== 0 ) {
|
||||
this.slices[this.patternLeftAnchorSpan.i] |= BITIgnore;
|
||||
this.flavorBits &= ~BITFlavorNetLeftAnchor;
|
||||
}
|
||||
}
|
||||
// Pointless trailing wildcard
|
||||
if (
|
||||
l > 3 &&
|
||||
hasBits(this.slices[i+l-3], BITAsterisk) &&
|
||||
hasNoBits(this.slices[i+l-6], BITPatternToken)
|
||||
len > 3 &&
|
||||
hasBits(this.slices[i+len-3], BITAsterisk) &&
|
||||
hasNoBits(this.slices[i+len-6], BITPatternToken)
|
||||
) {
|
||||
// Ignore only if the pattern would not end up looking like
|
||||
// a regex.
|
||||
if (
|
||||
hasNoBits(this.slices[i], BITSlash) ||
|
||||
hasNoBits(this.slices[i+l-6], BITSlash)
|
||||
hasNoBits(this.slices[i+len-6], BITSlash)
|
||||
) {
|
||||
this.slices[i+l-3] |= BITIgnore;
|
||||
this.slices[i+len-3] |= BITIgnore;
|
||||
}
|
||||
l -= 3;
|
||||
this.patternSpan.l = l;
|
||||
len -= 3;
|
||||
this.patternSpan.len = len;
|
||||
// We can ignore right-hand pattern anchor
|
||||
if ( this.patternRightAnchorSpan.l !== 0 ) {
|
||||
if ( this.patternRightAnchorSpan.len !== 0 ) {
|
||||
this.slices[this.patternRightAnchorSpan.i] |= BITIgnore;
|
||||
this.flavorBits &= ~BITFlavorNetRightAnchor;
|
||||
}
|
||||
|
@ -581,8 +581,8 @@ const Parser = class {
|
|||
// Pointless left-hand pattern anchoring
|
||||
if (
|
||||
(
|
||||
l === 0 ||
|
||||
l !== 0 && hasBits(this.slices[i], BITAsterisk)
|
||||
len === 0 ||
|
||||
len !== 0 && hasBits(this.slices[i], BITAsterisk)
|
||||
) &&
|
||||
hasBits(this.flavorBits, BITFlavorNetLeftAnchor)
|
||||
) {
|
||||
|
@ -592,8 +592,8 @@ const Parser = class {
|
|||
// Pointless right-hand pattern anchoring
|
||||
if (
|
||||
(
|
||||
l === 0 ||
|
||||
l !== 0 && hasBits(this.slices[i+l-3], BITAsterisk)
|
||||
len === 0 ||
|
||||
len !== 0 && hasBits(this.slices[i+len-3], BITAsterisk)
|
||||
) &&
|
||||
hasBits(this.flavorBits, BITFlavorNetRightAnchor)
|
||||
) {
|
||||
|
@ -747,9 +747,9 @@ const Parser = class {
|
|||
ptr += 3;
|
||||
// Trim left
|
||||
if ( (slices[0] & BITSpace) !== 0 ) {
|
||||
this.leftSpaceSpan.l = 3;
|
||||
this.leftSpaceSpan.len = 3;
|
||||
} else {
|
||||
this.leftSpaceSpan.l = 0;
|
||||
this.leftSpaceSpan.len = 0;
|
||||
}
|
||||
// Trim right
|
||||
const lastSlice = this.eolSpan.i - 3;
|
||||
|
@ -758,25 +758,25 @@ const Parser = class {
|
|||
(slices[lastSlice] & BITSpace) !== 0
|
||||
) {
|
||||
this.rightSpaceSpan.i = lastSlice;
|
||||
this.rightSpaceSpan.l = 3;
|
||||
this.rightSpaceSpan.len = 3;
|
||||
} else {
|
||||
this.rightSpaceSpan.i = this.eolSpan.i;
|
||||
this.rightSpaceSpan.l = 0;
|
||||
this.rightSpaceSpan.len = 0;
|
||||
}
|
||||
// Quit cleanly
|
||||
this.sliceWritePtr = ptr;
|
||||
this.allBits = allBits;
|
||||
}
|
||||
|
||||
splitSlot(slot, l) {
|
||||
splitSlot(slot, len) {
|
||||
this.sliceWritePtr += 3;
|
||||
if ( this.sliceWritePtr > this.slices.length ) {
|
||||
this.slices.push(0, 0, 0);
|
||||
}
|
||||
this.slices.copyWithin(slot + 3, slot, this.sliceWritePtr - 3);
|
||||
this.slices[slot+3+1] = this.slices[slot+1] + l;
|
||||
this.slices[slot+3+2] = this.slices[slot+2] - l;
|
||||
this.slices[slot+2] = l;
|
||||
this.slices[slot+3+1] = this.slices[slot+1] + len;
|
||||
this.slices[slot+3+2] = this.slices[slot+2] - len;
|
||||
this.slices[slot+2] = len;
|
||||
for ( const span of this.spans ) {
|
||||
if ( span.i > slot ) {
|
||||
span.i += 3;
|
||||
|
@ -792,8 +792,8 @@ const Parser = class {
|
|||
}
|
||||
|
||||
markSpan(span, bits) {
|
||||
const { i, l } = span;
|
||||
this.markSlices(i, i + l, bits);
|
||||
const { i, len } = span;
|
||||
this.markSlices(i, i + len, bits);
|
||||
}
|
||||
|
||||
unmarkSlices(beg, end, bits) {
|
||||
|
@ -847,9 +847,9 @@ const Parser = class {
|
|||
}
|
||||
|
||||
strFromSpan(span) {
|
||||
if ( span.l === 0 ) { return ''; }
|
||||
if ( span.len === 0 ) { return ''; }
|
||||
const beg = span.i;
|
||||
return this.strFromSlices(beg, beg + span.l - 3);
|
||||
return this.strFromSlices(beg, beg + span.len - 3);
|
||||
}
|
||||
|
||||
isBlank() {
|
||||
|
@ -857,25 +857,25 @@ const Parser = class {
|
|||
}
|
||||
|
||||
hasOptions() {
|
||||
return this.optionsSpan.l !== 0;
|
||||
return this.optionsSpan.len !== 0;
|
||||
}
|
||||
|
||||
getPattern() {
|
||||
if ( this.pattern !== '' ) { return this.pattern; }
|
||||
const { i, l } = this.patternSpan;
|
||||
if ( l === 0 ) { return ''; }
|
||||
const { i, len } = this.patternSpan;
|
||||
if ( len === 0 ) { return ''; }
|
||||
let beg = this.slices[i+1];
|
||||
let end = this.slices[i+l+1];
|
||||
let end = this.slices[i+len+1];
|
||||
this.pattern = this.raw.slice(beg, end);
|
||||
return this.pattern;
|
||||
}
|
||||
|
||||
getNetPattern() {
|
||||
if ( this.pattern !== '' ) { return this.pattern; }
|
||||
const { i, l } = this.patternSpan;
|
||||
if ( l === 0 ) { return ''; }
|
||||
const { i, len } = this.patternSpan;
|
||||
if ( len === 0 ) { return ''; }
|
||||
let beg = this.slices[i+1];
|
||||
let end = this.slices[i+l+1];
|
||||
let end = this.slices[i+len+1];
|
||||
if ( hasBits(this.flavorBits, BITFlavorNetRegex) ) {
|
||||
beg += 1; end -= 1;
|
||||
}
|
||||
|
@ -888,15 +888,15 @@ const Parser = class {
|
|||
// - Single character other than `*` wildcard
|
||||
patternIsDubious() {
|
||||
return this.patternBits !== BITAsterisk &&
|
||||
this.optionsSpan.l === 0 &&
|
||||
this.patternSpan.l === 3 &&
|
||||
this.optionsSpan.len === 0 &&
|
||||
this.patternSpan.len === 3 &&
|
||||
this.slices[this.patternSpan.i+2] === 1;
|
||||
}
|
||||
|
||||
patternIsMatchAll() {
|
||||
const { l } = this.patternSpan;
|
||||
return l === 0 ||
|
||||
l === 3 && hasBits(this.patternBits, BITAsterisk);
|
||||
const { len } = this.patternSpan;
|
||||
return len === 0 ||
|
||||
len === 3 && hasBits(this.patternBits, BITAsterisk);
|
||||
}
|
||||
|
||||
patternIsPlainHostname() {
|
||||
|
@ -908,9 +908,9 @@ const Parser = class {
|
|||
) {
|
||||
return false;
|
||||
}
|
||||
const { i, l } = this.patternSpan;
|
||||
const { i, len } = this.patternSpan;
|
||||
return hasBits(this.slices[i], BITAlphaNum) &&
|
||||
hasBits(this.slices[i+l-3], BITAlphaNum);
|
||||
hasBits(this.slices[i+len-3], BITAlphaNum);
|
||||
}
|
||||
|
||||
patternIsLeftHostnameAnchored() {
|
||||
|
@ -954,17 +954,17 @@ const Parser = class {
|
|||
if ( hasUpper === false && this.pattern !== '' ) {
|
||||
return this.pattern;
|
||||
}
|
||||
const { i, l } = this.patternSpan;
|
||||
if ( l === 0 ) { return ''; }
|
||||
const { i, len } = this.patternSpan;
|
||||
if ( len === 0 ) { return ''; }
|
||||
const beg = this.slices[i+1];
|
||||
const end = this.slices[i+l+1];
|
||||
const end = this.slices[i+len+1];
|
||||
this.pattern = this.pattern || this.raw.slice(beg, end);
|
||||
if ( hasUpper === false ) { return this.pattern; }
|
||||
this.pattern = this.pattern.toLowerCase();
|
||||
this.raw = this.raw.slice(0, beg) +
|
||||
this.pattern +
|
||||
this.raw.slice(end);
|
||||
this.unmarkSlices(i, i+l, BITUppercase);
|
||||
this.unmarkSlices(i, i + len, BITUppercase);
|
||||
this.patternBits &= ~BITUppercase;
|
||||
return this.pattern;
|
||||
}
|
||||
|
@ -977,16 +977,16 @@ const Parser = class {
|
|||
if ( hasBits(this.patternBits, BITAsterisk) === false ) {
|
||||
return false;
|
||||
}
|
||||
const { i, l } = this.patternSpan;
|
||||
return l !== 0 && hasBits(this.slices[i], BITAsterisk);
|
||||
const { i, len } = this.patternSpan;
|
||||
return len !== 0 && hasBits(this.slices[i], BITAsterisk);
|
||||
}
|
||||
|
||||
patternHasTrailingWildcard() {
|
||||
if ( hasBits(this.patternBits, BITAsterisk) === false ) {
|
||||
return false;
|
||||
}
|
||||
const { i, l } = this.patternSpan;
|
||||
return l !== 0 && hasBits(this.slices[i+l-1], BITAsterisk);
|
||||
const { i, len } = this.patternSpan;
|
||||
return len !== 0 && hasBits(this.slices[i+len-1], BITAsterisk);
|
||||
}
|
||||
|
||||
optionHasUnicode() {
|
||||
|
@ -1008,8 +1008,8 @@ const Parser = class {
|
|||
return [];
|
||||
}
|
||||
|
||||
setMaxTokenLength(l) {
|
||||
this.maxTokenLength = l;
|
||||
setMaxTokenLength(len) {
|
||||
this.maxTokenLength = len;
|
||||
}
|
||||
|
||||
hasUnicode() {
|
||||
|
@ -1025,8 +1025,8 @@ const Parser = class {
|
|||
|
||||
toPunycode() {
|
||||
if ( this.patternHasUnicode() === false ) { return true; }
|
||||
const { i, l } = this.patternSpan;
|
||||
if ( l === 0 ) { return true; }
|
||||
const { i, len } = this.patternSpan;
|
||||
if ( len === 0 ) { return true; }
|
||||
let pattern = this.getNetPattern();
|
||||
const match = this.reHostname.exec(this.pattern);
|
||||
if ( match === null ) { return; }
|
||||
|
@ -1038,7 +1038,7 @@ const Parser = class {
|
|||
const punycoded = this.punycoder.hostname.replace(/__asterisk__/g, '*');
|
||||
pattern = punycoded + this.pattern.slice(match.index + match[0].length);
|
||||
const beg = this.slices[i+1];
|
||||
const end = this.slices[i+l+1];
|
||||
const end = this.slices[i+len+1];
|
||||
const raw = this.raw.slice(0, beg) + pattern + this.raw.slice(end);
|
||||
this.analyze(raw);
|
||||
return true;
|
||||
|
@ -1936,7 +1936,7 @@ const Span = class {
|
|||
this.reset();
|
||||
}
|
||||
reset() {
|
||||
this.i = this.l = 0;
|
||||
this.i = this.len = 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1963,7 +1963,7 @@ const NetOptionsIterator = class {
|
|||
}
|
||||
init() {
|
||||
this.readPtr = this.writePtr = 0;
|
||||
this.done = this.parser.optionsSpan.l === 0;
|
||||
this.done = this.parser.optionsSpan.len === 0;
|
||||
if ( this.done ) {
|
||||
this.value = undefined;
|
||||
return this;
|
||||
|
@ -1981,7 +1981,7 @@ const NetOptionsIterator = class {
|
|||
// At index 0 is the option descriptor.
|
||||
// At indices 1-5 is a slice index.
|
||||
const lopts = this.parser.optionsSpan.i;
|
||||
const ropts = lopts + this.parser.optionsSpan.l;
|
||||
const ropts = lopts + this.parser.optionsSpan.len;
|
||||
const slices = this.parser.slices;
|
||||
const optSlices = this.optSlices;
|
||||
let typeCount = 0;
|
||||
|
@ -2206,12 +2206,12 @@ const PatternTokenIterator = class {
|
|||
this.done = true;
|
||||
}
|
||||
[Symbol.iterator]() {
|
||||
const { i, l } = this.parser.patternSpan;
|
||||
if ( l === 0 ) {
|
||||
const { i, len } = this.parser.patternSpan;
|
||||
if ( len === 0 ) {
|
||||
return this.end();
|
||||
}
|
||||
this.l = i;
|
||||
this.r = i + l;
|
||||
this.r = i + len;
|
||||
this.i = i;
|
||||
this.done = false;
|
||||
this.value = { token: '', pos: 0 };
|
||||
|
@ -2269,14 +2269,14 @@ const ExtOptionsIterator = class {
|
|||
this.done = true;
|
||||
}
|
||||
[Symbol.iterator]() {
|
||||
const { i, l } = this.parser.optionsSpan;
|
||||
if ( l === 0 ) {
|
||||
const { i, len } = this.parser.optionsSpan;
|
||||
if ( len === 0 ) {
|
||||
this.l = this.r = 0;
|
||||
this.done = true;
|
||||
this.value = undefined;
|
||||
} else {
|
||||
this.l = i;
|
||||
this.r = i + l;
|
||||
this.r = i + len;
|
||||
this.done = false;
|
||||
this.value = { hn: undefined, not: false, bad: false };
|
||||
}
|
||||
|
|
191
src/lib/codemirror/addon/edit/closebrackets.js
vendored
Normal file
191
src/lib/codemirror/addon/edit/closebrackets.js
vendored
Normal file
|
@ -0,0 +1,191 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
var defaults = {
|
||||
pairs: "()[]{}''\"\"",
|
||||
closeBefore: ")]}'\":;>",
|
||||
triples: "",
|
||||
explode: "[]{}"
|
||||
};
|
||||
|
||||
var Pos = CodeMirror.Pos;
|
||||
|
||||
CodeMirror.defineOption("autoCloseBrackets", false, function(cm, val, old) {
|
||||
if (old && old != CodeMirror.Init) {
|
||||
cm.removeKeyMap(keyMap);
|
||||
cm.state.closeBrackets = null;
|
||||
}
|
||||
if (val) {
|
||||
ensureBound(getOption(val, "pairs"))
|
||||
cm.state.closeBrackets = val;
|
||||
cm.addKeyMap(keyMap);
|
||||
}
|
||||
});
|
||||
|
||||
function getOption(conf, name) {
|
||||
if (name == "pairs" && typeof conf == "string") return conf;
|
||||
if (typeof conf == "object" && conf[name] != null) return conf[name];
|
||||
return defaults[name];
|
||||
}
|
||||
|
||||
var keyMap = {Backspace: handleBackspace, Enter: handleEnter};
|
||||
function ensureBound(chars) {
|
||||
for (var i = 0; i < chars.length; i++) {
|
||||
var ch = chars.charAt(i), key = "'" + ch + "'"
|
||||
if (!keyMap[key]) keyMap[key] = handler(ch)
|
||||
}
|
||||
}
|
||||
ensureBound(defaults.pairs + "`")
|
||||
|
||||
function handler(ch) {
|
||||
return function(cm) { return handleChar(cm, ch); };
|
||||
}
|
||||
|
||||
function getConfig(cm) {
|
||||
var deflt = cm.state.closeBrackets;
|
||||
if (!deflt || deflt.override) return deflt;
|
||||
var mode = cm.getModeAt(cm.getCursor());
|
||||
return mode.closeBrackets || deflt;
|
||||
}
|
||||
|
||||
function handleBackspace(cm) {
|
||||
var conf = getConfig(cm);
|
||||
if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||
|
||||
var pairs = getOption(conf, "pairs");
|
||||
var ranges = cm.listSelections();
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
if (!ranges[i].empty()) return CodeMirror.Pass;
|
||||
var around = charsAround(cm, ranges[i].head);
|
||||
if (!around || pairs.indexOf(around) % 2 != 0) return CodeMirror.Pass;
|
||||
}
|
||||
for (var i = ranges.length - 1; i >= 0; i--) {
|
||||
var cur = ranges[i].head;
|
||||
cm.replaceRange("", Pos(cur.line, cur.ch - 1), Pos(cur.line, cur.ch + 1), "+delete");
|
||||
}
|
||||
}
|
||||
|
||||
function handleEnter(cm) {
|
||||
var conf = getConfig(cm);
|
||||
var explode = conf && getOption(conf, "explode");
|
||||
if (!explode || cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||
|
||||
var ranges = cm.listSelections();
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
if (!ranges[i].empty()) return CodeMirror.Pass;
|
||||
var around = charsAround(cm, ranges[i].head);
|
||||
if (!around || explode.indexOf(around) % 2 != 0) return CodeMirror.Pass;
|
||||
}
|
||||
cm.operation(function() {
|
||||
var linesep = cm.lineSeparator() || "\n";
|
||||
cm.replaceSelection(linesep + linesep, null);
|
||||
cm.execCommand("goCharLeft");
|
||||
ranges = cm.listSelections();
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var line = ranges[i].head.line;
|
||||
cm.indentLine(line, null, true);
|
||||
cm.indentLine(line + 1, null, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function contractSelection(sel) {
|
||||
var inverted = CodeMirror.cmpPos(sel.anchor, sel.head) > 0;
|
||||
return {anchor: new Pos(sel.anchor.line, sel.anchor.ch + (inverted ? -1 : 1)),
|
||||
head: new Pos(sel.head.line, sel.head.ch + (inverted ? 1 : -1))};
|
||||
}
|
||||
|
||||
function handleChar(cm, ch) {
|
||||
var conf = getConfig(cm);
|
||||
if (!conf || cm.getOption("disableInput")) return CodeMirror.Pass;
|
||||
|
||||
var pairs = getOption(conf, "pairs");
|
||||
var pos = pairs.indexOf(ch);
|
||||
if (pos == -1) return CodeMirror.Pass;
|
||||
|
||||
var closeBefore = getOption(conf,"closeBefore");
|
||||
|
||||
var triples = getOption(conf, "triples");
|
||||
|
||||
var identical = pairs.charAt(pos + 1) == ch;
|
||||
var ranges = cm.listSelections();
|
||||
var opening = pos % 2 == 0;
|
||||
|
||||
var type;
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var range = ranges[i], cur = range.head, curType;
|
||||
var next = cm.getRange(cur, Pos(cur.line, cur.ch + 1));
|
||||
if (opening && !range.empty()) {
|
||||
curType = "surround";
|
||||
} else if ((identical || !opening) && next == ch) {
|
||||
if (identical && stringStartsAfter(cm, cur))
|
||||
curType = "both";
|
||||
else if (triples.indexOf(ch) >= 0 && cm.getRange(cur, Pos(cur.line, cur.ch + 3)) == ch + ch + ch)
|
||||
curType = "skipThree";
|
||||
else
|
||||
curType = "skip";
|
||||
} else if (identical && cur.ch > 1 && triples.indexOf(ch) >= 0 &&
|
||||
cm.getRange(Pos(cur.line, cur.ch - 2), cur) == ch + ch) {
|
||||
if (cur.ch > 2 && /\bstring/.test(cm.getTokenTypeAt(Pos(cur.line, cur.ch - 2)))) return CodeMirror.Pass;
|
||||
curType = "addFour";
|
||||
} else if (identical) {
|
||||
var prev = cur.ch == 0 ? " " : cm.getRange(Pos(cur.line, cur.ch - 1), cur)
|
||||
if (!CodeMirror.isWordChar(next) && prev != ch && !CodeMirror.isWordChar(prev)) curType = "both";
|
||||
else return CodeMirror.Pass;
|
||||
} else if (opening && (next.length === 0 || /\s/.test(next) || closeBefore.indexOf(next) > -1)) {
|
||||
curType = "both";
|
||||
} else {
|
||||
return CodeMirror.Pass;
|
||||
}
|
||||
if (!type) type = curType;
|
||||
else if (type != curType) return CodeMirror.Pass;
|
||||
}
|
||||
|
||||
var left = pos % 2 ? pairs.charAt(pos - 1) : ch;
|
||||
var right = pos % 2 ? ch : pairs.charAt(pos + 1);
|
||||
cm.operation(function() {
|
||||
if (type == "skip") {
|
||||
cm.execCommand("goCharRight");
|
||||
} else if (type == "skipThree") {
|
||||
for (var i = 0; i < 3; i++)
|
||||
cm.execCommand("goCharRight");
|
||||
} else if (type == "surround") {
|
||||
var sels = cm.getSelections();
|
||||
for (var i = 0; i < sels.length; i++)
|
||||
sels[i] = left + sels[i] + right;
|
||||
cm.replaceSelections(sels, "around");
|
||||
sels = cm.listSelections().slice();
|
||||
for (var i = 0; i < sels.length; i++)
|
||||
sels[i] = contractSelection(sels[i]);
|
||||
cm.setSelections(sels);
|
||||
} else if (type == "both") {
|
||||
cm.replaceSelection(left + right, null);
|
||||
cm.triggerElectric(left + right);
|
||||
cm.execCommand("goCharLeft");
|
||||
} else if (type == "addFour") {
|
||||
cm.replaceSelection(left + left + left + left, "before");
|
||||
cm.execCommand("goCharRight");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function charsAround(cm, pos) {
|
||||
var str = cm.getRange(Pos(pos.line, pos.ch - 1),
|
||||
Pos(pos.line, pos.ch + 1));
|
||||
return str.length == 2 ? str : null;
|
||||
}
|
||||
|
||||
function stringStartsAfter(cm, pos) {
|
||||
var token = cm.getTokenAt(Pos(pos.line, pos.ch + 1))
|
||||
return /\bstring/.test(token.type) && token.start == pos.ch &&
|
||||
(pos.ch == 0 || !/\bstring/.test(cm.getTokenTypeAt(pos)))
|
||||
}
|
||||
});
|
150
src/lib/codemirror/addon/edit/matchbrackets.js
vendored
Normal file
150
src/lib/codemirror/addon/edit/matchbrackets.js
vendored
Normal file
|
@ -0,0 +1,150 @@
|
|||
// CodeMirror, copyright (c) by Marijn Haverbeke and others
|
||||
// Distributed under an MIT license: https://codemirror.net/LICENSE
|
||||
|
||||
(function(mod) {
|
||||
if (typeof exports == "object" && typeof module == "object") // CommonJS
|
||||
mod(require("../../lib/codemirror"));
|
||||
else if (typeof define == "function" && define.amd) // AMD
|
||||
define(["../../lib/codemirror"], mod);
|
||||
else // Plain browser env
|
||||
mod(CodeMirror);
|
||||
})(function(CodeMirror) {
|
||||
var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
|
||||
(document.documentMode == null || document.documentMode < 8);
|
||||
|
||||
var Pos = CodeMirror.Pos;
|
||||
|
||||
var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"};
|
||||
|
||||
function bracketRegex(config) {
|
||||
return config && config.bracketRegex || /[(){}[\]]/
|
||||
}
|
||||
|
||||
function findMatchingBracket(cm, where, config) {
|
||||
var line = cm.getLineHandle(where.line), pos = where.ch - 1;
|
||||
var afterCursor = config && config.afterCursor
|
||||
if (afterCursor == null)
|
||||
afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className)
|
||||
var re = bracketRegex(config)
|
||||
|
||||
// A cursor is defined as between two characters, but in in vim command mode
|
||||
// (i.e. not insert mode), the cursor is visually represented as a
|
||||
// highlighted box on top of the 2nd character. Otherwise, we allow matches
|
||||
// from before or after the cursor.
|
||||
var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) ||
|
||||
re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)];
|
||||
if (!match) return null;
|
||||
var dir = match.charAt(1) == ">" ? 1 : -1;
|
||||
if (config && config.strict && (dir > 0) != (pos == where.ch)) return null;
|
||||
var style = cm.getTokenTypeAt(Pos(where.line, pos + 1));
|
||||
|
||||
var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style || null, config);
|
||||
if (found == null) return null;
|
||||
return {from: Pos(where.line, pos), to: found && found.pos,
|
||||
match: found && found.ch == match.charAt(0), forward: dir > 0};
|
||||
}
|
||||
|
||||
// bracketRegex is used to specify which type of bracket to scan
|
||||
// should be a regexp, e.g. /[[\]]/
|
||||
//
|
||||
// Note: If "where" is on an open bracket, then this bracket is ignored.
|
||||
//
|
||||
// Returns false when no bracket was found, null when it reached
|
||||
// maxScanLines and gave up
|
||||
function scanForBracket(cm, where, dir, style, config) {
|
||||
var maxScanLen = (config && config.maxScanLineLength) || 10000;
|
||||
var maxScanLines = (config && config.maxScanLines) || 1000;
|
||||
|
||||
var stack = [];
|
||||
var re = bracketRegex(config)
|
||||
var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1)
|
||||
: Math.max(cm.firstLine() - 1, where.line - maxScanLines);
|
||||
for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
|
||||
var line = cm.getLine(lineNo);
|
||||
if (!line) continue;
|
||||
var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1;
|
||||
if (line.length > maxScanLen) continue;
|
||||
if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0);
|
||||
for (; pos != end; pos += dir) {
|
||||
var ch = line.charAt(pos);
|
||||
if (re.test(ch) && (style === undefined || cm.getTokenTypeAt(Pos(lineNo, pos + 1)) == style)) {
|
||||
var match = matching[ch];
|
||||
if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch);
|
||||
else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch};
|
||||
else stack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
|
||||
}
|
||||
|
||||
function matchBrackets(cm, autoclear, config) {
|
||||
// Disable brace matching in long lines, since it'll cause hugely slow updates
|
||||
var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000;
|
||||
var marks = [], ranges = cm.listSelections();
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config);
|
||||
if (match && cm.getLine(match.from.line).length <= maxHighlightLen) {
|
||||
var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
|
||||
marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style}));
|
||||
if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen)
|
||||
marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style}));
|
||||
}
|
||||
}
|
||||
|
||||
if (marks.length) {
|
||||
// Kludge to work around the IE bug from issue #1193, where text
|
||||
// input stops going to the textare whever this fires.
|
||||
if (ie_lt8 && cm.state.focused) cm.focus();
|
||||
|
||||
var clear = function() {
|
||||
cm.operation(function() {
|
||||
for (var i = 0; i < marks.length; i++) marks[i].clear();
|
||||
});
|
||||
};
|
||||
if (autoclear) setTimeout(clear, 800);
|
||||
else return clear;
|
||||
}
|
||||
}
|
||||
|
||||
function doMatchBrackets(cm) {
|
||||
cm.operation(function() {
|
||||
if (cm.state.matchBrackets.currentlyHighlighted) {
|
||||
cm.state.matchBrackets.currentlyHighlighted();
|
||||
cm.state.matchBrackets.currentlyHighlighted = null;
|
||||
}
|
||||
cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
|
||||
});
|
||||
}
|
||||
|
||||
CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
|
||||
if (old && old != CodeMirror.Init) {
|
||||
cm.off("cursorActivity", doMatchBrackets);
|
||||
if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
|
||||
cm.state.matchBrackets.currentlyHighlighted();
|
||||
cm.state.matchBrackets.currentlyHighlighted = null;
|
||||
}
|
||||
}
|
||||
if (val) {
|
||||
cm.state.matchBrackets = typeof val == "object" ? val : {};
|
||||
cm.on("cursorActivity", doMatchBrackets);
|
||||
}
|
||||
});
|
||||
|
||||
CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
|
||||
CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){
|
||||
// Backwards-compatibility kludge
|
||||
if (oldConfig || typeof config == "boolean") {
|
||||
if (!oldConfig) {
|
||||
config = config ? {strict: true} : null
|
||||
} else {
|
||||
oldConfig.strict = config
|
||||
config = oldConfig
|
||||
}
|
||||
}
|
||||
return findMatchingBracket(this, pos, config)
|
||||
});
|
||||
CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){
|
||||
return scanForBracket(this, pos, dir, style, config);
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue