Add syntax highlighting/auto-completion for preparsing directives · gorhill/uBlock@83c01fb (original) (raw)

`@@ -25,17 +25,53 @@

`

25

25

``

26

26

`/******************************************************************************/

`

27

27

``

``

28

`+

{

`

``

29

`+

// >>>>> start of local scope

`

``

30

+

``

31

`+

/******************************************************************************/

`

``

32

+

``

33

`+

const redirectNames = new Map();

`

``

34

`+

const scriptletNames = new Map();

`

``

35

`+

const preparseDirectiveNames = new Set();

`

``

36

+

``

37

`+

/******************************************************************************/

`

``

38

+

28

39

`CodeMirror.defineMode('ubo-static-filtering', function() {

`

29

40

`const StaticFilteringParser = typeof vAPI === 'object'

`

30

41

` ? vAPI.StaticFilteringParser

`

31

42

` : self.StaticFilteringParser;

`

32

43

`if ( StaticFilteringParser instanceof Object === false ) { return; }

`

33

44

`const parser = new StaticFilteringParser({ interactive: true });

`

34

45

``

35

``

`-

const reDirective = /^!#(?:if|endif|include)\b/;

`

``

46

`+

const rePreparseDirectives = /^!#(?:if|endif|include)\b/;

`

``

47

`+

const rePreparseIfDirective = /^(!#if !?)(.+)$/;

`

36

48

`let parserSlot = 0;

`

37

49

`let netOptionValueMode = false;

`

38

50

``

``

51

`+

const colorCommentSpan = function(stream) {

`

``

52

`+

if ( rePreparseDirectives.test(stream.string) === false ) {

`

``

53

`+

stream.skipToEnd();

`

``

54

`+

return 'comment';

`

``

55

`+

}

`

``

56

`+

const match = rePreparseIfDirective.exec(stream.string);

`

``

57

`+

if ( match === null ) {

`

``

58

`+

stream.skipToEnd();

`

``

59

`+

return 'variable strong';

`

``

60

`+

}

`

``

61

`+

if ( stream.pos < match[1].length ) {

`

``

62

`+

stream.pos = match[1].length;

`

``

63

`+

return 'variable strong';

`

``

64

`+

}

`

``

65

`+

stream.skipToEnd();

`

``

66

`+

if (

`

``

67

`+

preparseDirectiveNames.size === 0 ||

`

``

68

`+

preparseDirectiveNames.has(match[2].trim())

`

``

69

`+

) {

`

``

70

`+

return 'variable strong';

`

``

71

`+

}

`

``

72

`+

return 'error strong';

`

``

73

`+

};

`

``

74

+

39

75

`const colorExtHTMLPatternSpan = function(stream) {

`

40

76

`const { i } = parser.patternSpan;

`

41

77

`if ( stream.pos === parser.slices[i+1] ) {

`

`@@ -202,10 +238,7 @@ CodeMirror.defineMode('ubo-static-filtering', function() {

`

202

238

`return 'comment';

`

203

239

`}

`

204

240

`if ( parser.category === parser.CATComment ) {

`

205

``

`-

stream.skipToEnd();

`

206

``

`-

return reDirective.test(stream.string)

`

207

``

`-

? 'variable strong'

`

208

``

`-

: 'comment';

`

``

241

`+

return colorCommentSpan(stream);

`

209

242

`}

`

210

243

`if ( (parser.slices[parserSlot] & parser.BITIgnore) !== 0 ) {

`

211

244

`stream.pos += parser.slices[parserSlot+2];

`

`@@ -243,6 +276,23 @@ CodeMirror.defineMode('ubo-static-filtering', function() {

`

243

276

`style = style.trim();

`

244

277

`return style !== '' ? style : null;

`

245

278

`},

`

``

279

`+

setHints: function(details) {

`

``

280

`+

for ( const [ name, desc ] of details.redirectResources ) {

`

``

281

`+

const displayText = desc.aliasOf !== ''

`

``

282

`` +

? ${name} (${desc.aliasOf})

``

``

283

`+

: '';

`

``

284

`+

if ( desc.canRedirect ) {

`

``

285

`+

redirectNames.set(name, displayText);

`

``

286

`+

}

`

``

287

`+

if ( desc.canInject && name.endsWith('.js') ) {

`

``

288

`+

scriptletNames.set(name.slice(0, -3), displayText);

`

``

289

`+

}

`

``

290

`+

}

`

``

291

`+

details.preparseDirectives.forEach(a => {

`

``

292

`+

preparseDirectiveNames.add(a);

`

``

293

`+

});

`

``

294

`+

initHints();

`

``

295

`+

},

`

246

296

`};

`

247

297

`});

`

248

298

``

`@@ -251,17 +301,13 @@ CodeMirror.defineMode('ubo-static-filtering', function() {

`

251

301

`// Following code is for auto-completion. Reference:

`

252

302

`// https://codemirror.net/demo/complete.html

`

253

303

``

254

``

`-

(( ) => {

`

255

``

`-

if ( typeof vAPI !== 'object' ) { return; }

`

256

``

-

``

304

`+

const initHints = function() {

`

257

305

`const StaticFilteringParser = typeof vAPI === 'object'

`

258

306

` ? vAPI.StaticFilteringParser

`

259

307

` : self.StaticFilteringParser;

`

260

308

`if ( StaticFilteringParser instanceof Object === false ) { return; }

`

261

309

``

262

310

`const parser = new StaticFilteringParser();

`

263

``

`-

const redirectNames = new Map();

`

264

``

`-

const scriptletNames = new Map();

`

265

311

`const proceduralOperatorNames = new Map(

`

266

312

`Array.from(parser.proceduralOperatorTokens).filter(item => {

`

267

313

`return (item[1] & 0b01) !== 0;

`

`@@ -380,7 +426,28 @@ CodeMirror.defineMode('ubo-static-filtering', function() {

`

380

426

`return pickBestHints(cursor, matchLeft[1], matchRight[1], hints);

`

381

427

`};

`

382

428

``

383

``

`-

const getHints = function(cm) {

`

``

429

`+

const getCommentHints = function(cursor, line) {

`

``

430

`+

const beg = cursor.ch;

`

``

431

`+

if ( line.startsWith('!#if ') ) {

`

``

432

`+

const matchLeft = /^!#if !?(\w*)$/.exec(line.slice(0, beg));

`

``

433

`+

const matchRight = /^\w*/.exec(line.slice(beg));

`

``

434

`+

if ( matchLeft === null || matchRight === null ) { return; }

`

``

435

`+

const hints = [];

`

``

436

`+

for ( const hint of preparseDirectiveNames ) {

`

``

437

`+

hints.push(hint);

`

``

438

`+

}

`

``

439

`+

return pickBestHints(cursor, matchLeft[1], matchRight[0], hints);

`

``

440

`+

}

`

``

441

`+

if ( line.startsWith('!#') && line !== '!#endif' ) {

`

``

442

`+

const matchLeft = /^!#(\w*)$/.exec(line.slice(0, beg));

`

``

443

`+

const matchRight = /^\w*/.exec(line.slice(beg));

`

``

444

`+

if ( matchLeft === null || matchRight === null ) { return; }

`

``

445

`+

const hints = [ 'if ', 'endif\n', 'include ' ];

`

``

446

`+

return pickBestHints(cursor, matchLeft[1], matchRight[0], hints);

`

``

447

`+

}

`

``

448

`+

};

`

``

449

+

``

450

`+

CodeMirror.registerHelper('hint', 'ubo-static-filtering', function(cm) {

`

384

451

`const cursor = cm.getCursor();

`

385

452

`const line = cm.getLine(cursor.line);

`

386

453

`parser.analyze(line);

`

`@@ -393,25 +460,15 @@ CodeMirror.defineMode('ubo-static-filtering', function() {

`

393

460

`if ( parser.category === parser.CATStaticNetFilter ) {

`

394

461

`return getNetHints(cursor, line);

`

395

462

`}

`

396

``

`-

};

`

397

``

-

398

``

`-

vAPI.messaging.send('dashboard', {

`

399

``

`-

what: 'getResourceDetails'

`

400

``

`-

}).then(response => {

`

401

``

`-

if ( Array.isArray(response) === false ) { return; }

`

402

``

`-

for ( const [ name, details ] of response ) {

`

403

``

`-

const displayText = details.aliasOf !== ''

`

404

``

`` -

? ${name} (${details.aliasOf})

``

405

``

`-

: '';

`

406

``

`-

if ( details.canRedirect ) {

`

407

``

`-

redirectNames.set(name, displayText);

`

408

``

`-

}

`

409

``

`-

if ( details.canInject && name.endsWith('.js') ) {

`

410

``

`-

scriptletNames.set(name.slice(0, -3), displayText);

`

411

``

`-

}

`

``

463

`+

if ( parser.category === parser.CATComment ) {

`

``

464

`+

return getCommentHints(cursor, line);

`

412

465

`}

`

413

``

`-

CodeMirror.registerHelper('hint', 'ubo-static-filtering', getHints);

`

414

466

`});

`

415

``

`-

})();

`

``

467

`+

};

`

``

468

+

``

469

`+

/******************************************************************************/

`

``

470

+

``

471

`+

// <<<<< end of local scope

`

``

472

`+

}

`

416

473

``

417

474

`/******************************************************************************/

`