@bhsd/codemirror-mediawiki 2.1.9 → 2.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/mw/dist/base.js CHANGED
@@ -1,7 +1,7 @@
1
- import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror-mediawiki@2.1.9/dist/main.min.js';
1
+ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror-mediawiki@2.1.11/dist/main.min.js';
2
2
  (() => {
3
3
  var _a;
4
- mw.loader.load('https://testingcf.jsdelivr.net/npm/@bhsd/codemirror-mediawiki@2.1.9/mediawiki.min.css', 'text/css');
4
+ mw.loader.load('https://testingcf.jsdelivr.net/npm/@bhsd/codemirror-mediawiki@2.1.11/mediawiki.min.css', 'text/css');
5
5
  mw.loader.addStyleTag('.wikiEditor-ui-toolbar{z-index:7}');
6
6
  const instances = new WeakMap();
7
7
  const getInstance = ($ele) => instances.get($ele[0]);
@@ -55,7 +55,7 @@ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror
55
55
  return this;
56
56
  },
57
57
  };
58
- const USING_LOCAL = mw.loader.getState('ext.CodeMirror') !== null, DATA_MODULE = mw.loader.getState('ext.CodeMirror.data') ? 'ext.CodeMirror.data' : 'ext.CodeMirror', ALL_SETTINGS_CACHE = (_a = JSON.parse(localStorage.getItem('InPageEditMwConfig'))) !== null && _a !== void 0 ? _a : {}, SITE_ID = `${mw.config.get('wgServerName')}${mw.config.get('wgScriptPath')}`, SITE_SETTINGS = ALL_SETTINGS_CACHE[SITE_ID], VALID = (SITE_SETTINGS === null || SITE_SETTINGS === void 0 ? void 0 : SITE_SETTINGS.time) > Date.now() - 86400 * 1000 * 30;
58
+ const USING_LOCAL = mw.loader.getState('ext.CodeMirror') !== null, DATA_MODULE = mw.loader.getState('ext.CodeMirror.data') ? 'ext.CodeMirror.data' : 'ext.CodeMirror', ALL_SETTINGS_CACHE = (_a = JSON.parse(localStorage.getItem('InPageEditMwConfig'))) !== null && _a !== void 0 ? _a : {}, SITE_ID = `${mw.config.get('wgServerName')}${mw.config.get('wgScriptPath')}`, SITE_SETTINGS = ALL_SETTINGS_CACHE[SITE_ID], VALID = Number(SITE_SETTINGS === null || SITE_SETTINGS === void 0 ? void 0 : SITE_SETTINGS.time) > Date.now() - 86400 * 1000 * 30;
59
59
  const getConfig = (magicWords, rule, flip) => {
60
60
  const words = magicWords.filter(rule).filter(({ 'case-sensitive': i }) => i !== flip)
61
61
  .flatMap(({ aliases, name, 'case-sensitive': i }) => aliases.map(alias => ({
@@ -166,7 +166,7 @@ import { CodeMirror6 } from 'https://testingcf.jsdelivr.net/npm/@bhsd/codemirror
166
166
  const { lang } = this;
167
167
  let opt;
168
168
  if (typeof optOrNs === 'number') {
169
- if (lang === 'mediawiki' && (optOrNs === 10 || optOrNs === 828)) {
169
+ if (lang === 'mediawiki' && (optOrNs === 10 || optOrNs === 828 || optOrNs === 2)) {
170
170
  opt = { include: true };
171
171
  }
172
172
  else if (lang === 'javascript' && (optOrNs === 8 || optOrNs === 2300)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bhsd/codemirror-mediawiki",
3
- "version": "2.1.9",
3
+ "version": "2.1.11",
4
4
  "description": "Modified CodeMirror mode based on wikimedia/mediawiki-extensions-CodeMirror",
5
5
  "keywords": [
6
6
  "mediawiki",
@@ -28,14 +28,15 @@
28
28
  "url": "git+https://github.com/bhsd-harry/codemirror-mediawiki.git"
29
29
  },
30
30
  "scripts": {
31
- "build:core": "esbuild ./src/codemirror.ts --bundle --minify --target=es2018 --format=esm --sourcemap --outfile=dist/main.min.js; tsc --emitDeclarationOnly; rm dist/gh-page.d.ts",
31
+ "build:core": "esbuild ./src/codemirror.ts --bundle --minify --target=es2018 --format=esm --sourcemap --outfile=dist/main.min.js && tsc --emitDeclarationOnly && rm dist/gh-page.d.ts",
32
32
  "build:mw": "bash build.sh mw/base.ts mw/dist/base.js",
33
33
  "build:gh-page": "bash build.sh src/gh-page.ts gh-page.js",
34
34
  "build": "npm run build:core && npm run build:mw",
35
35
  "lint:ts": "tsc --noEmit && tsc --project mw/tsconfig.json --noEmit && eslint --cache .",
36
36
  "lint:css": "stylelint *.css",
37
37
  "lint": "npm run lint:ts && npm run lint:css",
38
- "test": "http-server .. -c-1 --cors &",
38
+ "server": "http-server .. -c-1 --cors &",
39
+ "test": "npm run build:core && npm run build:gh-page && npm run server",
39
40
  "test:end": "pkill -x http-server"
40
41
  },
41
42
  "engines": {
package/src/codemirror.ts CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  highlightActiveLine,
8
8
  highlightWhitespace,
9
9
  highlightTrailingWhitespace,
10
+ drawSelection,
10
11
  } from '@codemirror/view';
11
12
  import {
12
13
  syntaxHighlighting,
@@ -52,6 +53,13 @@ const avail: Record<string, [(config?: any) => Extension, Record<string, unknown
52
53
  highlightTrailingWhitespace: [highlightTrailingWhitespace, {}],
53
54
  bracketMatching: [bracketMatching, {mediawiki: {brackets: '[]{}'}}],
54
55
  closeBrackets: [closeBrackets, {}],
56
+ allowMultipleSelections: [
57
+ (): Extension => [
58
+ EditorState.allowMultipleSelections.of(true),
59
+ drawSelection(),
60
+ ],
61
+ {},
62
+ ],
55
63
  };
56
64
 
57
65
  /**
@@ -260,8 +268,12 @@ export class CodeMirror6 {
260
268
  this.#view.dispatch({
261
269
  effects: [
262
270
  this.#extensions.reconfigure(names.map(name => {
263
- const [extension, configs] = avail[name]!;
264
- return extension(configs[this.#lang]);
271
+ const option = avail[name];
272
+ if (option) {
273
+ const [extension, configs] = option;
274
+ return extension(configs[this.#lang]);
275
+ }
276
+ return [];
265
277
  })),
266
278
  ],
267
279
  });
package/src/mediawiki.ts CHANGED
@@ -48,6 +48,12 @@ export interface MwConfig {
48
48
  implicitlyClosedHtmlTags?: string[];
49
49
  }
50
50
 
51
+ const enum TableCell {
52
+ Td,
53
+ Th,
54
+ Caption,
55
+ }
56
+
51
57
  const copyState = (state: State): State => {
52
58
  const newState = {} as State;
53
59
  for (const [key, val] of Object.entries(state)) {
@@ -85,7 +91,7 @@ class MediaWiki {
85
91
  constructor(config: MwConfig) {
86
92
  this.config = config;
87
93
  // eslint-disable-next-line require-unicode-regexp
88
- this.urlProtocols = new RegExp(`^(?:${config.urlProtocols})`, 'i');
94
+ this.urlProtocols = new RegExp(`^(?:${config.urlProtocols})(?=[^\\s[\\]<>])`, 'i');
89
95
  this.isBold = false;
90
96
  this.wasBold = false;
91
97
  this.isItalic = false;
@@ -145,7 +151,8 @@ class MediaWiki {
145
151
  }));
146
152
  }
147
153
 
148
- eatHtmlEntity(stream: StringStream, style: string): string { // eslint-disable-line class-methods-use-this
154
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
155
+ eatHtmlEntity(stream: StringStream, style: string): string {
149
156
  const entity = stream.match(/^(?:#x[a-f\d]+|#\d+|[a-z\d]+);/iu) as RegExpMatchArray | false;
150
157
  return entity && isHtmlEntity(`&${entity[0]}`) ? modeConfig.tags.htmlEntity : style;
151
158
  }
@@ -158,7 +165,7 @@ class MediaWiki {
158
165
  );
159
166
  }
160
167
 
161
- // eslint-disable-next-line class-methods-use-this
168
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
162
169
  makeLocalStyle(style: string, state: State, endGround?: 'nTemplate' | 'nLink' | 'nExt'): string {
163
170
  let ground = '';
164
171
 
@@ -235,7 +242,7 @@ class MediaWiki {
235
242
  return (ground && `mw${ground}-ground `) + style;
236
243
  }
237
244
 
238
- inBlock(style: string, terminator: string, consumeLast = true): Tokenizer {
245
+ inBlock(style: string, terminator: string, consumeLast?: boolean): Tokenizer {
239
246
  return (stream, state) => {
240
247
  if (stream.skipTo(terminator)) {
241
248
  if (consumeLast) {
@@ -278,7 +285,7 @@ class MediaWiki {
278
285
  } else if (stream.match(/^<!--(?!.*?-->.*?=)/u, false)) {
279
286
  // T171074: handle trailing comments
280
287
  stream.backUp(count);
281
- state.tokenize = this.inBlock(modeConfig.tags.sectionHeader, '<!--', false);
288
+ state.tokenize = this.inBlock(modeConfig.tags.sectionHeader, '<!--');
282
289
  }
283
290
  return this.makeLocalStyle(modeConfig.tags.section, state);
284
291
  }
@@ -303,7 +310,7 @@ class MediaWiki {
303
310
  return this.makeLocalStyle(modeConfig.tags.templateVariableName, state);
304
311
  }
305
312
 
306
- inVariableDefault(isFirst: boolean): Tokenizer {
313
+ inVariableDefault(isFirst?: boolean): Tokenizer {
307
314
  const style = modeConfig.tags[isFirst ? 'templateVariable' : 'comment'];
308
315
  return (stream, state) => {
309
316
  if (stream.match(/^[^{}[<&~|]+/u)) {
@@ -312,7 +319,7 @@ class MediaWiki {
312
319
  state.tokenize = state.stack.pop()!;
313
320
  return this.makeLocalStyle(modeConfig.tags.templateVariableBracket, state);
314
321
  } else if (stream.eat('|')) {
315
- state.tokenize = this.inVariableDefault(false);
322
+ state.tokenize = this.inVariableDefault();
316
323
  return this.makeLocalStyle(modeConfig.tags.templateVariableDelimiter, state);
317
324
  }
318
325
  return this.eatWikiText(style)(stream, state);
@@ -345,7 +352,7 @@ class MediaWiki {
345
352
  return this.eatWikiText(modeConfig.tags.parserFunction)(stream, state);
346
353
  }
347
354
 
348
- inTemplatePageName(haveAte: boolean): Tokenizer {
355
+ inTemplatePageName(haveAte?: boolean): Tokenizer {
349
356
  return (stream, state) => {
350
357
  if (stream.match(/^\s*\|\s*/u)) {
351
358
  state.tokenize = this.inTemplateArgument(true);
@@ -372,11 +379,11 @@ class MediaWiki {
372
379
  };
373
380
  }
374
381
 
375
- inTemplateArgument(expectArgName: boolean): Tokenizer {
382
+ inTemplateArgument(expectArgName?: boolean): Tokenizer {
376
383
  return (stream, state) => {
377
384
  if (expectArgName && stream.eatWhile(/[^=|}{[<&~]/u)) {
378
385
  if (stream.eat('=')) {
379
- state.tokenize = this.inTemplateArgument(false);
386
+ state.tokenize = this.inTemplateArgument();
380
387
  return this.makeLocalStyle(modeConfig.tags.templateArgumentName, state);
381
388
  }
382
389
  return this.makeLocalStyle(modeConfig.tags.template, state);
@@ -531,7 +538,7 @@ class MediaWiki {
531
538
  };
532
539
  }
533
540
 
534
- eatTagName(chars: number, isCloseTag: boolean, isHtmlTag: boolean): Tokenizer {
541
+ eatTagName(chars: number, isCloseTag: boolean, isHtmlTag?: boolean): Tokenizer {
535
542
  return (stream, state) => {
536
543
  let name = '';
537
544
  for (let i = 0; i < chars; i++) {
@@ -613,8 +620,7 @@ class MediaWiki {
613
620
  eatExtTagArea(name: string): Tokenizer {
614
621
  return (stream, state) => {
615
622
  const from = stream.pos,
616
- pattern = new RegExp(`</${name}\\s*(?:>|$)`, 'iu'),
617
- m = pattern.exec(from ? stream.string.slice(from) : stream.string);
623
+ m = new RegExp(`</${name}\\s*(?:>|$)`, 'iu').exec(from ? stream.string.slice(from) : stream.string);
618
624
  let origString: string | false = false;
619
625
 
620
626
  if (m) {
@@ -627,9 +633,8 @@ class MediaWiki {
627
633
  }
628
634
  return state.tokenize(stream, state);
629
635
  }
630
- const to = m.index + from;
631
636
  origString = stream.string;
632
- stream.string = origString.slice(0, to);
637
+ stream.string = origString.slice(0, m.index + from);
633
638
  }
634
639
 
635
640
  state.stack.push(state.tokenize);
@@ -642,12 +647,13 @@ class MediaWiki {
642
647
  return (stream, state) => {
643
648
  stream.next(); // eat <
644
649
  stream.next(); // eat /
645
- state.tokenize = this.eatTagName(name.length, true, false);
650
+ state.tokenize = this.eatTagName(name.length, true);
646
651
  return this.makeLocalStyle(modeConfig.tags.extTagBracket, state);
647
652
  };
648
653
  }
649
654
 
650
- inExtTokens(origString: string | false): Tokenizer { // eslint-disable-line class-methods-use-this
655
+ // eslint-disable-next-line @typescript-eslint/class-methods-use-this
656
+ inExtTokens(origString: string | false): Tokenizer {
651
657
  return (stream, state) => {
652
658
  let ret: string;
653
659
  if (state.extMode === false) {
@@ -680,17 +686,6 @@ class MediaWiki {
680
686
  return this.eatWikiText(modeConfig.tags.tableDefinition)(stream, state);
681
687
  }
682
688
 
683
- inTableCaption(stream: StringStream, state: State): string {
684
- if (stream.sol() && stream.match(/^\s*(?:[|!]|\{\{\s*!\s*\}\})/u, false)) {
685
- state.tokenize = this.inTable.bind(this);
686
- return this.inTable(stream, state);
687
- } else if (stream.match(/^\s*(?:\||\{\{\s*!\s*\}\}){2}/u, false)) {
688
- state.tokenize = this.inTableRow(false, false);
689
- return '';
690
- }
691
- return this.eatWikiText(modeConfig.tags.tableCaption)(stream, state);
692
- }
693
-
694
689
  inTable(stream: StringStream, state: State): string {
695
690
  if (stream.sol()) {
696
691
  stream.eatSpace();
@@ -700,25 +695,31 @@ class MediaWiki {
700
695
  return this.makeLocalStyle(modeConfig.tags.tableDelimiter, state);
701
696
  } else if (stream.eat('+')) {
702
697
  stream.eatSpace();
703
- state.tokenize = this.inTableCaption.bind(this);
698
+ state.tokenize = this.inTableRow(true, TableCell.Caption);
704
699
  return this.makeLocalStyle(modeConfig.tags.tableDelimiter, state);
705
700
  } else if (stream.eat('}')) {
706
701
  state.tokenize = state.stack.pop()!;
707
702
  return this.makeLocalStyle(modeConfig.tags.tableBracket, state);
708
703
  }
709
704
  stream.eatSpace();
710
- state.tokenize = this.inTableRow(true, false);
705
+ state.tokenize = this.inTableRow(true, TableCell.Td);
711
706
  return this.makeLocalStyle(modeConfig.tags.tableDelimiter, state);
712
707
  } else if (stream.eat('!')) {
713
708
  stream.eatSpace();
714
- state.tokenize = this.inTableRow(true, true);
709
+ state.tokenize = this.inTableRow(true, TableCell.Th);
715
710
  return this.makeLocalStyle(modeConfig.tags.tableDelimiter, state);
716
711
  }
717
712
  }
718
713
  return this.eatWikiText('')(stream, state);
719
714
  }
720
715
 
721
- inTableRow(isStart: boolean, isHead: boolean): Tokenizer {
716
+ inTableRow(isStart: boolean, type: TableCell): Tokenizer {
717
+ let style = '';
718
+ if (type === TableCell.Caption) {
719
+ style = modeConfig.tags.tableCaption;
720
+ } else if (type === TableCell.Th) {
721
+ style = modeConfig.tags.strong;
722
+ }
722
723
  return (stream, state) => {
723
724
  if (stream.sol()) {
724
725
  if (stream.match(/^\s*(?:[|!]|\{\{\s*!\s*\}\})/u, false)) {
@@ -726,19 +727,17 @@ class MediaWiki {
726
727
  return this.inTable(stream, state);
727
728
  }
728
729
  } else if (stream.match(/^[^'|{[<&~!]+/u)) {
729
- return this.makeStyle(isHead ? modeConfig.tags.strong : '', state);
730
- } else if (
731
- stream.match(/^(?:\||\{\{\s*!\s*\}\}){2}/u) || isHead && stream.match('!!')
732
- || isStart && stream.match(/^(?:\||\{\{\s*!\s*\}\})/u)
733
- ) {
730
+ return this.makeStyle(style, state);
731
+ } else if (stream.match(/^(?:\||\{\{\s*!\s*\}\}){2}/u) || type === TableCell.Th && stream.match('!!')) {
734
732
  this.isBold = false;
735
733
  this.isItalic = false;
736
- if (isStart) {
737
- state.tokenize = this.inTableRow(false, isHead);
738
- }
734
+ state.tokenize = this.inTableRow(true, type);
735
+ return this.makeLocalStyle(modeConfig.tags.tableDelimiter, state);
736
+ } else if (isStart && stream.match(/^(?:\||\{\{\s*!\s*\}\})/u)) {
737
+ state.tokenize = this.inTableRow(false, type);
739
738
  return this.makeLocalStyle(modeConfig.tags.tableDelimiter, state);
740
739
  }
741
- return this.eatWikiText(isHead ? modeConfig.tags.strong : '')(stream, state);
740
+ return this.eatWikiText(style)(stream, state);
742
741
  };
743
742
  }
744
743
 
@@ -780,10 +779,11 @@ class MediaWiki {
780
779
 
781
780
  eatWikiText(style: string): Tokenizer {
782
781
  return (stream, state) => {
783
- let ch: string | void; // eslint-disable-line @typescript-eslint/no-invalid-void-type
784
- const sol = stream.sol();
782
+ let ch: string;
785
783
 
786
- if (sol) {
784
+ if (stream.eol()) {
785
+ return '';
786
+ } else if (stream.sol()) {
787
787
  if (stream.match('//')) {
788
788
  return this.makeStyle(style, state);
789
789
  // highlight free external links, see T108448
@@ -792,7 +792,7 @@ class MediaWiki {
792
792
  state.tokenize = this.inFreeExternalLink.bind(this);
793
793
  return this.makeStyle(modeConfig.tags.freeExtLinkProtocol, state);
794
794
  }
795
- ch = stream.next();
795
+ ch = stream.next()!;
796
796
  switch (ch) {
797
797
  case '-':
798
798
  if (stream.match(/^-{3,}/u)) {
@@ -864,7 +864,7 @@ class MediaWiki {
864
864
  // pass
865
865
  }
866
866
  } else {
867
- ch = stream.next();
867
+ ch = stream.next()!;
868
868
  }
869
869
 
870
870
  switch (ch) {
@@ -938,14 +938,14 @@ class MediaWiki {
938
938
  // Template
939
939
  state.nTemplate++;
940
940
  state.stack.push(state.tokenize);
941
- state.tokenize = this.inTemplatePageName(false);
941
+ state.tokenize = this.inTemplatePageName();
942
942
  return this.makeLocalStyle(modeConfig.tags.templateBracket, state);
943
943
  }
944
944
  break;
945
945
  case '<': {
946
946
  if (stream.match('!--')) { // comment
947
947
  state.stack.push(state.tokenize);
948
- state.tokenize = this.inBlock(modeConfig.tags.comment, '-->');
948
+ state.tokenize = this.inBlock(modeConfig.tags.comment, '-->', true);
949
949
  return this.makeLocalStyle(modeConfig.tags.comment, state);
950
950
  }
951
951
  const isCloseTag = Boolean(stream.eat('/')),
@@ -954,17 +954,18 @@ class MediaWiki {
954
954
  const tagname = mt[0]!.toLowerCase();
955
955
  if (tagname in this.config.tags) {
956
956
  // Parser function
957
+ state.stack.push(state.tokenize);
957
958
  if (isCloseTag) {
958
959
  state.tokenize = this.inChar('>', modeConfig.tags.error);
959
960
  return this.makeLocalStyle(modeConfig.tags.error, state);
960
961
  }
961
962
  stream.backUp(tagname.length);
962
- state.stack.push(state.tokenize);
963
- state.tokenize = this.eatTagName(tagname.length, isCloseTag, false);
963
+ state.tokenize = this.eatTagName(tagname.length, isCloseTag);
964
964
  return this.makeLocalStyle(modeConfig.tags.extTagBracket, state);
965
965
  } else if (this.permittedHtmlTags.has(tagname)) {
966
966
  // Html tag
967
967
  if (isCloseTag && tagname !== state.inHtmlTag.pop()) {
968
+ state.stack.push(state.tokenize);
968
969
  state.tokenize = this.inChar('>', modeConfig.tags.error);
969
970
  return this.makeLocalStyle(modeConfig.tags.error, state);
970
971
  } else if (isCloseTag && this.implicitlyClosedHtmlTags.has(tagname)) {
@@ -1101,12 +1102,7 @@ class MediaWiki {
1101
1102
  },
1102
1103
 
1103
1104
  token: (stream, state): string => {
1104
- let style: string,
1105
- p: number | null = null,
1106
- t: Token,
1107
- f: number | null,
1108
- tmpTokens: Token[] = [];
1109
- const readyTokens: Token[] = [];
1105
+ let t: Token;
1110
1106
 
1111
1107
  if (this.oldTokens.length > 0) {
1112
1108
  // just send saved tokens till they exists
@@ -1122,6 +1118,11 @@ class MediaWiki {
1122
1118
  this.firstSpace = null;
1123
1119
  }
1124
1120
 
1121
+ let style: string,
1122
+ p: number | null = null,
1123
+ f: number | null;
1124
+ const tmpTokens: Token[] = [],
1125
+ readyTokens: Token[] = [];
1125
1126
  do {
1126
1127
  // get token style
1127
1128
  style = state.tokenize(stream, state);
@@ -1135,7 +1136,7 @@ class MediaWiki {
1135
1136
  if (tmpTokens.length > 0) {
1136
1137
  // save tokens
1137
1138
  readyTokens.push(...tmpTokens);
1138
- tmpTokens = [];
1139
+ tmpTokens.length = 0;
1139
1140
  }
1140
1141
  }
1141
1142
  // save token
@@ -1217,10 +1218,9 @@ for (const [language, parser] of Object.entries(plugins)) {
1217
1218
  * @param config Configuration for the MediaWiki mode
1218
1219
  */
1219
1220
  export const mediawiki = (config: MwConfig): LanguageSupport => {
1220
- const mode = new MediaWiki(config);
1221
- const parser = mode.mediawiki;
1222
- const lang = StreamLanguage.define(parser);
1223
- const highlighter = syntaxHighlighting(HighlightStyle.define(mode.getTagStyles()) as Highlighter);
1221
+ const mode = new MediaWiki(config),
1222
+ lang = StreamLanguage.define(mode.mediawiki),
1223
+ highlighter = syntaxHighlighting(HighlightStyle.define(mode.getTagStyles()) as Highlighter);
1224
1224
  return new LanguageSupport(lang, highlighter);
1225
1225
  };
1226
1226