[",
32 | "codemirror-keymap-autocomplete": "Аўтадапаўненьне",
33 | "codemirror-keymap-startcompletion": "Паказваць падказкі аўтадапаўненьня",
34 | "codemirror-keymap-selectcompletion": "Выбраць падказку аўтадапаўненьня",
35 | "codemirror-keymap-other": "Іншае",
36 | "codemirror-keymap-preferences": "Налады падсьветкі сынтаксу",
37 | "prefs-accessibility": "Даступнасьць",
38 | "codemirror-wikilint-whitespace": "уставіць пропуст",
39 | "codemirror-beta-feature-title": "Палепшаная падсьветка сынтаксісу",
40 | "codemirror-beta-feature-description": "Атрымаць раньні доступ да новай падсьветкі сынтаксісу і такіх функцый рэдагаваньня, як згортваньне і аўтадапаўненьне коду."
41 | }
42 |
--------------------------------------------------------------------------------
/i18n/ur.json:
--------------------------------------------------------------------------------
1 | {
2 | "@metadata": {
3 | "authors": [
4 | "Muhammad Shuaib"
5 | ]
6 | },
7 | "codemirror-desc": "ویکی خانۂ ترمیم میں سنٹیکس کو نمایاں کرتا ہے",
8 | "codemirror-toggle-label": "سنٹیکس روشن گر",
9 | "codemirror-toggle-label-short": "نحو",
10 | "codemirror-prefs-summary": "اس سہولت کے متعلق مزید تفصیلات [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Extension:CodeMirror یہاں] دستیاب ہیں۔",
11 | "codemirror-prefs-enable": "ویکی متن کے لیے نحو روشن گری کو فعال کريں",
12 | "codemirror-prefs-title": "نحو روشن گری کی ترجیحات",
13 | "codemirror-prefs-help": "معاونت",
14 | "codemirror-prefs-autocomplete": "خودکار تکمیل فعال کریں",
15 | "codemirror-prefs-linenumbering": "سطروں کا نمبر شمار دکھائیں",
16 | "codemirror-prefs-linewrapping": "سطروں کو تہ کریں",
17 | "codemirror-prefs-activeline": "فعال سطر کو نمایاں کریں",
18 | "codemirror-prefs-specialchars": "خصوصی حروف دکھائیں",
19 | "codemirror-prefs-colorblind": "ویکی مندرجات میں ترمیم کے دوران سنٹیکس کو نمایاں کرنے لیے رنگوندا دوست سہولت کو فعال کریں",
20 | "codemirror-prefs-colorblind-help": "اگر آپ سنٹیکس کو نمایاں کرنے لیے کسی آلہ کو استعمال کر رہے ہیں تو یہ سہولت کام نہیں کرے گی۔",
21 | "codemirror-close": "بند کریں",
22 | "codemirror-find": "تلاش کریں",
23 | "codemirror-next": "اگلا تلاش کریں",
24 | "codemirror-previous": "پچھلا تلاش کریں",
25 | "codemirror-all": "تمام",
26 | "codemirror-all-tooltip": "تمام مماثل لفظوں کو منتخب کریں",
27 | "codemirror-match-case": "بڑے چھوٹے حروف کا خیال رکھیں",
28 | "codemirror-regexp": "ریجیکس",
29 | "codemirror-regexp-invalid": "نادرست ریجیکس",
30 | "codemirror-by-word": "لفظ کے ذریعے",
31 | "codemirror-replace": "تبدیل کریں",
32 | "codemirror-replace-placeholder": "تبدیل کریں",
33 | "codemirror-replace-all": "سب بدل دیں",
34 | "codemirror-done": "مکمل",
35 | "codemirror-find-results": "$2 میں سے $1",
36 | "codemirror-goto-line": "اس سطر پر جائیں",
37 | "codemirror-goto-line-go": "جائیں",
38 | "codemirror-control-character": "کنٹرول کیریکٹر $1",
39 | "codemirror-special-char-null": "خالی کیریکٹر",
40 | "codemirror-special-char-bell": "بیل کیریکٹر",
41 | "codemirror-special-char-backspace": "بیک اسپیس",
42 | "codemirror-special-char-newline": "نئی سطر",
43 | "codemirror-folded-code": "تہ شدہ کوڈ",
44 | "prefs-accessibility": "رسائی",
45 | "prefs-syntax-highlighting": "نحو روشن گری",
46 | "codemirror-beta-feature-title": "نحو کی بہتر روشن گری"
47 | }
48 |
--------------------------------------------------------------------------------
/resources/modes/codemirror.javascript.js:
--------------------------------------------------------------------------------
1 | const { javascript, javascriptLanguage, scopeCompletionSource } = require( '../lib/codemirror6.bundle.modes.js' );
2 | const CodeMirrorMode = require( './codemirror.mode.js' );
3 | const CodeMirrorWorker = require( '../workers/codemirror.worker.js' );
4 |
5 | /**
6 | * JavaScript language support for CodeMirror.
7 | *
8 | * @example
9 | * const require = await mw.loader.using( [ 'ext.CodeMirror.v6', 'ext.CodeMirror.v6.modes' ] );
10 | * const CodeMirror = require( 'ext.CodeMirror.v6' );
11 | * const { javascript } = require( 'ext.CodeMirror.v6.modes' );
12 | * const cm = new CodeMirror( myTextarea, javascript() );
13 | * cm.initialize();
14 | * @extends CodeMirrorMode
15 | * @hideconstructor
16 | */
17 | class CodeMirrorJavaScript extends CodeMirrorMode {
18 |
19 | /** @inheritDoc */
20 | get language() {
21 | return javascriptLanguage;
22 | }
23 |
24 | /** @inheritDoc */
25 | get lintSource() {
26 | return async ( view ) => {
27 | const data = await this.worker.lint( view );
28 | return data.map( ( {
29 | ruleId,
30 | message,
31 | severity,
32 | line,
33 | column,
34 | endLine,
35 | endColumn,
36 | fix,
37 | suggestions = []
38 | } ) => {
39 | const start = CodeMirrorWorker.pos( view, line, column );
40 | const diagnostic = {
41 | rule: ruleId,
42 | source: 'ESLint',
43 | message: message + ( ruleId ? ` (${ ruleId })` : '' ),
44 | severity: severity === 1 ? 'info' : 'error',
45 | from: start,
46 | to: endLine === undefined ?
47 | start + 1 :
48 | CodeMirrorWorker.pos( view, endLine, endColumn )
49 | };
50 | if ( fix || suggestions.length ) {
51 | diagnostic.actions = [
52 | ...fix ? [ { name: 'fix', fix } ] : [],
53 | ...suggestions.map( ( suggestion ) => ( { name: 'suggestion', fix: suggestion.fix } ) )
54 | ].map( ( { name, fix: { range: [ from, to ], text } } ) => ( {
55 | name,
56 | apply( v ) {
57 | v.dispatch( { changes: { from, to, insert: text } } );
58 | }
59 | } ) );
60 | }
61 | return diagnostic;
62 | } );
63 | };
64 | }
65 |
66 | /** @inheritDoc */
67 | get support() {
68 | return [
69 | javascript().support,
70 | javascriptLanguage.data.of( { autocomplete: scopeCompletionSource( window ) } )
71 | ];
72 | }
73 | }
74 |
75 | module.exports = CodeMirrorJavaScript;
76 |
--------------------------------------------------------------------------------
/i18n/ja.json:
--------------------------------------------------------------------------------
1 | {
2 | "@metadata": {
3 | "authors": [
4 | "Ant176",
5 | "Delim",
6 | "Kokage si",
7 | "Marine-Blue",
8 | "Shirayuki",
9 | "Shulmj",
10 | "SkyDaisy9",
11 | "Suzukaze-c",
12 | "Waki285"
13 | ]
14 | },
15 | "codemirror-desc": "ウィキテキストエディターに構文強調の機能を追加する",
16 | "codemirror-toggle-label": "構文の強調",
17 | "codemirror-toggle-label-short": "構文",
18 | "codemirror-prefs-summary": "この機能の詳細については、[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Extension:CodeMirror ヘルプページ]を参照してください。",
19 | "codemirror-prefs-enable": "構文強調表示を有効にする",
20 | "codemirror-prefs-title": "構文強調表示の個人設定",
21 | "codemirror-prefs-help": "ヘルプ",
22 | "codemirror-prefs-colorblind": "ウィキテキストでの編集時にカラーブラインドに応じた配色を使用する",
23 | "codemirror-prefs-colorblind-help": "構文強調表示のガジェットを使用している場合、この設定は機能しません。",
24 | "codemirror-close": "閉じる",
25 | "codemirror-find": "検索",
26 | "codemirror-next": "次を検索",
27 | "codemirror-previous": "前を検索",
28 | "codemirror-all": "すべて",
29 | "codemirror-match-case": "大文字・小文字を区別する",
30 | "codemirror-regexp": "正規表現",
31 | "codemirror-by-word": "単語",
32 | "codemirror-replace": "置換",
33 | "codemirror-replace-placeholder": "置換",
34 | "codemirror-replace-all": "すべて置換",
35 | "codemirror-done": "完了",
36 | "codemirror-control-character": "制御文字 $1",
37 | "codemirror-special-char-null": "ヌル文字",
38 | "codemirror-special-char-bell": "ベル文字",
39 | "codemirror-special-char-backspace": "バックスペース",
40 | "codemirror-special-char-newline": "改行",
41 | "codemirror-special-char-vertical-tab": "垂直タブ",
42 | "codemirror-special-char-carriage-return": "キャリッジリターン",
43 | "codemirror-special-char-escape": "エスケープ文字",
44 | "codemirror-special-char-nbsp": "改行なしスペース",
45 | "codemirror-special-char-zero-width-space": "ゼロ幅スペース",
46 | "codemirror-special-char-zero-width-non-joiner": "ゼロ幅 Non-joiner",
47 | "codemirror-special-char-zero-width-joiner": "ゼロ幅 joiner",
48 | "codemirror-special-char-left-to-right-mark": "LTRマーク",
49 | "codemirror-special-char-right-to-left-mark": "RTLマーク",
50 | "codemirror-special-char-line-separator": "行区切り",
51 | "codemirror-special-char-left-to-right-override": "LTR オーバーライド",
52 | "codemirror-special-char-right-to-left-override": "RTL オーバーライド",
53 | "prefs-accessibility": "アクセシビリティ",
54 | "prefs-syntax-highlighting": "構文の強調",
55 | "codemirror-beta-feature-title": "改善された構文ハイライト",
56 | "codemirror-beta-feature-description": "コードの折りたたみやオートコンプリートなど、新たな構文ハイライトや編集機能にいち早くアクセスできます。"
57 | }
58 |
--------------------------------------------------------------------------------
/resources/workers/mediawiki/worker.js:
--------------------------------------------------------------------------------
1 | /* global Parser */
2 | const onmessage = require( '../common.js' );
3 | require( 'wikiparser-node/bundle/bundle-es8.min.js' );
4 |
5 | // Rules to be treated as "info" severity in CodeMirrorLint.
6 | const infoRules = [
7 | 'bold-header',
8 | 'format-leakage',
9 | 'table-layout',
10 | 'unknown-page',
11 | 'unclosed-table',
12 | 'unterminated-url',
13 | 'var-anchor'
14 | ];
15 |
16 | Parser.lintConfig = {
17 | fix: false,
18 | rules: {
19 | 'fostered-content': [
20 | 1,
21 | { transclusion: 0 }
22 | ],
23 | h1: 1,
24 | 'illegal-attr': [
25 | 2,
26 | {
27 | tabindex: 1,
28 | unknown: 2,
29 | value: 2
30 | }
31 | ],
32 | 'insecure-style': 0,
33 | 'lonely-apos': [
34 | 1,
35 | {
36 | word: 0
37 | }
38 | ],
39 | 'lonely-bracket': [
40 | 1,
41 | {
42 | extLink: 2,
43 | single: 0
44 | }
45 | ],
46 | 'lonely-http': 0,
47 | 'no-arg': 0,
48 | 'no-duplicate': [
49 | 2,
50 | {
51 | category: 1,
52 | id: 1,
53 | unknownImageParameter: 0
54 | }
55 | ],
56 | 'unmatched-tag': 0
57 | }
58 | };
59 |
60 | // HACK: Customize severity of some rules to "info" which is not supported by wikiparser-node.
61 | for ( const rule of infoRules ) {
62 | Parser.lintConfig.rules[ rule ] = 1;
63 | }
64 |
65 | const last = {};
66 |
67 | const setConfig = ( config ) => {
68 | Parser.config = config;
69 | };
70 | const getConfig = () => Parser.getConfig();
71 | const setI18N = ( i18n ) => {
72 | const obj = {};
73 | for ( const key in i18n ) {
74 | if ( key.startsWith( 'codemirror-wikilint-' ) ) {
75 | obj[ key.slice( 20 ) ] = i18n[ key ];
76 | }
77 | }
78 | Parser.i18n = obj;
79 | };
80 | const getI18N = () => Parser.i18n;
81 | const setLintConfig = ( config ) => {
82 | Parser.lintConfig = config;
83 | };
84 | const lint = ( wikitext ) => {
85 | if ( last.wikitext === wikitext ) {
86 | return last.diagnostics;
87 | }
88 | const diagnostics = Parser.parse( wikitext ).lint()
89 | .map( ( diag ) => {
90 | if ( infoRules.includes( diag.rule ) ) {
91 | diag.severity = 'info';
92 | }
93 | return diag;
94 | } );
95 | last.wikitext = wikitext;
96 | last.diagnostics = diagnostics;
97 | return diagnostics;
98 | };
99 |
100 | onmessage( setConfig, getConfig, lint, setI18N, getI18N, setLintConfig );
101 |
--------------------------------------------------------------------------------
/tests/jest/codemirror.bidiIsolation.test.js:
--------------------------------------------------------------------------------
1 | const CodeMirror = require( '../../resources/codemirror.js' );
2 | const { mediawiki } = require( '../../resources/modes/mediawiki/codemirror.mediawiki.js' );
3 | const bidiIsolationExtension = require( '../../resources/modes/mediawiki/codemirror.mediawiki.bidiIsolation.js' );
4 |
5 | const testCases = [
6 | {
7 | title: 'wraps HTML tags with span.cm-bidi-isolate',
8 | input: 'שלוםשלוםשלום',
9 | output: ']שלום<span class="foobar">שלום</span>שלום
'
10 | },
11 | {
12 | title: 'wraps self-closing extension tags with span.cm-bidi-isolate',
13 | input: '',
14 | output: '<ref name="foo" />
'
15 | }
16 | ];
17 |
18 | // Setup CodeMirror instance.
19 | const textarea = document.createElement( 'textarea' );
20 | textarea.dir = 'rtl';
21 | document.body.appendChild( textarea );
22 | const cm = new CodeMirror( textarea, mediawiki() );
23 | cm.initialize();
24 | // Normally ran by mw.hook, but we don't mock the hook system in the Jest tests.
25 | cm.preferences.registerExtension( 'bidiIsolation', bidiIsolationExtension, cm.view );
26 |
27 | describe( 'CodeMirrorMediaWikiBidiIsolation', () => {
28 | it.each( testCases )(
29 | 'bidi isolation ($title)',
30 | ( { input, output } ) => {
31 | cm.view.dispatch( {
32 | changes: {
33 | from: 0,
34 | to: cm.view.state.doc.length,
35 | insert: input
36 | }
37 | } );
38 | cm.$textarea.textSelection = jest.fn().mockReturnValue( input );
39 | expect( cm.view.dom.querySelector( '.cm-content' ).innerHTML ).toStrictEqual( output );
40 | }
41 | );
42 | } );
43 |
--------------------------------------------------------------------------------
/tests/jest/codemirror.gotoLine.test.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable-next-line n/no-missing-require */
2 | const { StateEffectType, StateField } = require( 'ext.CodeMirror.v6.lib' );
3 | const CodeMirror = require( '../../resources/codemirror.js' );
4 | const CodeMirrorGotoLine = require( '../../resources/codemirror.gotoLine.js' );
5 |
6 | describe( 'CodeMirrorGotoLine', () => {
7 | const getCodeMirror = () => {
8 | const form = document.createElement( 'form' );
9 | const textarea = document.createElement( 'textarea' );
10 | form.appendChild( textarea );
11 | const cm = new CodeMirror( textarea );
12 | cm.initialize();
13 | return cm;
14 | };
15 |
16 | it( 'constructor', () => {
17 | const gotoLine = new CodeMirrorGotoLine();
18 | expect( gotoLine.toggleEffect ).toBeInstanceOf( StateEffectType );
19 | expect( gotoLine.panelStateField ).toBeInstanceOf( StateField );
20 | } );
21 |
22 | it( 'should show the goto line panel with Mod-Alt-g and close it with Escape', () => {
23 | const cm = getCodeMirror();
24 | cm.view.contentDOM.dispatchEvent(
25 | new KeyboardEvent( 'keydown', { key: 'g', altKey: true, ctrlKey: true } )
26 | );
27 | const panel = cm.view.dom.querySelector( '.cm-mw-goto-line-panel' );
28 | expect( panel ).toBeTruthy();
29 | panel.dispatchEvent( new KeyboardEvent( 'keydown', { key: 'Escape' } ) );
30 | expect( cm.view.dom.querySelector( '.cm-mw-goto-line-panel' ) ).toBeFalsy();
31 | } );
32 |
33 | it( 'Submission should move the cursor to the specified line', () => {
34 | const cm = getCodeMirror();
35 | cm.textSelection.setContents( 'Foobar\n'.repeat( 10 ) );
36 | cm.textSelection.setSelection( { start: 5 } );
37 | cm.view.contentDOM.dispatchEvent(
38 | new KeyboardEvent( 'keydown', { key: 'g', altKey: true, ctrlKey: true } )
39 | );
40 | const input = cm.view.dom.querySelector( '[name=line]' );
41 | expect( input.value ).toStrictEqual( '1' );
42 | input.value = '3';
43 | const panel = cm.view.dom.querySelector( '.cm-mw-goto-line-panel' );
44 | panel.dispatchEvent( new KeyboardEvent( 'keydown', { key: 'Enter' } ) );
45 | expect( cm.textSelection.getCaretPosition() ).toStrictEqual( 'Foobar\n'.repeat( 2 ).length );
46 | } );
47 |
48 | it( 'should be callable from the CodeMirror class', () => {
49 | const cm = getCodeMirror();
50 | cm.gotoLine.run( cm.view );
51 | const panel = cm.view.dom.querySelector( '.cm-mw-goto-line-panel' );
52 | expect( panel ).toBeTruthy();
53 | } );
54 | } );
55 |
--------------------------------------------------------------------------------
/i18n/sr-el.json:
--------------------------------------------------------------------------------
1 | {
2 | "@metadata": {
3 | "authors": [
4 | "Obsuser"
5 | ]
6 | },
7 | "codemirror-desc": "Omogućava isticanje sintakse u uređivaču vikiteksta",
8 | "codemirror-toggle-label": "Isticanje sintakse",
9 | "codemirror-prefs-enable": "Omogući isticanje sintakse",
10 | "codemirror-prefs-help": "pomoć",
11 | "codemirror-prefs-whitespace": "Istakni bele prostore",
12 | "codemirror-close": "Zatvori",
13 | "codemirror-find": "Pronađi",
14 | "codemirror-next": "Pronađi sledeće",
15 | "codemirror-previous": "Pronađi prethodno",
16 | "codemirror-all": "sve",
17 | "codemirror-match-case": "Pazi na veličinu slova",
18 | "codemirror-regexp": "Regeks",
19 | "codemirror-by-word": "Cela reč",
20 | "codemirror-replace": "Zameni",
21 | "codemirror-replace-placeholder": "Zameni",
22 | "codemirror-replace-all": "Zameni sve",
23 | "codemirror-done": "Gotovo",
24 | "codemirror-find-results": "$1 od $2",
25 | "codemirror-keymap-help-title": "Tasterske prečice",
26 | "codemirror-keymap-help-close": "Zatvori",
27 | "codemirror-keymap-accessibility": "Pristupačnost",
28 | "codemirror-keymap-textstyling": "Stil teksta",
29 | "codemirror-keymap-bold": "Podebljano",
30 | "codemirror-keymap-italic": "Iskošeno",
31 | "codemirror-keymap-link": "Veza",
32 | "codemirror-keymap-computercode": "Računarski kod",
33 | "codemirror-keymap-strikethrough": "Precrtaj",
34 | "codemirror-keymap-subscript": "Indeks",
35 | "codemirror-keymap-superscript": "Eksponent",
36 | "codemirror-keymap-underline": "Podvučeno",
37 | "codemirror-keymap-history": "Istorija",
38 | "codemirror-keymap-undo": "Opozovi",
39 | "codemirror-keymap-redo": "Ponovi",
40 | "codemirror-keymap-undoselection": "Poništi izbor",
41 | "codemirror-keymap-redoselection": "Ponovi izbor",
42 | "codemirror-keymap-indent": "Povećaj uvlačenje",
43 | "codemirror-keymap-outdent": "Smanji uvlačenje",
44 | "codemirror-keymap-blockquote": "Blok citat",
45 | "codemirror-keymap-heading": "Naslov (1-6)",
46 | "codemirror-keymap-heading-n": "Naslov $1",
47 | "codemirror-keymap-search": "Pretraga",
48 | "codemirror-keymap-find": "Pronađi i zameni",
49 | "codemirror-keymap-insert": "Umetni",
50 | "codemirror-keymap-reference": "Referenca",
51 | "codemirror-keymap-comment": "Komentar",
52 | "codemirror-keymap-other": "Drugo",
53 | "codemirror-keymap-direction": "Promena usmerenosti",
54 | "codemirror-keymap-help": "Tasterske prečice",
55 | "prefs-accessibility": "Pristupačnost",
56 | "prefs-syntax-highlighting": "Isticanje sintakse"
57 | }
58 |
--------------------------------------------------------------------------------
/resources/ve-cm/ve.ui.CodeMirrorTool.v6.js:
--------------------------------------------------------------------------------
1 | /**
2 | * VisualEditor UserInterface CodeMirror tool.
3 | *
4 | * @class
5 | * @abstract
6 | * @extends ve.ui.Tool
7 | * @constructor
8 | * @param {OO.ui.ToolGroup} toolGroup
9 | * @param {Object} [config] Configuration options
10 | */
11 | ve.ui.CodeMirrorTool = function VeUiCodeMirrorTool() {
12 | // Parent constructor
13 | ve.ui.CodeMirrorTool.super.apply( this, arguments );
14 |
15 | // Events
16 | this.toolbar.connect( this, { surfaceChange: 'onSurfaceChange' } );
17 | };
18 |
19 | /* Inheritance */
20 |
21 | OO.inheritClass( ve.ui.CodeMirrorTool, ve.ui.Tool );
22 |
23 | /* Static properties */
24 |
25 | ve.ui.CodeMirrorTool.static.name = 'codeMirror';
26 | ve.ui.CodeMirrorTool.static.autoAddToCatchall = false;
27 | ve.ui.CodeMirrorTool.static.title = OO.ui.deferMsg( 'codemirror-toggle-label' );
28 | ve.ui.CodeMirrorTool.static.icon = 'highlight';
29 | ve.ui.CodeMirrorTool.static.group = 'utility';
30 | ve.ui.CodeMirrorTool.static.commandName = 'codeMirror';
31 | ve.ui.CodeMirrorTool.static.deactivateOnSelect = false;
32 |
33 | /**
34 | * @inheritdoc
35 | */
36 | ve.ui.CodeMirrorTool.prototype.onSelect = function () {
37 | // Parent method
38 | ve.ui.CodeMirrorTool.super.prototype.onSelect.apply( this, arguments );
39 |
40 | // This is the value of what CM will be toggled to.
41 | // When CM has not been loaded yet (the mirror property is undefined), this should return true.
42 | const useCodeMirror = !this.toolbar.surface.mirror || this.toolbar.surface.mirror.isActive;
43 |
44 | this.setActive( useCodeMirror );
45 | };
46 |
47 | /**
48 | * @inheritdoc
49 | */
50 | ve.ui.CodeMirrorTool.prototype.onSurfaceChange = function ( oldSurface, newSurface ) {
51 | const isDisabled = newSurface.getMode() !== 'source';
52 | this.setDisabled( isDisabled );
53 | if ( !isDisabled ) {
54 | const command = this.getCommand();
55 | const useCodeMirror = mw.user.options.get( 'usecodemirror' ) > 0;
56 | command.execute( newSurface, [ useCodeMirror ] );
57 | this.setActive( useCodeMirror );
58 | newSurface.once( 'destroy', () => {
59 | if ( newSurface.mirror ) {
60 | newSurface.mirror.destroy();
61 | }
62 | } );
63 | }
64 | };
65 |
66 | ve.ui.CodeMirrorTool.prototype.onUpdateState = function () {};
67 |
68 | /* Registration */
69 |
70 | ve.ui.toolFactory.register( ve.ui.CodeMirrorTool );
71 |
72 | /* Command */
73 |
74 | ve.ui.commandRegistry.register(
75 | new ve.ui.Command(
76 | 'codeMirror', 'codeMirror', 'toggle'
77 | )
78 | );
79 |
--------------------------------------------------------------------------------
/tests/selenium/pageobjects/edit.page.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const Page = require( 'wdio-mediawiki/Page' );
4 |
5 | // Copied from mediawiki-core edit.page.js
6 | class EditPage extends Page {
7 | async openForEditing( title, queryParams = {} ) {
8 | queryParams = Object.assign( {
9 | action: 'edit',
10 | vehidebetadialog: 1,
11 | hidewelcomedialog: 1,
12 | cm6enable: 1
13 | }, queryParams );
14 | await super.openTitle( title, queryParams );
15 | }
16 |
17 | get wikiEditorToolbar() {
18 | return $( '#wikiEditor-ui-toolbar' );
19 | }
20 |
21 | get textInput() {
22 | return $( '#wpTextbox1' );
23 | }
24 |
25 | get codeMirrorButton() {
26 | return $( '#mw-editbutton-codemirror' );
27 | }
28 |
29 | get codeMirrorContentEditable() {
30 | return $( '.cm-content' );
31 | }
32 |
33 | async clickText() {
34 | await this.codeMirrorContentEditable.isDisplayed();
35 | if ( await this.visualEditorContentEditable.isDisplayed() ) {
36 | await this.visualEditorContentEditable.click();
37 | } else {
38 | await this.codeMirrorContentEditable.click();
39 | }
40 | }
41 |
42 | get visualEditorContentEditable() {
43 | return $( '.ve-ce-attachedRootNode' );
44 | }
45 |
46 | get visualEditorPageMenu() {
47 | return $( '.ve-ui-toolbar-group-pageMenu' );
48 | }
49 |
50 | get visualEditorCodeMirrorButton() {
51 | return $( '.oo-ui-tool-name-codeMirror' );
52 | }
53 |
54 | get visualEditorMessageDialog() {
55 | return $( '.oo-ui-messageDialog-actions' );
56 | }
57 |
58 | get visualEditorDestructiveButton() {
59 | return $( '.oo-ui-flaggedElement-destructive' );
60 | }
61 |
62 | async visualEditorToggleCodeMirror() {
63 | await this.visualEditorPageMenu.waitForDisplayed();
64 | await this.visualEditorPageMenu.click();
65 | await this.visualEditorCodeMirrorButton.waitForDisplayed();
66 | await this.visualEditorCodeMirrorButton.click();
67 | }
68 |
69 | get codeMirrorCodeFoldingButton() {
70 | return $( '.cm-tooltip-fold' );
71 | }
72 |
73 | get codeMirrorCodeFoldingPlaceholder() {
74 | return $( '.cm-foldPlaceholder' );
75 | }
76 |
77 | get highlightedBracket() {
78 | return $( '.cm-line .cm-matchingBracket' );
79 | }
80 |
81 | get highlightedBrackets() {
82 | return $$( '.cm-line .cm-matchingBracket' );
83 | }
84 |
85 | async getHighlightedMatchingBrackets() {
86 | const matchingTexts = await this.highlightedBrackets.map( ( el ) => el.getText() );
87 | return matchingTexts.join( '' );
88 | }
89 | }
90 |
91 | module.exports = new EditPage();
92 |
--------------------------------------------------------------------------------
/resources/codemirror.wikieditor.less:
--------------------------------------------------------------------------------
1 | @import 'mediawiki.skin.variables.less';
2 | @import './codemirror.mixins.less';
3 |
4 | // Overrides for WikiEditor.
5 |
6 | // NOTE: This file is included in the ext.CodeMirror.v6.init module for performance reasons.
7 | // We need the styles for the toggle button without having to load all of ext.CodeMirror.v6.
8 |
9 | .wikiEditor-ui-text .cm-editor {
10 | border: inherit;
11 | }
12 |
13 | // Mimics .group-search
14 | .wikiEditor-ui-toolbar .section-advanced .group-codemirror {
15 | float: right;
16 | border-right: 0;
17 | border-left: @border-subtle;
18 | }
19 |
20 | .cm-mw-toggle-wikieditor {
21 | .oo-ui-icon-syntax-highlight {
22 | background-color: @color-base-fixed;
23 | // The SVG is just barely over 300 bytes, and is also only temporary
24 | // until an official icon has been established in Codex/OOUI (T174145).
25 | .embed-img( 'icon' );
26 | }
27 |
28 | &:hover {
29 | background-color: @background-color-interactive;
30 | }
31 |
32 | &.oo-ui-toggleWidget-on {
33 | .oo-ui-labelElement-label {
34 | color: @color-progressive;
35 | }
36 |
37 | .oo-ui-icon-syntax-highlight {
38 | background-color: @color-progressive;
39 | }
40 | }
41 |
42 | &.oo-ui-buttonElement-frameless.oo-ui-labelElement.oo-ui-iconElement:first-child {
43 | margin-left: 0;
44 | }
45 | }
46 |
47 | .oo-ui-icon-wrapping {
48 | .embed-img( 'wrapping' );
49 | }
50 |
51 | .oo-ui-icon-gotoLine {
52 | .embed-img( 'gotoLine' );
53 | }
54 |
55 | .oo-ui-icon-pilcrow {
56 | .embed-img( 'pilcrow' );
57 | }
58 |
59 | .oo-ui-icon-check-all {
60 | .embed-img( 'check-all' );
61 | }
62 |
63 | .oo-ui-icon-settings {
64 | .embed-img( 'settings' );
65 | }
66 |
67 | // Hide MediaWiki's native edit help link for consistency with CodeEditor.
68 | // This is also hidden by WikiEditor, but only for the wikitext content model.
69 | .client-js .editButtons .editHelp {
70 | display: none;
71 | }
72 |
73 | // Hide all buttons except CodeMirror on read only pages (T301615),
74 | // and different content types.
75 | // This is the same hack that CodeEditor uses to customize the toolbar.
76 | // WikiEditor should be updated to better handle read only pages (T188817).
77 | .wikiEditor-ui {
78 | &.ext-codemirror-readonly,
79 | &:not( .ext-codemirror-mediawiki ) {
80 | .wikiEditor-section-secondary,
81 | .group:not( .group-codemirror ):not( .group-codemirror-format ):not( .group-codemirror-preferences ):not( .group-codemirror-search ),
82 | .tabs,
83 | .sections {
84 | display: none;
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/i18n/sr-ec.json:
--------------------------------------------------------------------------------
1 | {
2 | "@metadata": {
3 | "authors": [
4 | "Aca",
5 | "Acamicamacaraca",
6 | "BadDog",
7 | "Kizule",
8 | "Milicevic01",
9 | "Obsuser"
10 | ]
11 | },
12 | "codemirror-desc": "Омогућава истицање синтаксе у уређивачу викитекста",
13 | "codemirror-toggle-label": "Истицање синтаксе",
14 | "codemirror-prefs-enable": "Омогући истицање синтаксе",
15 | "codemirror-prefs-help": "помоћ",
16 | "codemirror-prefs-section-references": "Референце",
17 | "codemirror-prefs-whitespace": "Истакни беле просторе",
18 | "codemirror-close": "Затвори",
19 | "codemirror-find": "Пронађи",
20 | "codemirror-next": "Пронађи следеће",
21 | "codemirror-previous": "Пронађи претходно",
22 | "codemirror-all": "све",
23 | "codemirror-match-case": "Пази на величину слова",
24 | "codemirror-regexp": "Регекс",
25 | "codemirror-by-word": "Цела реч",
26 | "codemirror-replace": "Замени",
27 | "codemirror-replace-placeholder": "Замени",
28 | "codemirror-replace-all": "Замени све",
29 | "codemirror-done": "Готово",
30 | "codemirror-find-results": "$1 од $2",
31 | "codemirror-keymap-help-title": "Тастерске пречице",
32 | "codemirror-keymap-help-close": "Затвори",
33 | "codemirror-keymap-accessibility": "Приступачност",
34 | "codemirror-keymap-textstyling": "Стил текста",
35 | "codemirror-keymap-bold": "Подебљано",
36 | "codemirror-keymap-italic": "Искошено",
37 | "codemirror-keymap-link": "Веза",
38 | "codemirror-keymap-computercode": "Рачунарски код",
39 | "codemirror-keymap-strikethrough": "Прецртај",
40 | "codemirror-keymap-subscript": "Индекс",
41 | "codemirror-keymap-superscript": "Експонент",
42 | "codemirror-keymap-underline": "Подвучено",
43 | "codemirror-keymap-history": "Историја",
44 | "codemirror-keymap-undo": "Опозови",
45 | "codemirror-keymap-redo": "Понови",
46 | "codemirror-keymap-undoselection": "Поништи избор",
47 | "codemirror-keymap-redoselection": "Понови избор",
48 | "codemirror-keymap-indent": "Повећај увлачење",
49 | "codemirror-keymap-outdent": "Смањи увлачење",
50 | "codemirror-keymap-blockquote": "Блок цитат",
51 | "codemirror-keymap-heading": "Наслов (1-6)",
52 | "codemirror-keymap-heading-n": "Наслов $1",
53 | "codemirror-keymap-search": "Претрага",
54 | "codemirror-keymap-find": "Пронађи и замени",
55 | "codemirror-keymap-insert": "Уметни",
56 | "codemirror-keymap-reference": "Референца",
57 | "codemirror-keymap-comment": "Коментар",
58 | "codemirror-keymap-other": "Друго",
59 | "codemirror-keymap-direction": "Промена усмерености",
60 | "codemirror-keymap-help": "Тастерске пречице",
61 | "prefs-accessibility": "Приступачност",
62 | "prefs-syntax-highlighting": "Истицање синтаксе"
63 | }
64 |
--------------------------------------------------------------------------------
/resources/modes/codemirror.mode.js:
--------------------------------------------------------------------------------
1 | const { Extension, Language, LanguageSupport, LintSource } = require( 'ext.CodeMirror.v6.lib' );
2 | const CodeMirrorWorker = require( '../workers/codemirror.worker.js' );
3 |
4 | /* eslint-disable jsdoc/valid-types */
5 | /**
6 | * Abstract interface for CodeMirror modes.
7 | *
8 | * Clients can unpack individual modes from the
9 | * {@link module:ext.CodeMirror.v6.modes ext.CodeMirror.v6.modes} module.
10 | * Each mode is exposed as a method with the same name as the mode in the form of a
11 | * {@link LanguageSupport}-like instance that is consumable by {@link CodeMirror}.
12 | *
13 | * @example
14 | * const CodeMirror = require( 'ext.CodeMirror.v6' );
15 | * const { javascript } = require( 'ext.CodeMirror.v6.modes' );
16 | * const cm = new CodeMirror( myTextArea, javascript() );
17 | * cm.initialize();
18 | * @implements LanguageSupport
19 | */
20 | class CodeMirrorMode {
21 | /* eslint-enable jsdoc/valid-types */
22 |
23 | /**
24 | * @param {string} name
25 | * @internal
26 | */
27 | constructor( name ) {
28 | /**
29 | * The name of the mode.
30 | *
31 | * @type {string}
32 | */
33 | this.name = name;
34 |
35 | /**
36 | * The web worker for the mode, if any.
37 | *
38 | * @type {CodeMirrorWorker|undefined}
39 | */
40 | this.worker = this.hasWorker ? new CodeMirrorWorker( this.name ) : undefined;
41 | }
42 |
43 | /**
44 | * The complete set of extensions for this mode, including the language and any
45 | * supporting extensions.
46 | *
47 | * @return {Extension}
48 | */
49 | get extension() {
50 | return [ this.language, this.support ];
51 | }
52 |
53 | /**
54 | * The language object.
55 | *
56 | * @type {Language}
57 | */
58 | get language() {
59 | throw new Error( 'Not implemented' );
60 | }
61 |
62 | /**
63 | * The function to lint the code in the editor.
64 | *
65 | * @type {LintSource|undefined}
66 | */
67 | get lintSource() {
68 | return undefined;
69 | }
70 |
71 | /**
72 | * The function to lint the code in the editor using a MediaWiki API.
73 | *
74 | * @type {LintSource|undefined}
75 | */
76 | get lintApi() {
77 | return undefined;
78 | }
79 |
80 | /**
81 | * Whether the mode should load a web worker.
82 | *
83 | * @return {boolean}
84 | */
85 | get hasWorker() {
86 | return !!this.lintSource;
87 | }
88 |
89 | /**
90 | * Supporting extensions.
91 | *
92 | * @type {Extension}
93 | */
94 | get support() {
95 | return [];
96 | }
97 | }
98 |
99 | module.exports = CodeMirrorMode;
100 |
--------------------------------------------------------------------------------
/i18n/bg.json:
--------------------------------------------------------------------------------
1 | {
2 | "@metadata": {
3 | "authors": [
4 | "Kareyac",
5 | "ShockD",
6 | "StanProg"
7 | ]
8 | },
9 | "codemirror-desc": "Осигурява оцветяване на синтаксиса в редактора на уикитекст",
10 | "codemirror-toggle-label": "Оцветяване на синтаксиса",
11 | "codemirror-toggle-label-short": "Синтаксис",
12 | "codemirror-prefs-summary": "Повече информация за тази възможност можете да прочетете на [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Extension:CodeMirror помощната страница].",
13 | "codemirror-prefs-enable": "Разрешаване оцветяването на синтаксиса за уикитекст",
14 | "codemirror-prefs-title": "Настойки на оцветяването на синтаксиса",
15 | "codemirror-prefs-autocomplete": "Активиране на автоматичното дописване",
16 | "codemirror-prefs-bidiisolation": "Изолиране на двупосочен текст",
17 | "codemirror-prefs-bracketmatching": "Активиране съвпадението на скоби",
18 | "codemirror-prefs-linenumbering": "Показване номерата на редовете",
19 | "codemirror-prefs-linewrapping": "Пренасяне на редовете",
20 | "codemirror-prefs-activeline": "Оцветяване на активния ред",
21 | "codemirror-prefs-specialchars": "Показване на специалните знаци",
22 | "codemirror-prefs-colorblind": "Активиране на удобна за далтонисти схема за оцветяване на синтаксиса при редактиране на уикитекст",
23 | "codemirror-prefs-colorblind-help": "Ако използвате джаджа за оцветяване на синтаксиса, тази настройка няма да работи.",
24 | "codemirror-close": "Затваряне",
25 | "codemirror-find": "Търсене",
26 | "codemirror-next": "Търсене напред",
27 | "codemirror-previous": "Търсене назад",
28 | "codemirror-all": "Всички",
29 | "codemirror-all-tooltip": "Избиране на всички съвпадения",
30 | "codemirror-match-case": "Зачитане на регистъра",
31 | "codemirror-regexp": "Регулярен израз",
32 | "codemirror-regexp-invalid": "Неправилен регулярен израз",
33 | "codemirror-by-word": "По дума",
34 | "codemirror-replace": "Замяна",
35 | "codemirror-replace-placeholder": "Замяна",
36 | "codemirror-replace-all": "Замяна на всички",
37 | "codemirror-done": "Готово",
38 | "codemirror-find-results": "$1 от $2",
39 | "codemirror-special-char-newline": "Нов ред",
40 | "codemirror-special-char-line-separator": "Разделител на редове",
41 | "codemirror-unfold": "разгъване",
42 | "codemirror-folded-code": "сгънат код",
43 | "prefs-accessibility": "Достъпност",
44 | "prefs-syntax-highlighting": "Оцветяване на синтаксиса",
45 | "codemirror-beta-feature-title": "Подобрено оцветяване на синтаксиса",
46 | "codemirror-beta-feature-description": "Получаване на ранен достъп до нови възможности за оцветяване на синтаксиса и редактиране, като например сгъване на шаблон и автоматично дописване."
47 | }
48 |
--------------------------------------------------------------------------------
/resources/ve-cm/ve.ui.CodeMirror.init.js:
--------------------------------------------------------------------------------
1 | require( '../ext.CodeMirror.data.js' );
2 |
3 | /**
4 | * @module ext.CodeMirror.visualEditor.init
5 | * @description
6 | * Main entry point for CodeMirror initialization in VisualEditor.
7 | *
8 | * The init module is loaded by Hooks.php and is not intended for external use.
9 | * Use {@link module:ext.CodeMirror.v6.visualEditor ext.CodeMirror.v6.visualEditor} instead.
10 | *
11 | * @see module:ext.CodeMirror.v6.visualEditor
12 | * @todo Change the PluginModules in extension.json and drop this when fully migrated to v6.
13 | * @internal
14 | */
15 |
16 | const urlParams = new URLSearchParams( window.location.search );
17 | const shouldUseV6 = mw.config.get( 'extCodeMirrorConfig' ).useV6 ||
18 | urlParams.get( 'cm6enable' ) ||
19 | mw.user.options.get( 'codemirror-beta-feature-enable' ) === '1';
20 |
21 | if ( shouldUseV6 ) {
22 | // Add CodeMirror to allow-listed targets as they are created.
23 | mw.loader.using( 'ext.visualEditor.targetLoader' ).then( () => {
24 | // Loads on desktop and mobile, and for any supported target.
25 | // The target must still have 'codeMirror' in its toolbar configuration during VE
26 | // initialization, unless we add the tool manually (as done for DiscussionTools below).
27 | mw.libs.ve.targetLoader.addPlugin( 'ext.CodeMirror.v6.visualEditor' );
28 | mw.libs.ve.targetLoader.loadModules( 'source' );
29 |
30 | mw.hook( 've.newTarget' ).add( ( target ) => {
31 | if ( target.constructor.static.name === 'article' ) {
32 | // Should already be loaded for desktop article targets.
33 | return;
34 | }
35 | // T407918: DiscussionTools integration
36 | if ( target.constructor.static.name === 'discussionTools' ) {
37 | const promise = mw.loader.using( 'ext.CodeMirror.v6.visualEditor' );
38 | target.on( 'surfaceReady', async () => {
39 | if ( target.getSurface().getMode() !== 'source' ) {
40 | return;
41 | }
42 | await promise;
43 | // Add the button to the DT toolbar.
44 | const toolGroup = target.getToolbar().getToolGroupByName( 'style' );
45 | const tool = new ve.ui.CodeMirrorTool( toolGroup );
46 | toolGroup.addItems( tool );
47 | // onSurfaceChange will run the now-registered Command.
48 | tool.onSurfaceChange( null, target.getSurface() );
49 | } );
50 | }
51 | } );
52 | } );
53 | } else {
54 | // Hack to ensure ext.CodeMirror.visualEditor is loaded before VE initializes (T374072).
55 | // ve.loadModules is only fired on desktop articles, which for CM5 is what we want.
56 | mw.hook( 've.loadModules' ).add( ( addPlugin ) => {
57 | addPlugin( () => mw.loader.using( 'ext.CodeMirror.visualEditor' ) );
58 | } );
59 | }
60 |
--------------------------------------------------------------------------------
/resources/legacy/modules/ve-cm/ve.ui.CodeMirrorTool.js:
--------------------------------------------------------------------------------
1 | /**
2 | * VisualEditor UserInterface CodeMirror tool.
3 | *
4 | * @class
5 | * @abstract
6 | * @extends ve.ui.Tool
7 | * @constructor
8 | * @param {OO.ui.ToolGroup} toolGroup
9 | * @param {Object} [config] Configuration options
10 | */
11 | ve.ui.CodeMirrorTool = function VeUiCodeMirrorTool() {
12 | // Parent constructor
13 | ve.ui.CodeMirrorTool.super.apply( this, arguments );
14 |
15 | this.extCodeMirror = require( 'ext.CodeMirror' );
16 |
17 | // Events
18 | this.toolbar.connect( this, { surfaceChange: 'onSurfaceChange' } );
19 | };
20 |
21 | /* Inheritance */
22 |
23 | OO.inheritClass( ve.ui.CodeMirrorTool, ve.ui.Tool );
24 |
25 | /* Static properties */
26 |
27 | ve.ui.CodeMirrorTool.static.name = 'codeMirror';
28 | ve.ui.CodeMirrorTool.static.autoAddToCatchall = false;
29 | ve.ui.CodeMirrorTool.static.title = OO.ui.deferMsg( 'codemirror-toggle-label' );
30 | ve.ui.CodeMirrorTool.static.icon = 'highlight';
31 | ve.ui.CodeMirrorTool.static.group = 'utility';
32 | ve.ui.CodeMirrorTool.static.commandName = 'codeMirror';
33 | ve.ui.CodeMirrorTool.static.deactivateOnSelect = false;
34 |
35 | /**
36 | * @inheritdoc
37 | */
38 | ve.ui.CodeMirrorTool.prototype.onSelect = function () {
39 | // Parent method
40 | ve.ui.CodeMirrorTool.super.prototype.onSelect.apply( this, arguments );
41 |
42 | const useCodeMirror = !!this.toolbar.surface.mirror;
43 | this.setActive( useCodeMirror );
44 |
45 | new mw.Api().saveOption( 'usecodemirror', useCodeMirror ? 1 : 0 );
46 | mw.user.options.set( 'usecodemirror', useCodeMirror ? 1 : 0 );
47 | };
48 |
49 | /**
50 | * @inheritdoc
51 | */
52 | ve.ui.CodeMirrorTool.prototype.onSurfaceChange = function ( oldSurface, newSurface ) {
53 | const isDisabled = newSurface.getMode() !== 'source';
54 |
55 | this.setDisabled( isDisabled );
56 | if ( !isDisabled ) {
57 | const command = this.getCommand();
58 | const surface = this.toolbar.getSurface();
59 | const useCodeMirror = mw.user.options.get( 'usecodemirror' ) > 0;
60 | command.execute( surface, [ useCodeMirror ] );
61 | this.setActive( useCodeMirror );
62 | }
63 | };
64 |
65 | ve.ui.CodeMirrorTool.prototype.onUpdateState = function () {};
66 |
67 | {
68 | // eslint-disable-next-line no-jquery/no-global-selector
69 | const contentDir = $( '.mw-body-content .mw-parser-output' ).attr( 'dir' ) ||
70 | // New pages will use wgPageContentLanguage which is set on the html element.
71 | document.documentElement.dir;
72 |
73 | if ( contentDir === 'ltr' ) {
74 | /* Registration */
75 | ve.ui.toolFactory.register( ve.ui.CodeMirrorTool );
76 |
77 | /* Command */
78 | ve.ui.commandRegistry.register(
79 | new ve.ui.Command(
80 | 'codeMirror', 'codeMirror', 'toggle'
81 | )
82 | );
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/tests/jest/codemirror.child.test.js:
--------------------------------------------------------------------------------
1 | const CodeMirror = require( '../../resources/codemirror.js' );
2 | const CodeMirrorChild = require( '../../resources/codemirror.child.js' );
3 | const { mediawiki } = require( '../../resources/modes/mediawiki/codemirror.mediawiki.js' );
4 |
5 | let textarea, otherTextarea, cm, cmLogEditFeatureSpy;
6 |
7 | beforeEach( () => {
8 | textarea = document.createElement( 'textarea' );
9 | cm = new CodeMirror( textarea, mediawiki() );
10 | cmLogEditFeatureSpy = jest.spyOn( cm, 'logEditFeature' );
11 | cm.initialize();
12 | otherTextarea = document.createElement( 'textarea' );
13 | } );
14 |
15 | afterEach( () => {
16 | mw.hook.mockHooks = {};
17 | document.body.innerHTML = '';
18 | } );
19 |
20 | describe( 'CodeMirrorChild', () => {
21 | it( 'should use the same langExtension as the primary if none is given', () => {
22 | const childCm = new CodeMirrorChild( otherTextarea, cm );
23 | childCm.initialize();
24 | expect( childCm.langExtension ).toBe( cm.langExtension );
25 | } );
26 |
27 | it( 'should toggle when the primary instance is toggled', () => {
28 | const childCm = new CodeMirrorChild( otherTextarea, cm );
29 | childCm.initialize();
30 | cm.toggle( false );
31 | expect( cm.isActive ).toBe( false );
32 | expect( childCm.isActive ).toBe( false );
33 | } );
34 |
35 | it( 'should sync preferences with the primary', () => {
36 | const childCm = new CodeMirrorChild( otherTextarea, cm );
37 | childCm.initialize();
38 | expect( childCm.preferences.getPreference( 'lineNumbering' ) ).toBe( true );
39 | cm.preferences.setPreference( 'lineNumbering', false );
40 | expect( childCm.preferences.getPreference( 'lineNumbering' ) ).toBe( false );
41 | expect( cm.preferences.getPreference( 'lineNumbering' ) ).toBe( false );
42 | } );
43 |
44 | it( 'should not log activation or feature usage', () => {
45 | const childCm = new CodeMirrorChild( otherTextarea, cm );
46 | const childSpy = jest.spyOn( childCm, 'logEditFeature' );
47 | childCm.initialize();
48 | expect( cm.preferences.getPreference( 'lineNumbering' ) ).toBe( true );
49 | expect( childCm.preferences.getPreference( 'lineNumbering' ) ).toBe( true );
50 | expect( cmLogEditFeatureSpy ).toHaveBeenCalledWith( 'activated' );
51 | expect( cmLogEditFeatureSpy ).toHaveBeenCalledWith( 'prefs-lineNumbering' );
52 | expect( childSpy ).not.toHaveBeenCalledWith( 'prefs-lineNumbering' );
53 | } );
54 |
55 | it( 'should not make API requests to update preferences', () => {
56 | const childCm = new CodeMirrorChild( otherTextarea, cm );
57 | const childSpy = jest.spyOn( childCm.preferences, 'setPreferencesInternal' );
58 | childCm.initialize();
59 | cm.preferences.setPreference( 'lineNumbering', false );
60 | expect( childSpy ).not.toHaveBeenCalled();
61 | } );
62 | } );
63 |
--------------------------------------------------------------------------------
/resources/modes/mediawiki/codemirror.mediawiki.colorblind.less:
--------------------------------------------------------------------------------
1 | /*
2 | * This style sheet overrides colors in mediawiki.less
3 | * with more colorblind-friendly colors.
4 | */
5 | @import '../../codemirror.mixins.less';
6 |
7 | /* copied from codemirror.mediawiki.less */
8 | @table-color: #d08;
9 | @table-color-dark: #ff5edd;
10 |
11 | .cm-mw-colorblind-colors {
12 | /* misc */
13 | .cm-mw-section-header,
14 | .cm-mw-list,
15 | .cm-mw-doubleUnderscore,
16 | .cm-mw-signature,
17 | .cm-mw-hr,
18 | .cm-mw-redirect,
19 | .cm-mw-indenting,
20 | .cm-mw-apostrophes {
21 | color: #e4a400;
22 | }
23 |
24 | /* tags */
25 | .cm-mw-exttag-name,
26 | .cm-mw-exttag-bracket,
27 | .cm-mw-exttag-attribute,
28 | .cm-mw-exttag-attribute-value,
29 | .cm-mw-htmltag-name,
30 | .cm-mw-htmltag-bracket,
31 | .cm-mw-htmltag-attribute,
32 | .cm-mw-htmltag-attribute-value {
33 | color: #56b4e9;
34 | }
35 |
36 | /* table */
37 | .cm-mw-table-definition-value {
38 | color: @table-color;
39 | .darkmode( color, @table-color-dark );
40 | }
41 |
42 | /* templates */
43 | .cm-mw-template,
44 | .cm-mw-template-name,
45 | .cm-mw-template-name-mnemonic,
46 | .cm-mw-template-argument-name,
47 | .cm-mw-template-delimiter,
48 | .cm-mw-template-bracket {
49 | color: #9c3a00;
50 | }
51 |
52 | /* variables */
53 | .cm-mw-templatevariable,
54 | .cm-mw-templatevariable-name,
55 | .cm-mw-templatevariable-bracket,
56 | .cm-mw-templatevariable-delimiter {
57 | color: #009e73;
58 | }
59 |
60 | /* remove background colors */
61 | .cm-mw-matching,
62 | .cm-mw-skipformatting,
63 | .cm-mw-doubleUnderscore,
64 | .cm-mw-signature,
65 | .cm-mw-hr,
66 | .cm-mw-redirect,
67 | pre.CodeMirror-line-like.cm-mw-exttag,
68 | .cm-mw-exttag,
69 | .cm-mw-tag-pre,
70 | .cm-mw-tag-nowiki,
71 | .cm-mw-template2-ground,
72 | .cm-mw-template3-ground,
73 | .cm-mw-template-ext-ground,
74 | .cm-mw-template-ext2-ground,
75 | .cm-mw-template-ext3-ground,
76 | .cm-mw-template-link-ground,
77 | .cm-mw-template-ext-link-ground,
78 | .cm-mw-template-ext2-link-ground,
79 | .cm-mw-template-ext3-link-ground,
80 | .cm-mw-template2-ext-ground,
81 | .cm-mw-template2-ext2-ground,
82 | .cm-mw-template2-ext3-ground,
83 | .cm-mw-template2-link-ground,
84 | .cm-mw-template2-ext-link-ground,
85 | .cm-mw-template2-ext2-link-ground,
86 | .cm-mw-template2-ext3-link-ground,
87 | .cm-mw-template3-ext-ground,
88 | .cm-mw-template3-ext2-ground,
89 | .cm-mw-template3-ext3-ground,
90 | .cm-mw-template3-link-ground,
91 | .cm-mw-template3-ext-link-ground,
92 | .cm-mw-template3-ext2-link-ground,
93 | .cm-mw-template3-ext3-link-ground,
94 | .cm-mw-ext-ground,
95 | .cm-mw-ext2-ground,
96 | .cm-mw-ext3-ground,
97 | .cm-mw-ext-link-ground,
98 | .cm-mw-ext2-link-ground,
99 | .cm-mw-ext3-link-ground,
100 | .cm-mw-link-ground {
101 | background-color: transparent;
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/resources/legacy/modules/ve-cm/ve.ui.CodeMirror.less:
--------------------------------------------------------------------------------
1 | @import 'mediawiki.skin.variables.less';
2 |
3 | .ve-init-mw-desktopArticleTarget {
4 | .CodeMirror {
5 | height: auto;
6 | z-index: -1;
7 | position: absolute;
8 | top: 0;
9 | left: 0;
10 | width: 100%;
11 | box-sizing: border-box;
12 | pointer-events: none;
13 | background: transparent;
14 |
15 | // Core VE default padding
16 | padding: 1.5em;
17 |
18 | // Skin specific paddings
19 | .skin-vector & {
20 | padding: 0 1rem;
21 |
22 | @media screen and ( min-width: 982px ) {
23 | .skin-vector-legacy& {
24 | padding: 0 1.5rem;
25 | }
26 | }
27 | }
28 |
29 | .skin-minerva &,
30 | .skin-monobook & {
31 | padding: 0;
32 | }
33 | }
34 |
35 | .CodeMirror-lines,
36 | .CodeMirror pre.CodeMirror-line,
37 | .CodeMirror pre.CodeMirror-line-like {
38 | padding: 0;
39 | }
40 |
41 | .CodeMirror-scroll {
42 | margin-right: 0;
43 | overflow: auto !important; /* stylelint-disable-line declaration-no-important */
44 | }
45 |
46 | .CodeMirror-sizer {
47 | border-right: 0;
48 | }
49 |
50 | .CodeMirror-selected {
51 | display: none;
52 | }
53 |
54 | .CodeMirror pre.cm-mw-section-1,
55 | .CodeMirror pre.cm-mw-section-2 {
56 | font-size: inherit;
57 | line-height: inherit;
58 | }
59 |
60 | // Ensure surfaces are using identical font rules
61 | .CodeMirror-code,
62 | .CodeMirror-code *,
63 | .ve-ui-mwWikitextSurface .ve-ce-paragraphNode {
64 | // The following are already set by mw-editfont-monospace on the parent: font-size, font-family
65 | line-height: 1.5em;
66 | word-wrap: break-word;
67 | // Support: Chrome<76, Firefox<69
68 | // Fallback for browsers which don't support break-spaces
69 | white-space: pre-wrap;
70 | // T347902
71 | white-space: break-spaces;
72 | word-break: normal;
73 | -webkit-hyphens: manual;
74 | -moz-hyphens: manual;
75 | -ms-hyphens: manual;
76 | hyphens: manual;
77 | -webkit-font-variant-ligatures: contextual;
78 | font-variant-ligatures: contextual;
79 |
80 | // Monospace fonts can change width when bold
81 | // stylelint-disable-next-line declaration-no-important
82 | font-weight: normal !important;
83 | // T252965
84 | line-break: initial;
85 | }
86 |
87 | .CodeMirror-gutters {
88 | background-color: transparent;
89 | border-right: 0;
90 | }
91 | .CodeMirror-linenumber {
92 | color: @color-subtle;
93 | padding-left: 0;
94 | }
95 | .CodeMirror-linenumber-padding {
96 | // Create a 12px padding between the line numbers and content.
97 | width: 8px;
98 | }
99 | }
100 |
101 | .ve-ce-documentNode-codeEditor-hide {
102 | opacity: 0.4;
103 |
104 | &::selection,
105 | & *::selection {
106 | background: #6da9f7 !important; /* stylelint-disable-line declaration-no-important */
107 | }
108 | }
109 |
110 | .ve-ce-documentNode-codeEditor-webkit-hide {
111 | -webkit-text-fill-color: transparent;
112 | }
113 |
--------------------------------------------------------------------------------
/resources/codemirror.child.js:
--------------------------------------------------------------------------------
1 | const { Extension, Prec } = require( 'ext.CodeMirror.v6.lib' );
2 | const CodeMirror = require( 'ext.CodeMirror.v6' );
3 |
4 | /**
5 | * A `CodeMirrorChild` is a CodeMirror instance controlled by a 'primary'
6 | * {@link CodeMirror} instance.
7 | *
8 | * This will sync preference changes from the primary instance and add hook
9 | * handlers to toggle the child instance when the primary instance is toggled.
10 | *
11 | * @example
12 | * const cm = new CodeMirror( textarea, languageExtension );
13 | * const cmChild = new cm.child( otherTextarea, cm, languageExtension );
14 | * cm.initialize();
15 | * // Will apply to both instances.
16 | * cm.toggle();
17 | * @class
18 | * @extends CodeMirror
19 | */
20 | class CodeMirrorChild extends CodeMirror {
21 | /**
22 | * Instantiate a child CodeMirror instance given a primary instance.
23 | *
24 | * @param {HTMLTextAreaElement} textarea Child textarea element.
25 | * @param {CodeMirror} primaryInstance The primary CodeMirror instance to sync with.
26 | * @param {LanguageSupport|Extension} [langExtension] Language support and its extension(s).
27 | * If not provided, the primary instance's language support will be used.
28 | * @override
29 | */
30 | constructor( textarea, primaryInstance, langExtension ) {
31 | super( textarea, langExtension || primaryInstance.langExtension );
32 |
33 | /**
34 | * The primary CodeMirror instance.
35 | *
36 | * @type {CodeMirror}
37 | */
38 | this.primaryInstance = primaryInstance;
39 | }
40 |
41 | /**
42 | * @inheritDoc
43 | */
44 | initialize( extensions = this.defaultExtensions ) {
45 | super.initialize( extensions );
46 |
47 | // Register the preferences keymap for this instance to route to the primary instance.
48 | this.keymap.registerKeyBinding(
49 | Object.assign( {}, this.keymap.keymapHelpRegistry.other.preferences, {
50 | prec: Prec.highest,
51 | run: () => {
52 | this.primaryInstance.preferences.toggle( this.primaryInstance.view, true );
53 | return true;
54 | }
55 | } ),
56 | this.view
57 | );
58 |
59 | // Toggle this CodeMirror instance when the primary instance is toggled.
60 | mw.hook( 'ext.CodeMirror.toggle' ).add( ( enabled, _cm, textarea ) => {
61 | if ( textarea !== this.textarea && enabled !== this.isActive ) {
62 | this.toggle( enabled );
63 | }
64 | } );
65 |
66 | // Sync preferences between this instance and the primary instance.
67 | mw.hook( 'ext.CodeMirror.preferences.apply' ).add( ( prefName, enabled ) => {
68 | if ( enabled !== this.preferences.getPreference( prefName ) ) {
69 | this.extensionRegistry.toggle( prefName, this.view, enabled );
70 | // Only update the preferences property directly to avoid
71 | // making API calls already made by the primary instance.
72 | this.preferences.preferences[ prefName ] = enabled;
73 | }
74 | } );
75 | }
76 |
77 | // Don't log activation or feature usage for child instances.
78 |
79 | /**
80 | * @override
81 | */
82 | // eslint-disable-next-line no-unused-vars
83 | logEditFeature( action ) {}
84 |
85 | /**
86 | * @override
87 | */
88 | setupFeatureLogging() {}
89 | }
90 |
91 | module.exports = CodeMirrorChild;
92 |
--------------------------------------------------------------------------------
/resources/ve-cm/ve.ui.CodeMirror.v6.less:
--------------------------------------------------------------------------------
1 | @import 'mediawiki.mixins.less';
2 |
3 | .ve-init-target {
4 | .cm-editor {
5 | background: transparent;
6 | border: 0;
7 | box-sizing: border-box;
8 | height: auto;
9 | left: 0;
10 | pointer-events: none;
11 | // stylelint-disable-next-line declaration-no-important
12 | position: absolute !important;
13 | top: 0;
14 | width: 100%;
15 | }
16 |
17 | .cm-gutterElement {
18 | /* @noflip */
19 | padding: 0 3px 0 4px !important; /* stylelint-disable-line declaration-no-important */
20 | }
21 |
22 | .cm-gutters {
23 | background-color: transparent !important; /* stylelint-disable-line declaration-no-important */
24 | .padding-inline( 0, 8px );
25 | /* @noflip */
26 | border-right: 0;
27 | }
28 |
29 | .cm-focused {
30 | outline: 0;
31 | }
32 |
33 | .cm-line {
34 | padding: 0;
35 | }
36 |
37 | .cm-content {
38 | padding: 0;
39 | // T381714: Disable the automatic minimum size, so it can shrink
40 | // See https://www.w3.org/TR/css-flexbox-1/#min-size-auto
41 | min-width: 0;
42 | }
43 |
44 | .CodeMirror-selected {
45 | display: none;
46 | }
47 |
48 | // Ensure surfaces are using identical font rules
49 | .cm-scroller,
50 | .cm-scroller *,
51 | .ext-codemirror-wrapper .ve-ce-paragraphNode {
52 | // The following are already set by mw-editfont-monospace on the parent: font-size, font-family
53 | // T382006: Chromium browsers have rounding issues between 1.5 and 1.5em
54 | // Site styles or user styles may also affect the result unintentionally, so override it anyway
55 | line-height: 1.5;
56 | word-wrap: break-word;
57 | // Support: Chrome<76, Firefox<69
58 | // Fallback for browsers which don't support break-spaces
59 | white-space: pre-wrap;
60 | // T347902
61 | white-space: break-spaces;
62 | word-break: normal;
63 | -webkit-hyphens: manual;
64 | -moz-hyphens: manual;
65 | -ms-hyphens: manual;
66 | hyphens: manual;
67 | -webkit-font-variant-ligatures: contextual;
68 | font-variant-ligatures: contextual;
69 |
70 | // Monospace fonts can change width when bold
71 | // stylelint-disable-next-line declaration-no-important
72 | font-weight: normal !important;
73 | // T252965
74 | line-break: initial;
75 | // T357482#10111614
76 | font-style: normal;
77 | }
78 |
79 | .cm-mw-section-1,
80 | .cm-mw-section-1 ~ *,
81 | .cm-mw-section-2,
82 | .cm-mw-section-2 ~ *,
83 | /* TODO: remove overqualified `span` after CM6 upgrade */
84 | span.cm-mw-section-3 ~ *,
85 | span.cm-mw-section-4 ~ *,
86 | span.cm-mw-section-5 ~ *,
87 | span.cm-mw-section-6 ~ * {
88 | font-size: inherit;
89 | line-height: inherit;
90 | font-weight: inherit;
91 | }
92 |
93 | .cm-activeLineGutter {
94 | background-color: transparent;
95 | border-right: 0;
96 | }
97 | }
98 |
99 | .ext-codemirror-wrapper .ve-ce-documentNode-codeEditor-hide {
100 | opacity: 0.4;
101 |
102 | &::selection,
103 | & *::selection {
104 | background: #6da9f7 !important; /* stylelint-disable-line declaration-no-important */
105 | }
106 | }
107 |
108 | .ext-codemirror-wrapper .ve-ce-documentNode-codeEditor-webkit-hide {
109 | -webkit-text-fill-color: transparent;
110 | }
111 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | The CodeMirror extension provides syntax highlighting in MediaWiki wikitext editors using
2 | the [CodeMirror library](https://codemirror.net/).
3 |
4 | Extension homepage:
5 | [https://www.mediawiki.org/wiki/Extension:CodeMirror](https://www.mediawiki.org/wiki/Special:MyLanguage/Extension:CodeMirror)
6 |
7 | JS documentation:
8 | [https://doc.wikimedia.org/CodeMirror](https://doc.wikimedia.org/CodeMirror)
9 |
10 | ## Development
11 |
12 | ### Preface
13 |
14 | Extension:CodeMirror is currently in the process of being upgraded to the new major version, CodeMirror 6.
15 | See the [change log](https://www.mediawiki.org/wiki/Extension:CodeMirror#Change_log) for details.
16 |
17 | Use of CodeMirror 6 is controlled by the `wgCodeMirrorV6` configuration setting, or by
18 | passing in `cm6enable=1` in the URL query string.
19 |
20 | CodeMirror 6 requires the use of NPM to bundle the dependencies. These are built using
21 | [Rollup](https://rollupjs.org/) and packaged as ResourceLoader-compatible modules under `lib/`.
22 | If you make changes to the versions of `@codemirror` or `@lezer` packages,
23 | you will need to run `npm run build` to update the ResourceLoader modules.
24 |
25 | ### NPM commands
26 |
27 | _NOTE: Consider using [Fresh](https://gerrit.wikimedia.org/g/fresh/) to run these tasks._
28 |
29 | * `npm install` to install dependencies.
30 | * `npm run doc` to generate the API documentation.
31 | * `npm test` to run the linting tools, JavaScript unit tests, and build checks.
32 | * `npm run test:lint` for linting of JS/LESS/CSS.
33 | * `npm run test:lint:js` for linting of just JavaScript.
34 | * `npm run test:lint:styles` for linting of just Less/CSS.
35 | * `npm run test:i18n` for linting of i18n messages with banana-checker.
36 | * `npm run test:unit` for the new Jest unit tests.
37 | * `npm run selenium-test` for the Selenium tests.
38 | * `npm run build` to rebundle the CodeMirror library. If changes are made to the `@codemirror`
39 | or `@lezer` dependencies in [package.json](package.json), this command *must* be run before
40 | sending the patch or CI will fail. This also calls the `build:eslint`,
41 | `build:stylelint`, and `build:luacheck` commands.
42 | * `npm run build:eslint` to rebundle the ESLint library. If changes are made to the
43 | `@bhsd/eslint-browserify` dependency in [package.json](package.json) or the
44 | [JavaScript worker](resources/workers/javascript/worker.js), this command *must* be run
45 | before sending the patch.
46 | * `npm run build:stylelint` to rebundle the Stylelint library. If changes are made to the
47 | `@bhsd/stylelint-browserify` dependency in [package.json](package.json) or the
48 | [CSS worker](resources/workers/css/worker.js), this command *must* be run before sending the
49 | patch.
50 | * `npm run build:luacheck` to rebundle the LuaCheck library. If changes are made to the
51 | `luacheck-browserify` dependency in [package.json](package.json) or the
52 | [Lua worker](resources/workers/lua/worker.js), this command *must* be run before
53 | sending the patch.
54 | * `npm run build:wikilint` to rebundle the WikiParser-Node library. If changes are made to the
55 | `wikiparser-node` dependency in [package.json](package.json) or the
56 | [MediaWiki worker](resources/workers/mediawiki/worker.js), this command *must* be run before
57 | sending the patch.
58 | * Older QUnit tests are in `resources/mode/mediawiki/tests/qunit/`. These have been
59 | replaced and will be removed after the CodeMirror 6 upgrade is complete.
60 |
--------------------------------------------------------------------------------
/i18n/pt-br.json:
--------------------------------------------------------------------------------
1 | {
2 | "@metadata": {
3 | "authors": [
4 | "!Silent",
5 | "Eduardo Addad de Oliveira",
6 | "Eduardoaddad",
7 | "Felipe L. Ewald",
8 | "Fúlvio"
9 | ]
10 | },
11 | "codemirror-desc": "Fornece um realce de sintaxe no editor de wikitexto",
12 | "codemirror-toggle-label": "Realce de sintaxe",
13 | "codemirror-toggle-label-short": "Sintaxe",
14 | "codemirror-prefs-summary": "Você pode saber mais sobre esse recurso lendo a página de ajuda [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Extension:CodeMirror].",
15 | "codemirror-prefs-enable": "Ativar realce de sintaxe",
16 | "codemirror-prefs-title": "Preferências de realce de sintaxe",
17 | "codemirror-prefs-help": "ajuda",
18 | "codemirror-prefs-codefolding": "Ativar dobramento de código",
19 | "codemirror-prefs-autocomplete": "Ativar o preenchimento automático",
20 | "codemirror-prefs-openlinks": "Abrir links com a tecla modificadora + clique",
21 | "codemirror-prefs-bidiisolation": "Isolar texto bidirecional",
22 | "codemirror-prefs-bracketmatching": "Ativar a correspondência de colchetes",
23 | "codemirror-prefs-linenumbering": "Mostrar números de linha",
24 | "codemirror-prefs-specialchars": "Mostrar caracteres especiais",
25 | "codemirror-prefs-whitespace": "Destacar espaços em branco",
26 | "codemirror-prefs-colorblind": "Ativar esquema compatível com daltônicos para realce de sintaxe ao editar o wikitexto",
27 | "codemirror-prefs-colorblind-help": "Se você usar um gadget para realce de sintaxe, essa preferência não funcionará.",
28 | "codemirror-close": "Fechar",
29 | "codemirror-find": "Procurar",
30 | "codemirror-next": "Buscar o próximo",
31 | "codemirror-previous": "Buscar o anterior",
32 | "codemirror-all": "Todos",
33 | "codemirror-match-case": "Coincidir maiúsculas e minúsculas",
34 | "codemirror-regexp": "Expressão regular",
35 | "codemirror-regexp-invalid": "Expressão regular inválida",
36 | "codemirror-by-word": "Mundo inteiro",
37 | "codemirror-replace": "Substituir",
38 | "codemirror-replace-placeholder": "Substituir",
39 | "codemirror-replace-all": "Substituir todos",
40 | "codemirror-done": "Feito",
41 | "codemirror-find-results": "$1 de $2",
42 | "codemirror-goto-line-go": "Ir",
43 | "codemirror-keymap-help-title": "Atalhos de teclado",
44 | "codemirror-keymap-help-close": "Fechar",
45 | "codemirror-keymap-accessibility": "Acessibilidade",
46 | "codemirror-keymap-textstyling": "Estilo de texto",
47 | "codemirror-keymap-bold": "Negrito",
48 | "codemirror-keymap-italic": "Itálico",
49 | "codemirror-keymap-link": "Link",
50 | "codemirror-keymap-preformatted": "Pré-formatado",
51 | "codemirror-keymap-blockquote": "Bloco de citação",
52 | "codemirror-keymap-heading": "Título (1-6)",
53 | "codemirror-keymap-heading-n": "Título $1",
54 | "codemirror-keymap-search": "Pesquisando",
55 | "codemirror-keymap-find": "Buscar e substituir",
56 | "codemirror-keymap-insert": "Inserir",
57 | "codemirror-keymap-reference": "Referência",
58 | "codemirror-keymap-comment": "Comentário",
59 | "codemirror-keymap-autocomplete": "Auto-completar",
60 | "codemirror-keymap-other": "Outro",
61 | "codemirror-keymap-direction": "Alterar direcionalidade",
62 | "prefs-accessibility": "Acessibilidade",
63 | "prefs-syntax-highlighting": "Realce de sintaxe",
64 | "codemirror-beta-feature-title": "Destaque de sintaxe aprimorado",
65 | "codemirror-beta-feature-description": "Obtenha acesso antecipado aos novos recursos de edição e realce de sintaxe, como dobramento de código e preenchimento automático."
66 | }
67 |
--------------------------------------------------------------------------------
/i18n/ka.json:
--------------------------------------------------------------------------------
1 | {
2 | "@metadata": {
3 | "authors": [
4 | "Გიო ოქრო"
5 | ]
6 | },
7 | "codemirror-desc": "უზრუნველყოფს ვიკიტექსტის რედაქტორში სინტაქსის განათებას",
8 | "codemirror-toggle-label": "სინტაქსის განათება",
9 | "codemirror-toggle-label-short": "სინტაქსი",
10 | "codemirror-prefs-summary": "შეგიძლიათ მეტი გაიგოთ ამ ფუნქციის შესახებ [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Extension:CodeMirror დახმარების გვერდის] წაკითხვით.",
11 | "codemirror-prefs-enable": "ვიკიტექსტის სინტაქსის განათების ჩართვა",
12 | "codemirror-prefs-title": "სინტაქსი განათების პარამეტრები",
13 | "codemirror-prefs-help": "დახმარება",
14 | "codemirror-prefs-codefolding": "კოდის ჩაკეცვის ჩართვა",
15 | "codemirror-prefs-autocomplete": "თვითდასრულების ჩართვა",
16 | "codemirror-prefs-openlinks": "ბმულების გახსნა მოდიფიკატორ ღილაკზე (Ctrl/Cmd) დაჭერითა და დაწკაპუნებით",
17 | "codemirror-prefs-bidiisolation": "ორმიმართულებიანი ტექსტის იზოლირება",
18 | "codemirror-prefs-bracketmatching": "ფრჩხილების შესაბამისობების ჩართვა",
19 | "codemirror-prefs-linenumbering": "სტრიქონის ნომრების ჩვენება",
20 | "codemirror-prefs-linewrapping": "სტრიქონის გადატანა",
21 | "codemirror-prefs-activeline": "აქტიური სტრიქონის განათება",
22 | "codemirror-prefs-specialchars": "სპეციალური სიმბოლოების ჩვენება",
23 | "codemirror-prefs-colorblind": "ვიკიტექსტის რედაქტირებისას დალტონიზმზე ადაპტირებული სინტაქსის განათების ჩართვა",
24 | "codemirror-prefs-colorblind-help": "თუკი სინტაქსის გასანათებლად გაჯეტს იყენებთ, ეს ფუნქცია არ იმუშავებს.",
25 | "codemirror-close": "დახურვა",
26 | "codemirror-find": "ძიება",
27 | "codemirror-next": "შემდეგის მოძიება",
28 | "codemirror-previous": "წინას მოძიება",
29 | "codemirror-all": "ყველა",
30 | "codemirror-all-tooltip": "ყველა დამთხვევის არჩევა",
31 | "codemirror-match-case": "რეგისტრის გათვალისწინება",
32 | "codemirror-regexp": "რეგულარული გამოსახულება",
33 | "codemirror-regexp-invalid": "არასწორი რეგულარული გამოსახულება",
34 | "codemirror-by-word": "მთელი სიტყვა",
35 | "codemirror-replace": "ჩანაცვლება",
36 | "codemirror-replace-placeholder": "ჩანაცვლება",
37 | "codemirror-replace-all": "ყველას ჩანაცვლება",
38 | "codemirror-done": "გაკეთდა",
39 | "codemirror-find-results": "$1 / $2",
40 | "codemirror-goto-line": "სტრიქონზე გადასვლა",
41 | "codemirror-goto-line-go": "გადასვლა",
42 | "codemirror-control-character": "საკონტროლო სიმბოლო $1",
43 | "codemirror-special-char-null": "ნულოვანი სიმბოლო",
44 | "codemirror-special-char-bell": "ზარის სიმბოლო",
45 | "codemirror-special-char-backspace": "ერთით უკან დაბრუნება",
46 | "codemirror-special-char-newline": "ახალი ხაზი",
47 | "codemirror-special-char-vertical-tab": "ვერტიკალური ტაბულაცია",
48 | "codemirror-special-char-carriage-return": "კარეტის დაბრუნება",
49 | "codemirror-special-char-escape": "გამოსვლის სიმბოლო",
50 | "codemirror-special-char-nbsp": "უწყვეტი შორისი",
51 | "codemirror-special-char-zero-width-space": "ნულოვანი სიგანის შორისი",
52 | "codemirror-special-char-zero-width-non-joiner": "ნულოვანი სიგანის გამყოფი",
53 | "codemirror-special-char-zero-width-joiner": "ნულოვანი სიგანის შემაერთებელი",
54 | "codemirror-special-char-left-to-right-mark": "ნიშანი „მარცხნიდან მარჯვნივ“",
55 | "codemirror-special-char-right-to-left-mark": "ნიშანი „მარჯვნივ მარცხნივ“",
56 | "codemirror-special-char-line-separator": "სტრიქონის გამყოფი",
57 | "codemirror-special-char-left-to-right-override": "გადაფარვა მარცხნიდან მარჯვნივ",
58 | "codemirror-special-char-right-to-left-override": "გადაფარვა მარჯვნიდან მარცხნივ",
59 | "prefs-accessibility": "ხელმისაწვდომობა"
60 | }
61 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "codemirror",
3 | "private": true,
4 | "scripts": {
5 | "build": "rollup -c && npm run build:stylelint && npm run build:eslint && npm run build:luacheck && npm run build:wikilint",
6 | "build:stylelint": "esbuild resources/workers/css/worker.js --charset=utf8 --bundle --minify --target=es2017 --format=iife --outfile=resources/workers/css/worker.min.js",
7 | "build:eslint": "esbuild resources/workers/javascript/worker.js --charset=utf8 --bundle --minify --target=es2017 --format=iife --outfile=resources/workers/javascript/worker.min.js",
8 | "build:luacheck": "esbuild resources/workers/lua/worker.js --charset=utf8 --bundle --minify --target=es2017 --format=iife --outfile=resources/workers/lua/worker.min.js",
9 | "build:wikilint": "esbuild resources/workers/mediawiki/worker.js --charset=utf8 --bundle --minify --target=es2017 --format=iife --outfile=resources/workers/mediawiki/worker.min.js",
10 | "test": "npm run test:lint && npm run test:unit && npm run check-built-assets",
11 | "test:lint": "npm run test:lint:styles && npm run test:lint:js && npm run test:lint:i18n",
12 | "test:lint:js": "eslint --cache .",
13 | "test:lint:styles": "stylelint --cache \"resources/**/*.less\"",
14 | "test:lint:i18n": "banana-checker i18n/",
15 | "test:unit": "jest",
16 | "coverage": "npm run test:unit",
17 | "check-built-assets": "{ git status resources/lib/ | grep \"nothing to commit, working tree clean\"; } && { echo 'CHECKING BUILD SOURCES ARE COMMITTED' && npm run build && git status resources/lib/ | grep \"nothing to commit, working tree clean\" || { npm run node-debug; false; }; }",
18 | "node-debug": "node -v && npm -v && echo 'ERROR: Please ensure that production assets have been built with `npm run build` and commited, and that you are using the correct version of Node/NPM.'",
19 | "selenium-test": "wdio tests/selenium/wdio.conf.js",
20 | "doc": "jsdoc -c jsdoc.json"
21 | },
22 | "devDependencies": {
23 | "@bhsd/eslint-browserify": "^8.57.1",
24 | "@bhsd/stylelint-browserify": "^16.24.0",
25 | "@codemirror/autocomplete": "^6.18.6",
26 | "@codemirror/commands": "^6.8.1",
27 | "@codemirror/lang-css": "6.3.1",
28 | "@codemirror/lang-html": "6.4.9",
29 | "@codemirror/lang-javascript": "6.2.2",
30 | "@codemirror/lang-json": "6.0.1",
31 | "@codemirror/lang-vue": "0.1.3",
32 | "@codemirror/language": "^6.11.0",
33 | "@codemirror/legacy-modes": "^6.5.1",
34 | "@codemirror/lint": "^6.8.4",
35 | "@codemirror/search": "^6.5.10",
36 | "@codemirror/state": "^6.5.2",
37 | "@codemirror/theme-one-dark": "^6.1.2",
38 | "@codemirror/view": "6.36.8",
39 | "@lezer/common": "1.2.3",
40 | "@lezer/highlight": "1.2.1",
41 | "@lezer/lr": "1.4.2",
42 | "@rollup/plugin-alias": "5.1.1",
43 | "@rollup/plugin-node-resolve": "15.2.3",
44 | "@typescript-eslint/eslint-plugin": "^7.18.0",
45 | "@wdio/cli": "9.19.2",
46 | "@wdio/junit-reporter": "9.19.2",
47 | "@wdio/local-runner": "9.19.2",
48 | "@wdio/mocha-framework": "9.19.2",
49 | "@wdio/spec-reporter": "9.19.2",
50 | "@wikimedia/mw-node-qunit": "7.2.0",
51 | "dotenv": "8.2.0",
52 | "esbuild": "^0.25.4",
53 | "eslint": "^8.57.1",
54 | "eslint-config-wikimedia": "0.32.3",
55 | "grunt-banana-checker": "0.13.0",
56 | "jest": "29.7.0",
57 | "jest-environment-jsdom": "29.7.0",
58 | "jquery": "3.7.1",
59 | "jsdoc": "4.0.5",
60 | "jsdoc-wmf-theme": "1.1.0",
61 | "luacheck-browserify": "^0.7.1",
62 | "rollup": "4.22.4",
63 | "stylelint-config-wikimedia": "0.18.0",
64 | "wdio-mediawiki": "4.1.3",
65 | "wikiparser-node": "^1.27.1"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/resources/codemirror.matchbrackets.js:
--------------------------------------------------------------------------------
1 | const {
2 | Config,
3 | Decoration,
4 | EditorState,
5 | Extension,
6 | MatchResult,
7 | SyntaxNode,
8 | bracketMatching,
9 | matchBrackets,
10 | syntaxTree
11 | } = require( 'ext.CodeMirror.v6.lib' );
12 |
13 | /**
14 | * Find surrounding brackets in the syntax tree.
15 | *
16 | * @param {SyntaxNode|null} node
17 | * @param {number} pos
18 | * @param {string} brackets
19 | * @return {MatchResult|undefined}
20 | * @internal
21 | * @private
22 | */
23 | const findSurroundingBrackets = ( node, pos, brackets ) => {
24 | let parent = node;
25 | while ( parent ) {
26 | const { firstChild, lastChild } = parent;
27 | if ( firstChild && lastChild ) {
28 | const i = brackets.indexOf( firstChild.name ),
29 | j = brackets.indexOf( lastChild.name );
30 | if (
31 | i !== -1 && j !== -1 && i % 2 === 0 && j % 2 === 1 &&
32 | firstChild.from < pos && lastChild.to > pos
33 | ) {
34 | return { start: firstChild, end: lastChild, matched: true };
35 | }
36 | }
37 | ( { parent } = parent );
38 | }
39 | return undefined;
40 | };
41 |
42 | /**
43 | * Find surrounding brackets in the plain text.
44 | *
45 | * @param {EditorState} state
46 | * @param {number} pos
47 | * @param {Config} config
48 | * @return {MatchResult|null}
49 | * @internal
50 | * @private
51 | */
52 | const findSurroundingPlainBrackets = ( state, pos, config ) => {
53 | const { brackets, maxScanDistance } = config,
54 | re = new RegExp(
55 | `[${
56 | [ ...brackets ].filter( ( _, i ) => i % 2 )
57 | .map( ( c ) => c === ']' ? '\\]' : c )
58 | .join( '' )
59 | }]`,
60 | 'g'
61 | ),
62 | str = state.sliceDoc( pos, pos + maxScanDistance );
63 | let mt = re.exec( str );
64 | while ( mt ) {
65 | const result = matchBrackets( state, pos + mt.index + 1, -1, config );
66 | if ( result && result.end && result.end.to <= pos ) {
67 | return result;
68 | }
69 | mt = re.exec( str );
70 | }
71 | return null;
72 | };
73 |
74 | /**
75 | * Highlight surrounding brackets in addition to matching brackets.
76 | *
77 | * @param {Config} configs
78 | * @return {Extension}
79 | * @internal
80 | * @private
81 | */
82 | module.exports = ( configs ) => {
83 | const extension = bracketMatching( configs ),
84 | [ { facet }, [ field ] ] = extension;
85 | Object.assign( field, {
86 | updateF( value, { state, docChanged, selection } ) {
87 | if ( !docChanged && !selection ) {
88 | return value;
89 | }
90 | const decorations = [],
91 | config = state.facet( facet ),
92 | { afterCursor, brackets, renderMatch } = config;
93 | for ( const { empty, head } of state.selection.ranges ) {
94 | if ( !empty ) {
95 | continue;
96 | }
97 | const tree = syntaxTree( state ),
98 | match = matchBrackets( state, head, -1, config ) ||
99 | head > 0 && matchBrackets( state, head - 1, 1, config ) ||
100 | afterCursor && (
101 | matchBrackets( state, head, 1, config ) ||
102 | head < state.doc.length && matchBrackets( state, head + 1, -1, config )
103 | ) ||
104 | findSurroundingBrackets( tree.resolveInner( head, -1 ), head, brackets ) ||
105 | afterCursor &&
106 | findSurroundingBrackets( tree.resolveInner( head, 1 ), head, brackets ) ||
107 | findSurroundingPlainBrackets( state, head, config );
108 | if ( match ) {
109 | decorations.push( ...renderMatch( match, state ) );
110 | }
111 | }
112 | return Decoration.set( decorations, true );
113 | }
114 | } );
115 | return extension;
116 | };
117 |
--------------------------------------------------------------------------------
/resources/modes/mediawiki/codemirror.mediawiki.bidiIsolation.js:
--------------------------------------------------------------------------------
1 | const {
2 | Decoration,
3 | DecorationSet,
4 | Direction,
5 | EditorView,
6 | PluginSpec,
7 | Prec,
8 | RangeSet,
9 | RangeSetBuilder,
10 | ViewPlugin,
11 | ViewUpdate,
12 | syntaxTree
13 | } = require( 'ext.CodeMirror.v6.lib' );
14 | const mwModeConfig = require( './codemirror.mediawiki.config.js' );
15 |
16 | /**
17 | * @type {Decoration}
18 | * @private
19 | */
20 | const isolate = Decoration.mark( {
21 | class: 'cm-bidi-isolate',
22 | bidiIsolate: Direction.LTR
23 | } );
24 |
25 | /**
26 | * @param {EditorView} view
27 | * @return {RangeSet}
28 | * @private
29 | */
30 | function computeIsolates( view ) {
31 | const set = new RangeSetBuilder();
32 |
33 | if ( view.editorAttrs.dir === 'rtl' ) {
34 | for ( const { from, to } of view.visibleRanges ) {
35 | let startPos = null;
36 | syntaxTree( view.state ).iterate( {
37 | from,
38 | to,
39 | enter( node ) {
40 | // Determine if this is a bracket node (start or end of a tag).
41 | const isBracket = node.name.split( '_' )
42 | .some( ( tag ) => [
43 | mwModeConfig.tags.htmlTagBracket,
44 | mwModeConfig.tags.extTagBracket
45 | ].includes( tag ) );
46 |
47 | if ( startPos === null && isBracket ) {
48 | // If we find a bracket node, we keep track of the start position.
49 | startPos = node.from;
50 | } else if ( isBracket ) {
51 | // When we find the closing bracket, add the isolate.
52 | set.add( startPos, node.to, isolate );
53 | startPos = null;
54 | }
55 | }
56 | } );
57 | }
58 | }
59 |
60 | return set.finish();
61 | }
62 |
63 | /**
64 | * Bidirectional isolation plugin for CodeMirror for use on RTL pages.
65 | * This ensures HTML and MediaWiki tags are always displayed left-to-right.
66 | *
67 | * Use this plugin by passing in `bidiIsolation: true` when instantiating
68 | * a [CodeMirrorMediaWiki]{@link CodeMirrorMediaWiki} object.
69 | *
70 | * @see https://codemirror.net/examples/bidi/
71 | * @internal
72 | * @private
73 | */
74 | class CodeMirrorMediaWikiBidiIsolation {
75 | /**
76 | * @constructor
77 | * @param {EditorView} view The editor view.
78 | */
79 | constructor( view ) {
80 | /** @type {DecorationSet} */
81 | this.isolates = computeIsolates( view );
82 | /** @type {Tree} */
83 | this.tree = syntaxTree( view.state );
84 | /** @type {Direction} */
85 | this.dir = view.textDirection;
86 | }
87 |
88 | /**
89 | * @param {ViewUpdate} update
90 | */
91 | update( update ) {
92 | if ( update.docChanged || update.viewportChanged ||
93 | syntaxTree( update.state ) !== this.tree ||
94 | update.view.textDirection !== this.dir
95 | ) {
96 | this.isolates = computeIsolates( update.view );
97 | this.tree = syntaxTree( update.state );
98 | }
99 | }
100 | }
101 |
102 | /**
103 | * @type {PluginSpec}
104 | * @private
105 | */
106 | const bidiIsolationSpec = {
107 | provide: ( plugin ) => {
108 | /**
109 | * @param {EditorView} view
110 | * @return {DecorationSet}
111 | */
112 | const access = ( view ) => view.plugin( plugin ) ?
113 | ( view.plugin( plugin ).isolates || Decoration.none ) :
114 | Decoration.none;
115 |
116 | // Use the lowest precedence to ensure that other decorations
117 | // don't break up the isolating decorations.
118 | return Prec.lowest( [
119 | EditorView.decorations.of( access ),
120 | EditorView.bidiIsolatedRanges.of( access )
121 | ] );
122 | }
123 | };
124 |
125 | module.exports = ViewPlugin.fromClass( CodeMirrorMediaWikiBidiIsolation, bidiIsolationSpec );
126 |
--------------------------------------------------------------------------------
/i18n/hr.json:
--------------------------------------------------------------------------------
1 | {
2 | "@metadata": {
3 | "authors": [
4 | "Bugoslav",
5 | "MaGa"
6 | ]
7 | },
8 | "codemirror-desc": "Omogućava isticanje sintakse u uređivaču wikiteksta",
9 | "codemirror-toggle-label": "Isticanje sintakse",
10 | "codemirror-toggle-label-short": "Sintaksa",
11 | "codemirror-prefs-summary": "Više o ovoj mogućnosti možete pročitati na [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Extension:CodeMirror stranici pomoći].",
12 | "codemirror-prefs-enable": "Omogući isticanje sintakse za wikitekst",
13 | "codemirror-prefs-title": "Postavke isticanja sintakse",
14 | "codemirror-prefs-help": "pomoć",
15 | "codemirror-prefs-autocomplete": "Omogući samodovršavanje",
16 | "codemirror-prefs-openlinks": "Otvaranje poveznica kombinacijom tipke za modificiranje i klika",
17 | "codemirror-prefs-bracketmatching": "Omogući podudaranje zagrada",
18 | "codemirror-prefs-linenumbering": "Prikaži brojeve redaka",
19 | "codemirror-prefs-linewrapping": "Prelamanje redova",
20 | "codemirror-prefs-activeline": "Označi aktivni red",
21 | "codemirror-prefs-specialchars": "Prikaži posebne znakove",
22 | "codemirror-prefs-colorblind": "Omogući shemu s istaknutom sintaksom prilikom uređivanja wikiteksta (prilagođeno osobama s poremećajem prepoznavanja boja)",
23 | "codemirror-prefs-colorblind-help": "Ako rabite dodatak za isticanje sintakse, ova postavka neće funkcionirati.",
24 | "codemirror-close": "Zatvori",
25 | "codemirror-find": "Traži",
26 | "codemirror-next": "Traži sljedeće",
27 | "codemirror-previous": "Traži prethodno",
28 | "codemirror-all": "Sve",
29 | "codemirror-all-tooltip": "Označi sva podudaranja",
30 | "codemirror-match-case": "Razlikuj velika/mala slova",
31 | "codemirror-regexp": "Regularni izraz",
32 | "codemirror-regexp-invalid": "Regularni izraz nije valjan",
33 | "codemirror-by-word": "Samo cijele riječi",
34 | "codemirror-replace": "Zamijeni",
35 | "codemirror-replace-placeholder": "Zamijeni s",
36 | "codemirror-replace-all": "Zamijeni sve",
37 | "codemirror-done": "Gotovo",
38 | "codemirror-find-results": "$1 od $2",
39 | "codemirror-goto-line": "Idi na liniju",
40 | "codemirror-goto-line-go": "Idi",
41 | "codemirror-control-character": "Kontrolni znak $1",
42 | "codemirror-special-char-null": "Null znak",
43 | "codemirror-special-char-bell": "Bell znak",
44 | "codemirror-special-char-backspace": "Backspace",
45 | "codemirror-special-char-newline": "Novi red",
46 | "codemirror-special-char-carriage-return": "Novi red",
47 | "codemirror-keymap-help-title": "Tipkovnički prečaci",
48 | "codemirror-keymap-bold": "Podebljano",
49 | "codemirror-keymap-italic": "Kurziv",
50 | "codemirror-keymap-link": "Poveznica",
51 | "codemirror-keymap-computercode": "Računalni kôd",
52 | "codemirror-keymap-strikethrough": "Precrtano",
53 | "codemirror-keymap-subscript": "Indeks",
54 | "codemirror-keymap-superscript": "Eksponent",
55 | "codemirror-keymap-underline": "Podcrtano",
56 | "codemirror-keymap-history": "Povijest",
57 | "codemirror-keymap-undo": "Vrati",
58 | "codemirror-keymap-redo": "Ponovi",
59 | "codemirror-keymap-undoselection": "Poništi odabir",
60 | "codemirror-keymap-redoselection": "Ponovi odabir",
61 | "codemirror-keymap-paragraph": "Oblikovanje odlomaka",
62 | "codemirror-keymap-indent": "Povećaj uvlaku",
63 | "codemirror-keymap-outdent": "Smanji uvlaku",
64 | "codemirror-keymap-preformatted": "Preoblikovano",
65 | "codemirror-keymap-find": "Traži i zamijeni",
66 | "codemirror-keymap-selectnext": "Odaberite sljedeće podudaranje",
67 | "codemirror-keymap-insert": "Umetni",
68 | "codemirror-keymap-blankline": "Prazan red",
69 | "codemirror-keymap-help": "Tipkovnički prečaci",
70 | "prefs-accessibility": "Pristupačnost",
71 | "prefs-syntax-highlighting": "Isticanje sintakse",
72 | "codemirror-beta-feature-title": "Poboljšano označavanje sintakse"
73 | }
74 |
--------------------------------------------------------------------------------
/i18n/ko.json:
--------------------------------------------------------------------------------
1 | {
2 | "@metadata": {
3 | "authors": [
4 | "Bisang",
5 | "Ellif",
6 | "Kwj2772",
7 | "Markingdots",
8 | "Ykhwong"
9 | ]
10 | },
11 | "codemirror-desc": "위키텍스트 편집기에서 구문 강조 기능을 제공합니다",
12 | "codemirror-toggle-label": "문법 강조",
13 | "codemirror-prefs-enable": "구문 강조 활성화",
14 | "codemirror-prefs-title": "구문 강조 환경 설정",
15 | "codemirror-prefs-help": "도움말",
16 | "codemirror-prefs-codefolding": "코드 폴딩 활성화",
17 | "codemirror-prefs-autocomplete": "자동 완성 활성화",
18 | "codemirror-prefs-bidiisolation": "양방향 텍스트 분리",
19 | "codemirror-prefs-bracketmatching": "대괄호 일치 활성화",
20 | "codemirror-prefs-linenumbering": "줄 번호 보이기",
21 | "codemirror-prefs-linewrapping": "자동 줄 바꿈",
22 | "codemirror-prefs-specialchars": "특수 문자 보이기",
23 | "codemirror-prefs-whitespace": "공백 강조",
24 | "codemirror-prefs-colorblind": "위키텍스트 편집 시 색각 이상자에게 친숙한 문법 강조용 스킴 활성화하기",
25 | "codemirror-prefs-colorblind-help": "문법 강조용 소도구를 사용하는 경우 이 환경 설정은 동작하지 않습니다.",
26 | "codemirror-close": "닫기",
27 | "codemirror-find": "찾기",
28 | "codemirror-next": "다음 찾기",
29 | "codemirror-previous": "이전 찾기",
30 | "codemirror-all": "모두",
31 | "codemirror-regexp": "정규 표현식",
32 | "codemirror-regexp-invalid": "잘못된 정규 표현식",
33 | "codemirror-by-word": "전체 단어",
34 | "codemirror-replace": "바꾸기",
35 | "codemirror-replace-placeholder": "바꾸기",
36 | "codemirror-replace-all": "모두 바꾸기",
37 | "codemirror-find-results": "$1 / $2",
38 | "codemirror-control-character": "제어 문자 $1",
39 | "codemirror-special-char-null": "널 문자",
40 | "codemirror-special-char-bell": "벨 문자",
41 | "codemirror-special-char-backspace": "백스페이스",
42 | "codemirror-special-char-newline": "새 줄",
43 | "codemirror-special-char-vertical-tab": "수직 탭",
44 | "codemirror-special-char-carriage-return": "캐리지 리턴",
45 | "codemirror-special-char-escape": "이스케이프 문자",
46 | "codemirror-special-char-nbsp": "줄 바꿈 없는 공백",
47 | "codemirror-special-char-zero-width-space": "너비가 0인 공백",
48 | "codemirror-special-char-zero-width-non-joiner": "너비가 0인 비결합자",
49 | "codemirror-special-char-left-to-right-mark": "왼쪽에서 오른쪽으로 표시",
50 | "codemirror-special-char-right-to-left-mark": "오른쪽에서 왼쪽으로 표시",
51 | "codemirror-special-char-line-separator": "줄 구분 기호",
52 | "codemirror-special-char-left-to-right-override": "왼쪽에서 오른쪽으로 재정의",
53 | "codemirror-special-char-right-to-left-override": "오른쪽에서 왼쪽으로 재정의",
54 | "codemirror-fold": "코드 폴딩",
55 | "codemirror-folded-code": "접힌 코드",
56 | "codemirror-keymap-help-title": "키보드 단축키",
57 | "codemirror-keymap-help-close": "닫기",
58 | "codemirror-keymap-accessibility": "접근성",
59 | "codemirror-keymap-bold": "굵게",
60 | "codemirror-keymap-italic": "기울임꼴",
61 | "codemirror-keymap-link": "링크",
62 | "codemirror-keymap-computercode": "컴퓨터 코드",
63 | "codemirror-keymap-strikethrough": "취소선",
64 | "codemirror-keymap-underline": "밑줄",
65 | "codemirror-keymap-history": "역사",
66 | "codemirror-keymap-undo": "실행 취소",
67 | "codemirror-keymap-redo": "다시 시도",
68 | "codemirror-keymap-undoselection": "선택 취소",
69 | "codemirror-keymap-indent": "들여쓰기 높이기",
70 | "codemirror-keymap-outdent": "들여쓰기 낮추기",
71 | "codemirror-keymap-heading": "문단 제목 (1-6)",
72 | "codemirror-keymap-search": "검색",
73 | "codemirror-keymap-find": "찾아 바꾸기",
74 | "codemirror-keymap-insert": "넣기",
75 | "codemirror-keymap-blankline": "빈 줄",
76 | "codemirror-keymap-comment": "의견",
77 | "codemirror-keymap-foldref": "모든 [ 태그 접기",
78 | "codemirror-keymap-autocomplete": "자동 완성",
79 | "codemirror-keymap-other": "기타",
80 | "codemirror-keymap-direction": "방향성을 변경",
81 | "codemirror-keymap-help": "키보드 단축키",
82 | "prefs-accessibility": "접근성",
83 | "prefs-syntax-highlighting": "문법 강조",
84 | "codemirror-wikilint-lonely": "외로운 \"$1\"",
85 | "codemirror-wikilint-nothing-in": "<$1>은 비어있어야 합니다",
86 | "codemirror-wikilint-remove": "제거",
87 | "codemirror-beta-feature-title": "개선된 구문 강조",
88 | "codemirror-beta-feature-description": "새로운 구문 강조, 그리고 틀 접기 및 자동 완성 등의 편집 기능을 먼저 사용해 봅니다."
89 | }
90 |
--------------------------------------------------------------------------------
/resources/workers/codemirror.worker.js:
--------------------------------------------------------------------------------
1 | const { EditorView, Text } = require( 'ext.CodeMirror.v6.lib' );
2 |
3 | const workers = new Map();
4 |
5 | /** Web worker for CodeMirror */
6 | class CodeMirrorWorker {
7 | /**
8 | * @constructor
9 | * @param {string} mode
10 | */
11 | constructor( mode ) {
12 | /**
13 | * The mode for which the worker is created.
14 | *
15 | * @type {string}
16 | */
17 | this.mode = mode;
18 | /**
19 | * Queue of callbacks to be called when the worker is loaded.
20 | *
21 | * @internal
22 | */
23 | this.queue = [];
24 | }
25 |
26 | /**
27 | * The web worker for the mode.
28 | *
29 | * @type {Worker}
30 | */
31 | get worker() {
32 | if ( !workers.has( this.mode ) ) {
33 | const worker = new Worker( `${
34 | mw.config.get( 'wgExtensionAssetsPath' )
35 | }/CodeMirror/resources/workers/${ this.mode }/worker.min.js` );
36 | workers.set( this.mode, worker );
37 | for ( const callback of this.queue ) {
38 | callback( this );
39 | }
40 |
41 | if ( mw.config.get( 'cmDebug' ) ) {
42 | window[ `${ this.mode }Worker` ] = worker;
43 | }
44 | }
45 |
46 | return workers.get( this.mode );
47 | }
48 |
49 | /**
50 | * Get the response from the worker for a given command.
51 | *
52 | * @param {string} command
53 | * @param {Text|undefined} [doc]
54 | * @return {Promise}
55 | * @private
56 | */
57 | getFeedback( command, doc ) {
58 | return new Promise( ( resolve ) => {
59 | const raw = doc && doc.toString();
60 | const listener = ( { data: [ cmd, diagnostics, resRaw ] } ) => {
61 | if ( command === cmd && raw === resRaw ) {
62 | this.worker.removeEventListener( 'message', listener );
63 | resolve( diagnostics );
64 | }
65 | };
66 | this.worker.addEventListener( 'message', listener );
67 | this.worker.postMessage( [ command, raw ] );
68 | } );
69 | }
70 |
71 | /**
72 | * Get lint diagnostics for the given document.
73 | *
74 | * @param {EditorView} view
75 | * @return {Promise}
76 | */
77 | lint( view ) {
78 | return this.getFeedback( 'lint', view.state.doc );
79 | }
80 |
81 | /**
82 | * Set the configuration for the worker.
83 | *
84 | * @param {Object} config
85 | */
86 | setConfig( config ) {
87 | this.worker.postMessage( [ 'setConfig', config ] );
88 | }
89 |
90 | /**
91 | * Get the configuration for the worker.
92 | *
93 | * @return {Promise}
94 | */
95 | getConfig() {
96 | return this.getFeedback( 'getConfig' );
97 | }
98 |
99 | /**
100 | * Set the localized messages for the worker.
101 | *
102 | * @param {Object} i18n
103 | */
104 | setI18N( i18n ) {
105 | this.worker.postMessage( [ 'setI18N', i18n ] );
106 | }
107 |
108 | /**
109 | * Get the localized messages for the worker.
110 | *
111 | * @return {Promise}
112 | */
113 | getI18N() {
114 | return this.getFeedback( 'getI18N' );
115 | }
116 |
117 | /**
118 | * Set the linting configuration for the worker.
119 | *
120 | * @param {Object} config
121 | */
122 | setLintConfig( config ) {
123 | this.worker.postMessage( [ 'setLintConfig', config ] );
124 | }
125 |
126 | /**
127 | * Add a callback to be called when the worker is loaded.
128 | *
129 | * @param {Function} callback
130 | */
131 | onload( callback ) {
132 | this.queue.push( callback );
133 | }
134 |
135 | /**
136 | * Calculate the position in the document for a given line and column.
137 | *
138 | * @param {EditorView} view
139 | * @param {number} line
140 | * @param {number} column
141 | * @return {number}
142 | */
143 | static pos( view, line, column ) {
144 | const cmLine = view.state.doc.line( line );
145 | return Math.min( cmLine.from + column - 1, cmLine.to );
146 | }
147 | }
148 |
149 | module.exports = CodeMirrorWorker;
150 |
--------------------------------------------------------------------------------
/tests/jest/codemirror.matchbrackets.test.js:
--------------------------------------------------------------------------------
1 | const CodeMirror = require( '../../resources/codemirror.js' );
2 | const { mediawiki } = require( '../../resources/modes/mediawiki/codemirror.mediawiki.js' );
3 | const { javascript } = require( '../../resources/modes/codemirror.mode.exporter.js' );
4 |
5 | const selector = '.cm-matchingBracket, .cm-nonmatchingBracket';
6 |
7 | describe( 'CodeMirrorBracketMatching for StreamLanguage', () => {
8 | let cm;
9 |
10 | beforeEach( () => {
11 | const textarea = document.createElement( 'textarea' );
12 | document.body.appendChild( textarea );
13 | cm = new CodeMirror( textarea, mediawiki() );
14 | cm.initialize();
15 | cm.textSelection.setContents( ' {{ Foo | 1 = {{ Bar }} }} Baz [' );
16 | } );
17 |
18 | it( 'should highlight nothing', () => {
19 | cm.textSelection.setSelection( { start: 0 } );
20 | expect( cm.view.contentDOM.querySelectorAll( selector ).length ).toEqual( 0 );
21 | cm.textSelection.setSelection( { start: 27 } );
22 | expect( cm.view.contentDOM.querySelectorAll( selector ).length ).toEqual( 0 );
23 | } );
24 |
25 | it( 'should highlight matched brackets', () => {
26 | cm.textSelection.setSelection( { start: 26 } );
27 | expect( cm.view.contentDOM.querySelectorAll( '.cm-matchingBracket' ).length ).toEqual( 2 );
28 | cm.textSelection.setSelection( { start: 14 } );
29 | expect( cm.view.contentDOM.querySelectorAll( '.cm-matchingBracket' ).length ).toEqual( 2 );
30 | } );
31 |
32 | it( 'should highlight unmatched brackets', () => {
33 | cm.textSelection.setSelection( { start: 31 } );
34 | expect( cm.view.contentDOM.querySelectorAll( '.cm-nonmatchingBracket' ).length ).toEqual( 1 );
35 | cm.textSelection.setSelection( { start: 32 } );
36 | expect( cm.view.contentDOM.querySelectorAll( '.cm-nonmatchingBracket' ).length ).toEqual( 1 );
37 | } );
38 |
39 | it( 'should highlight surrounding brackets', () => {
40 | cm.textSelection.setSelection( { start: 11 } );
41 | expect( cm.view.contentDOM.querySelectorAll( '.cm-matchingBracket' ).length ).toEqual( 2 );
42 | cm.textSelection.setSelection( { start: 18 } );
43 | expect( cm.view.contentDOM.querySelectorAll( '.cm-matchingBracket' ).length ).toEqual( 2 );
44 | } );
45 | } );
46 |
47 | describe( 'CodeMirrorBracketMatching for LRLanguage', () => {
48 | let cm;
49 |
50 | beforeEach( () => {
51 | const textarea = document.createElement( 'textarea' );
52 | document.body.appendChild( textarea );
53 | cm = new CodeMirror( textarea, javascript() );
54 | cm.initialize();
55 | cm.textSelection.setContents( 'if ( true ) { a = f( "{" ); }' );
56 | } );
57 |
58 | it( 'should highlight nothing', () => {
59 | cm.textSelection.setSelection( { start: 0 } );
60 | expect( cm.view.contentDOM.querySelectorAll( selector ).length ).toEqual( 0 );
61 | } );
62 |
63 | it( 'should highlight matched brackets', () => {
64 | cm.textSelection.setSelection( { start: 3 } );
65 | expect( cm.view.contentDOM.querySelectorAll( '.cm-matchingBracket' ).length ).toEqual( 2 );
66 | cm.textSelection.setSelection( { start: 27 } );
67 | expect( cm.view.contentDOM.querySelectorAll( '.cm-matchingBracket' ).length ).toEqual( 2 );
68 | } );
69 |
70 | it( 'should highlight unmatched brackets', () => {
71 | cm.textSelection.setSelection( { start: 22 } );
72 | expect( cm.view.contentDOM.querySelectorAll( '.cm-nonmatchingBracket' ).length ).toEqual( 1 );
73 | cm.textSelection.setSelection( { start: 23 } );
74 | expect( cm.view.contentDOM.querySelectorAll( '.cm-nonmatchingBracket' ).length ).toEqual( 1 );
75 | } );
76 |
77 | it( 'should highlight surrounding brackets', () => {
78 | cm.textSelection.setSelection( { start: 15 } );
79 | expect( cm.view.contentDOM.querySelectorAll( '.cm-matchingBracket' ).length ).toEqual( 2 );
80 | cm.textSelection.setSelection( { start: 21 } );
81 | expect( cm.view.contentDOM.querySelectorAll( '.cm-matchingBracket' ).length ).toEqual( 2 );
82 | } );
83 | } );
84 |
--------------------------------------------------------------------------------
/tests/jest/codemirror.lint.test.js:
--------------------------------------------------------------------------------
1 | // eslint-disable-next-line n/no-missing-require
2 | const { Text } = require( 'ext.CodeMirror.v6.lib' );
3 | const CodeMirrorLint = require( '../../resources/codemirror.lint.js' );
4 |
5 | const cmLint = new CodeMirrorLint();
6 | const { dom, update } = cmLint.panel;
7 | const doc = Text.of( [ 'foo', 'bar' ] );
8 | const apply = jest.fn();
9 |
10 | const updateSelection = ( anchor, head ) => {
11 | update( {
12 | state: {
13 | doc,
14 | selection: { main: { anchor, head } }
15 | },
16 | transactions: [],
17 | selectionSet: true
18 | } );
19 | };
20 |
21 | describe( 'CodeMirrorLint', () => {
22 | beforeEach( () => {
23 | cmLint.diagnostics = [
24 | {
25 | from: 0,
26 | to: 1,
27 | severity: 'error',
28 | message: 'Error message',
29 | actions: [
30 | {
31 | name: 'Fix',
32 | apply
33 | },
34 | {
35 | name: 'Suggestion',
36 | apply
37 | }
38 | ]
39 | },
40 | {
41 | from: 0,
42 | to: 1,
43 | severity: 'warning',
44 | message: 'Warning message'
45 | },
46 | {
47 | from: 0,
48 | to: 1,
49 | severity: 'info',
50 | message: 'Info message'
51 | }
52 | ];
53 | } );
54 |
55 | it( 'should contain 3 parts', () => {
56 | expect( dom.childElementCount ).toEqual( 3 );
57 | expect( dom.firstChild.className ).toEqual( 'cm-mw-panel--status-worker' );
58 | expect( dom.firstChild.childElementCount ).toEqual( 3 );
59 | expect( dom.firstChild.firstChild.className ).toEqual( 'cm-mw-panel--status-error' );
60 | expect( dom.firstChild.firstChild.lastChild.textContent ).toEqual( '0' );
61 | expect( dom.firstChild.children[ 1 ].className ).toEqual( 'cm-mw-panel--status-warning' );
62 | expect( dom.firstChild.children[ 1 ].lastChild.textContent ).toEqual( '0' );
63 | expect( dom.firstChild.lastChild.className ).toEqual( 'cm-mw-panel--status-info' );
64 | expect( dom.firstChild.lastChild.lastChild.textContent ).toEqual( '0' );
65 | expect( dom.children[ 1 ].className ).toEqual( 'cm-mw-panel--status-message' );
66 | expect( dom.lastChild.className ).toEqual( 'cm-mw-panel--status-line' );
67 | } );
68 |
69 | it( 'should update the diagnostics count', () => {
70 | const errorText = dom.querySelector( '.cm-mw-panel--status-error' ).lastChild;
71 | const warningText = dom.querySelector( '.cm-mw-panel--status-warning' ).lastChild;
72 | const infoText = dom.querySelector( '.cm-mw-panel--status-info' ).lastChild;
73 | cmLint.updateDiagnosticsCount( 'error', errorText );
74 | cmLint.updateDiagnosticsCount( 'warning', warningText );
75 | cmLint.updateDiagnosticsCount( 'info', infoText );
76 | expect( errorText.textContent ).toEqual( '1' );
77 | expect( warningText.textContent ).toEqual( '1' );
78 | expect( infoText.textContent ).toEqual( '1' );
79 | } );
80 |
81 | it( 'should update the diagnostic message', () => {
82 | const message = dom.querySelector( '.cm-mw-panel--status-message' );
83 | cmLint.updateDiagnosticMessage( 0, message );
84 | expect( message.textContent ).toEqual( 'Error messageFixSuggestion' );
85 | cmLint.updateDiagnosticMessage( 2, message );
86 | expect( message.textContent ).toEqual( '' );
87 | cmLint.updateDiagnosticMessage( 1, message );
88 | expect( message.textContent ).toEqual( 'Error messageFixSuggestion' );
89 | for ( const button of message.querySelectorAll( 'button' ) ) {
90 | button.click();
91 | }
92 | expect( apply ).toHaveBeenCalledTimes( 2 );
93 | } );
94 |
95 | it( 'should update the position/selection', () => {
96 | const line = dom.lastChild;
97 | updateSelection( 1, 1 );
98 | expect( line.textContent ).toEqual( '1:1' );
99 | updateSelection( 1, 6 );
100 | expect( line.textContent ).toEqual( '2:2|(1:1)' );
101 | updateSelection( 5, 2 );
102 | expect( line.textContent ).toEqual( '1:2|(1:0)' );
103 | } );
104 | } );
105 |
--------------------------------------------------------------------------------
/jsdoc.json:
--------------------------------------------------------------------------------
1 | {
2 | "opts": {
3 | "encoding": "utf8",
4 | "destination": "docs/js",
5 | "package": "package.json",
6 | "readme": "README.md",
7 | "recurse": true,
8 | "template": "node_modules/jsdoc-wmf-theme"
9 | },
10 | "plugins": [
11 | "node_modules/jsdoc-wmf-theme/plugins/default"
12 | ],
13 | "source": {
14 | "include": [ "resources" ],
15 | "exclude": [ "resources/legacy", "resources/lib" ],
16 | "includePattern": ".+\\.js$"
17 | },
18 | "tags": {},
19 | "templates": {
20 | "cleverLinks": true,
21 | "default": {
22 | "useLongnameInNav": true
23 | },
24 | "wmf": {
25 | "maintitle": "CodeMirror",
26 | "repository": "https://gerrit.wikimedia.org/g/mediawiki/extensions/CodeMirror",
27 | "linkMap": {
28 | "Command": "https://codemirror.net/docs/ref/#view.Command",
29 | "Compartment": "https://codemirror.net/docs/ref/#state.Compartment",
30 | "CompletionSource": "https://codemirror.net/docs/ref/#autocomplete.CompletionSource",
31 | "Config": "https://codemirror.net/docs/ref/#language.Config",
32 | "Decoration": "https://codemirror.net/docs/ref/#view.Decoration",
33 | "DecorationSet": "https://codemirror.net/docs/ref/#view.DecorationSet",
34 | "Direction": "https://codemirror.net/docs/ref/#view.Direction",
35 | "EditorState": "https://codemirror.net/docs/ref/#state.EditorState",
36 | "EditorView": "https://codemirror.net/docs/ref/#view.EditorView",
37 | "Extension": "https://codemirror.net/docs/ref/#state.Extension",
38 | "Hook": "https://doc.wikimedia.org/mediawiki-core/master/js/Hook.html",
39 | "KeyBinding": "https://codemirror.net/docs/ref/#view.KeyBinding",
40 | "Language": "https://codemirror.net/docs/ref/#language.Language",
41 | "LanguageSupport": "https://codemirror.net/docs/ref/#language.LanguageSupport",
42 | "LintSource": "https://codemirror.net/docs/ref/#lint.LintSource",
43 | "MatchResult": "https://codemirror.net/docs/ref/#language.MatchResult",
44 | "Panel": "https://codemirror.net/docs/ref/#view.Panel",
45 | "PluginSpec": "https://codemirror.net/docs/ref/#view.PluginSpec",
46 | "Prec": "https://codemirror.net/docs/ref/#state.Prec",
47 | "Prec.default": "https://codemirror.net/docs/ref/#state.Prec.default",
48 | "RangeSet": "https://codemirror.net/docs/ref/#state.RangeSet",
49 | "SearchQuery": "https://codemirror.net/docs/ref/#search.SearchQuery",
50 | "StateCommand": "https://codemirror.net/docs/ref/#state.StateCommand",
51 | "StateEffectType": "https://codemirror.net/docs/ref/#state.StateEffectType",
52 | "StateField": "https://codemirror.net/docs/ref/#state.StateField",
53 | "StreamParser": "https://codemirror.net/docs/ref/#language.StreamParser",
54 | "StringStream": "https://codemirror.net/docs/ref/#language.StringStream",
55 | "SyntaxNode": "https://lezer.codemirror.net/docs/ref/#common.SyntaxNode",
56 | "Tag": "https://lezer.codemirror.net/docs/ref/#highlight.Tag",
57 | "TagStyle": "https://codemirror.net/docs/ref/#language.TagStyle",
58 | "Tooltip": "https://codemirror.net/docs/ref/#view.Tooltip",
59 | "Tree": "https://lezer.codemirror.net/docs/ref/#common.Tree",
60 | "ViewUpdate": "https://codemirror.net/docs/ref/#view.ViewUpdate",
61 | "jQuery.fn.textSelection": "https://doc.wikimedia.org/mediawiki-core/master/js/jQueryPlugins.html#.textSelection",
62 | "mw.Api": "https://doc.wikimedia.org/mediawiki-core/master/js/mw.Api.html",
63 | "mw.msg": "https://doc.wikimedia.org/mediawiki-core/master/js/mw.html#.msg",
64 | "ve.ce.Surface": "https://doc.wikimedia.org/visualeditor-standalone/master/ve.ce.Surface.html",
65 | "ve.dm.Selection": "https://doc.wikimedia.org/visualeditor-standalone/master/ve.dm.Selection.html",
66 | "ve.dm.Transaction": "https://doc.wikimedia.org/visualeditor-standalone/master/ve.dm.Transaction.html",
67 | "ve.ui.Action": "https://doc.wikimedia.org/visualeditor-standalone/master/ve.ui.Action.html",
68 | "ve.ui.Surface": "https://doc.wikimedia.org/visualeditor-standalone/master/ve.ui.Surface.html",
69 | "ve.ui.Tool": "https://doc.wikimedia.org/visualeditor-standalone/master/ve.ui.Tool.html"
70 | }
71 | }
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/resources/lib/codemirror/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to contribute
2 |
3 | - [Getting help](#getting-help)
4 | - [Submitting bug reports](#submitting-bug-reports)
5 | - [Contributing code](#contributing-code)
6 |
7 | ## Getting help
8 |
9 | Community discussion, questions, and informal bug reporting is done on the
10 | [discuss.CodeMirror forum](http://discuss.codemirror.net).
11 |
12 | ## Submitting bug reports
13 |
14 | The preferred way to report bugs is to use the
15 | [GitHub issue tracker](http://github.com/codemirror/CodeMirror/issues). Before
16 | reporting a bug, read these pointers.
17 |
18 | **Note:** The issue tracker is for *bugs*, not requests for help. Questions
19 | should be asked on the
20 | [discuss.CodeMirror forum](http://discuss.codemirror.net) instead.
21 |
22 | ### Reporting bugs effectively
23 |
24 | - CodeMirror is maintained by volunteers. They don't owe you anything, so be
25 | polite. Reports with an indignant or belligerent tone tend to be moved to the
26 | bottom of the pile.
27 |
28 | - Include information about **the browser in which the problem occurred**. Even
29 | if you tested several browsers, and the problem occurred in all of them,
30 | mention this fact in the bug report. Also include browser version numbers and
31 | the operating system that you're on.
32 |
33 | - Mention which release of CodeMirror you're using. Preferably, try also with
34 | the current development snapshot, to ensure the problem has not already been
35 | fixed.
36 |
37 | - Mention very precisely what went wrong. "X is broken" is not a good bug
38 | report. What did you expect to happen? What happened instead? Describe the
39 | exact steps a maintainer has to take to make the problem occur. We can not
40 | fix something that we can not observe.
41 |
42 | - If the problem can not be reproduced in any of the demos included in the
43 | CodeMirror distribution, please provide an HTML document that demonstrates
44 | the problem. The best way to do this is to go to
45 | [jsbin.com](http://jsbin.com/ihunin/edit), enter it there, press save, and
46 | include the resulting link in your bug report.
47 |
48 | ## Contributing code
49 |
50 | Note that we are not accepting any new addons or modes into the main
51 | distribution. If you've written such a module, please distribute it as
52 | a separate NPM package.
53 |
54 | - Make sure you have a [GitHub Account](https://github.com/signup/free)
55 | - Fork [CodeMirror](https://github.com/codemirror/CodeMirror/)
56 | ([how to fork a repo](https://help.github.com/articles/fork-a-repo))
57 | - Make your changes
58 | - If your changes are easy to test or likely to regress, add tests.
59 | Tests for the core go into `test/test.js`, some modes have their own
60 | test suite under `mode/XXX/test.js`. Feel free to add new test
61 | suites to modes that don't have one yet (be sure to link the new
62 | tests into `test/index.html`).
63 | - Follow the general code style of the rest of the project (see
64 | below). Run `bin/lint` to verify that the linter is happy.
65 | - Make sure all tests pass. Visit `test/index.html` in your browser to
66 | run them.
67 | - Submit a pull request
68 | ([how to create a pull request](https://help.github.com/articles/fork-a-repo)).
69 | Don't put more than one feature/fix in a single pull request.
70 |
71 | By contributing code to CodeMirror you
72 |
73 | - agree to license the contributed code under CodeMirror's [MIT
74 | license](https://codemirror.net/5/LICENSE).
75 |
76 | - confirm that you have the right to contribute and license the code
77 | in question. (Either you hold all rights on the code, or the rights
78 | holder has explicitly granted the right to use it like this,
79 | through a compatible open source license or through a direct
80 | agreement with you.)
81 |
82 | ### Coding standards
83 |
84 | - 2 spaces per indentation level, no tabs.
85 |
86 | - Note that the linter (`bin/lint`) which is run after each commit
87 | complains about unused variables and functions. Prefix their names
88 | with an underscore to muffle it.
89 |
90 | - CodeMirror does *not* follow JSHint or JSLint prescribed style.
91 | Patches that try to 'fix' code to pass one of these linters will be
92 | unceremoniously discarded.
93 |
--------------------------------------------------------------------------------
/resources/modes/codemirror.css.js:
--------------------------------------------------------------------------------
1 | const { syntaxTree } = require( 'ext.CodeMirror.v6.lib' );
2 | const { cssLanguage, cssCompletionSource } = require( '../lib/codemirror6.bundle.modes.js' );
3 | const CodeMirrorMode = require( './codemirror.mode.js' );
4 | const CodeMirrorWorker = require( '../workers/codemirror.worker.js' );
5 |
6 | /**
7 | * CSS language support for CodeMirror.
8 | *
9 | * @example
10 | * const require = await mw.loader.using( [ 'ext.CodeMirror.v6', 'ext.CodeMirror.v6.modes' ] );
11 | * const CodeMirror = require( 'ext.CodeMirror.v6' );
12 | * const { css } = require( 'ext.CodeMirror.v6.modes' );
13 | * const cm = new CodeMirror( myTextarea, css() );
14 | * cm.initialize();
15 | * @extends CodeMirrorMode
16 | */
17 | class CodeMirrorCss extends CodeMirrorMode {
18 |
19 | /**
20 | * @param {string} name
21 | * @internal
22 | * @hideconstructor
23 | */
24 | constructor( name ) {
25 | super( name );
26 |
27 | /**
28 | * The dialect of the mode.
29 | * Either normal `css` or `sanitized-css` for
30 | * {@link https://www.mediawiki.org/wiki/Special:MyLanguage/Help:TemplateStyles TemplateStyles}.
31 | *
32 | * @type {string}
33 | */
34 | this.dialect = mw.config.get( 'wgPageContentModel' );
35 |
36 | // Custom linting rules for Extension:TemplateStyles
37 | if ( this.dialect === 'sanitized-css' ) {
38 | this.worker.onload( () => {
39 | this.worker.setConfig( {
40 | 'property-no-vendor-prefix': [
41 | true,
42 | { ignoreProperties: [ 'user-select' ] }
43 | ],
44 | 'property-disallowed-list': [ '/^--/' ]
45 | } );
46 | } );
47 | }
48 | }
49 |
50 | /** @inheritDoc */
51 | get language() {
52 | return cssLanguage;
53 | }
54 |
55 | /** @inheritDoc */
56 | get lintSource() {
57 | return async ( view ) => {
58 | const data = await this.worker.lint( view );
59 | return data.map( ( {
60 | text,
61 | severity,
62 | line,
63 | column,
64 | endLine,
65 | endColumn,
66 | rule,
67 | fix
68 | } ) => {
69 | const diagnostic = {
70 | rule,
71 | source: 'Stylelint',
72 | message: text,
73 | severity: severity === 'error' ? 'error' : 'info',
74 | from: CodeMirrorWorker.pos( view, line, column ),
75 | to: endLine === undefined ?
76 | view.state.doc.line( line ).to :
77 | CodeMirrorWorker.pos( view, endLine, endColumn )
78 | };
79 | if ( fix ) {
80 | const { range: [ from, to ], text: insert } = fix;
81 | diagnostic.actions = [
82 | {
83 | name: 'fix',
84 | apply( v ) {
85 | v.dispatch( { changes: { from, to, insert } } );
86 | }
87 | }
88 | ];
89 | }
90 | return diagnostic;
91 | } );
92 | };
93 | }
94 |
95 | /** @inheritDoc */
96 | get support() {
97 | return cssLanguage.data.of( {
98 | autocomplete: ( context ) => {
99 | const { state, pos: p } = context,
100 | node = syntaxTree( state ).resolveInner( p, -1 ),
101 | result = cssCompletionSource( context );
102 | if ( result ) {
103 | if ( node.name === 'ValueName' ) {
104 | const options = [ { label: 'revert', type: 'keyword' }, ...result.options ];
105 | let { prevSibling } = node;
106 | while ( prevSibling && prevSibling.name !== 'PropertyName' ) {
107 | ( { prevSibling } = prevSibling );
108 | }
109 | if ( prevSibling ) {
110 | for ( let i = 0; i < options.length; i++ ) {
111 | const option = options[ i ];
112 | if ( CSS.supports(
113 | state.sliceDoc( prevSibling.from, node.from ) + option.label
114 | ) ) {
115 | options.splice( i, 1, Object.assign( {}, option, {
116 | boost: 50
117 | } ) );
118 | }
119 | }
120 | }
121 | result.options = options;
122 | } else if ( this.dialect === 'sanitized-css' ) {
123 | result.options = result.options.filter(
124 | ( { type, label } ) => type !== 'property' ||
125 | !label.startsWith( '-' ) ||
126 | label.endsWith( '-user-select' )
127 | );
128 | }
129 | }
130 | return result;
131 | }
132 | } );
133 | }
134 | }
135 |
136 | module.exports = CodeMirrorCss;
137 |
--------------------------------------------------------------------------------
/resources/modes/mediawiki/codemirror.mediawiki.openLinks.js:
--------------------------------------------------------------------------------
1 | const {
2 | EditorView,
3 | Extension,
4 | ensureSyntaxTree
5 | } = require( 'ext.CodeMirror.v6.lib' );
6 | const mwModeConfig = require( './codemirror.mediawiki.config.js' );
7 | const { platform } = $.client.profile();
8 |
9 | const isMac = platform === 'mac' || platform === 'ipad' || platform === 'iphone',
10 | modKey = isMac ? 'Meta' : 'Control';
11 |
12 | /**
13 | * Toggle .cm-mw-open-links from all CodeMirror instances.
14 | *
15 | * @param {boolean} toggle
16 | * @private
17 | */
18 | function toggleOpenLinks( toggle ) {
19 | for ( const dom of document.querySelectorAll( '.cm-content' ) ) {
20 | // Use .add() and .remove() instead of .toggle() for safe measure.
21 | dom.classList[ toggle ? 'add' : 'remove' ]( 'cm-mw-open-links' );
22 | }
23 | }
24 |
25 | document.addEventListener( 'keydown', ( e ) => {
26 | if ( e.key === modKey ) {
27 | toggleOpenLinks( true );
28 | }
29 | } );
30 | document.addEventListener( 'keyup', ( e ) => {
31 | if ( e.key === modKey ) {
32 | toggleOpenLinks( false );
33 | }
34 | } );
35 | // Ensure openLinks classes are removed when switching tabs.
36 | document.addEventListener( 'visibilitychange', () => {
37 | if ( document.hidden ) {
38 | toggleOpenLinks( false );
39 | }
40 | } );
41 |
42 | /**
43 | * CodeMirror extension that opens links by modifier-clicking for the MediaWiki mode.
44 | * This automatically applied when using {@link CodeMirrorMediaWiki}.
45 | *
46 | * @type {Extension}
47 | * @internal
48 | * @private
49 | */
50 | const openLinksExtension = [
51 | EditorView.domEventHandlers( {
52 | /**
53 | * Handle the mousedown event to open links.
54 | *
55 | * @param {MouseEvent} e
56 | * @param {EditorView} view
57 | * @return {boolean}
58 | * @private
59 | */
60 | mousedown( e, view ) {
61 | if ( !( isMac ? e.metaKey : e.ctrlKey ) || e.button !== 0 ) {
62 | return false;
63 | }
64 | const position = view.posAtCoords( e );
65 | if ( !position ) {
66 | return false;
67 | }
68 | const { state } = view,
69 | tree = ensureSyntaxTree( state, position ),
70 | node = tree && tree.resolve( position, 1 );
71 | if ( !node ) {
72 | return false;
73 | }
74 | const { name, from, to } = node,
75 | names = name.split( '_' );
76 | if ( names.includes( mwModeConfig.tags.linkPageName ) ||
77 | names.includes( mwModeConfig.tags.templateName ) ) {
78 | let page = state.sliceDoc( from, to ).trim();
79 | if ( page.startsWith( '/' ) ) {
80 | page = `:${ mw.config.get( 'wgPageName' ) }${ page }`;
81 | }
82 | const ns = names.includes( mwModeConfig.tags.templateName ) ? 10 : 0,
83 | title = mw.Title.newFromText( page, ns );
84 | if ( title ) {
85 | open( title.getUrl(), '_blank', 'noopener noreferrer' );
86 | return true;
87 | }
88 | } else if ( names.includes( mwModeConfig.tags.extLinkProtocol ) ||
89 | names.includes( mwModeConfig.tags.freeExtLinkProtocol ) ) {
90 | open( state.sliceDoc( from, node.nextSibling.to ), '_blank', 'noopener noreferrer' );
91 | return true;
92 | } else if ( names.includes( mwModeConfig.tags.extLink ) ||
93 | names.includes( mwModeConfig.tags.freeExtLink ) ) {
94 | open( state.sliceDoc( node.prevSibling.from, to ), '_blank', 'noopener noreferrer' );
95 | return true;
96 | } else if ( names.includes( mwModeConfig.tags.pageName ) &&
97 | names.includes( 'mw-ext-templatestyles' ) ) {
98 | const title = mw.Title.newFromText( state.sliceDoc( from, to ).trim(), 10 );
99 | if ( title ) {
100 | open( title.getUrl(), '_blank', 'noopener noreferrer' );
101 | return true;
102 | }
103 | } else if ( name.includes( mwModeConfig.tags.pageName ) &&
104 | names.includes( mwModeConfig.tags.parserFunction ) ) {
105 | const ns = Number( /mw-function-(\d+)/.exec( name )[ 1 ] ),
106 | title = mw.Title.newFromText( state.sliceDoc( from, to ).trim(), ns );
107 | if ( title ) {
108 | open( title.getUrl(), '_blank', 'noopener noreferrer' );
109 | return true;
110 | }
111 | }
112 | return false;
113 | }
114 | } ),
115 | EditorView.contentAttributes.of( {
116 | 'data-open-links': ''
117 | } )
118 | ];
119 |
120 | module.exports = openLinksExtension;
121 |
--------------------------------------------------------------------------------
/i18n/mk.json:
--------------------------------------------------------------------------------
1 | {
2 | "@metadata": {
3 | "authors": [
4 | "Bjankuloski06",
5 | "McDutchie"
6 | ]
7 | },
8 | "codemirror-desc": "Дава потцртување на синтаксата во уредувачот на викитекст",
9 | "codemirror-toggle-label": "Истакнување на синтакса",
10 | "codemirror-toggle-label-short": "Синтакса",
11 | "codemirror-prefs-summary": "Повеќе за оваа функција можете да дознаете на [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Extension:CodeMirror помошната страница].",
12 | "codemirror-prefs-enable": "Овозможи истакнување на синтакса во викитекст",
13 | "codemirror-prefs-title": "Нагодувања за нагласување на синтакса",
14 | "codemirror-prefs-help": "помош",
15 | "codemirror-prefs-codefolding": "Овозможи склопување на кодот",
16 | "codemirror-prefs-autocomplete": "Овозможи самодополнување",
17 | "codemirror-prefs-openlinks": "Овозможи отворање врски со изменител + стисок",
18 | "codemirror-prefs-bidiisolation": "Издвој двонасочен текст",
19 | "codemirror-prefs-bracketmatching": "Овозможи совпаѓање на загради",
20 | "codemirror-prefs-linenumbering": "Прикажувај броеви на редовите",
21 | "codemirror-prefs-linewrapping": "Спаструвај редови",
22 | "codemirror-prefs-activeline": "Истакни го активниот ред",
23 | "codemirror-prefs-specialchars": "Прикажи посебни знаци",
24 | "codemirror-prefs-colorblind": "Овозможи истакнување на синтакса при уредување за далтонисти",
25 | "codemirror-prefs-colorblind-help": "Ако користите алатка за истакнување на синтакса, оваа можност нема да работи.",
26 | "codemirror-close": "Затвори",
27 | "codemirror-find": "Најди",
28 | "codemirror-next": "Најди следно",
29 | "codemirror-previous": "Најди претходно",
30 | "codemirror-all": "Сите",
31 | "codemirror-all-tooltip": "Изберете ги сите совпаѓања",
32 | "codemirror-match-case": "Разликувај големи/мали букви",
33 | "codemirror-regexp": "Рег. израз",
34 | "codemirror-regexp-invalid": "Неважечки регуларен израз",
35 | "codemirror-by-word": "По збор",
36 | "codemirror-replace": "Замени",
37 | "codemirror-replace-placeholder": "Замени",
38 | "codemirror-replace-all": "Замени сè",
39 | "codemirror-done": "Готово",
40 | "codemirror-find-results": "$1 од $2",
41 | "codemirror-goto-line": "Оди на редот",
42 | "codemirror-goto-line-go": "Оди",
43 | "codemirror-control-character": "Контролен знак $1",
44 | "codemirror-special-char-null": "Ништовен знак",
45 | "codemirror-special-char-bell": "Ѕвонче",
46 | "codemirror-special-char-backspace": "Враќач",
47 | "codemirror-special-char-newline": "Нов ред",
48 | "codemirror-special-char-vertical-tab": "Верикален табулатор",
49 | "codemirror-special-char-carriage-return": "Поврат на механизмот",
50 | "codemirror-special-char-escape": "Изменителен знак",
51 | "codemirror-special-char-nbsp": "Беспреломна белина",
52 | "codemirror-special-char-zero-width-space": "Бесширинска белина",
53 | "codemirror-special-char-zero-width-non-joiner": "Бесширински несврзувач",
54 | "codemirror-special-char-zero-width-joiner": "Бесширински сврзувач",
55 | "codemirror-special-char-left-to-right-mark": "Знак од лево на десно",
56 | "codemirror-special-char-right-to-left-mark": "Знак од десно на лево",
57 | "codemirror-special-char-line-separator": "Реден одделувач",
58 | "codemirror-special-char-left-to-right-override": "Презапис од лево на десно",
59 | "codemirror-special-char-right-to-left-override": "Презапис од десно на лево",
60 | "codemirror-special-char-narrow-nbsp": "Тесен беспреломен простор",
61 | "codemirror-special-char-left-to-right-isolate": "Самостојник од лево на десно",
62 | "codemirror-special-char-right-to-left-isolate": "Самостојник од десно на лево",
63 | "codemirror-special-char-pop-directional-isolate": "Самостојник за измена на насока",
64 | "codemirror-special-char-paragraph-separator": "Одделувач на пасуси",
65 | "codemirror-special-char-zero-width-no-break-space": "Слевач на зборови",
66 | "codemirror-special-char-object-replacement": "Знак за замена на објекти",
67 | "codemirror-fold": "Склопување на код",
68 | "codemirror-unfold": "расклопи",
69 | "codemirror-folded-code": "слопен код",
70 | "prefs-accessibility": "Пристапност",
71 | "prefs-syntax-highlighting": "Истакнување на синтакса",
72 | "codemirror-beta-feature-title": "Подобрено нагласување на синтакса",
73 | "codemirror-beta-feature-description": "Добијте ран пристап до новото нагласување на синтакса и можностите за уредување како склопување на предлошки и самодополнување."
74 | }
75 |
--------------------------------------------------------------------------------
/i18n/it.json:
--------------------------------------------------------------------------------
1 | {
2 | "@metadata": {
3 | "authors": [
4 | "Beta16",
5 | "Sakretsu",
6 | "Statix64"
7 | ]
8 | },
9 | "codemirror-desc": "Fornisce l'evidenziazione della sintassi nell'editor wikitesto",
10 | "codemirror-toggle-label": "Evidenziazione della sintassi",
11 | "codemirror-toggle-label-short": "Sintassi",
12 | "codemirror-prefs-enable": "Attiva l'evidenziazione della sintassi",
13 | "codemirror-prefs-title": "Preferenze di evidenziazione della sintassi",
14 | "codemirror-prefs-help": "aiuto",
15 | "codemirror-prefs-section-references": "Riferimenti",
16 | "codemirror-prefs-bidiisolation": "Isola il testo bidirezionale",
17 | "codemirror-prefs-linenumbering": "Mostra i numeri di riga",
18 | "codemirror-prefs-activeline": "Evidenzia la riga attiva",
19 | "codemirror-prefs-specialchars": "Mostra caratteri speciali",
20 | "codemirror-prefs-colorblind": "Attiva una combinazione di colori per daltonici per l'evidenziazione della sintassi del wikitesto",
21 | "codemirror-prefs-colorblind-help": "Questa preferenza non funziona se usi un accessorio per l'evidenziazione della sintassi.",
22 | "codemirror-close": "Chiudi",
23 | "codemirror-find": "Trova",
24 | "codemirror-next": "Trova successivo",
25 | "codemirror-previous": "Trova precedente",
26 | "codemirror-all": "Tutto",
27 | "codemirror-all-tooltip": "Seleziona tutte le corrispondenze",
28 | "codemirror-match-case": "Maiuscole/minuscole",
29 | "codemirror-regexp": "Espressione regolare",
30 | "codemirror-regexp-invalid": "Espressione regolare non valida",
31 | "codemirror-by-word": "Parola intera",
32 | "codemirror-replace": "Sostituisci",
33 | "codemirror-replace-all": "Sostituisci tutto",
34 | "codemirror-done": "Fatto",
35 | "codemirror-goto-line": "Vai alla riga",
36 | "codemirror-goto-line-go": "Vai",
37 | "codemirror-keymap-help-title": "Scorciatoie da tastiera",
38 | "codemirror-keymap-help-close": "Chiudi",
39 | "codemirror-keymap-accessibility": "Accessibilità",
40 | "codemirror-keymap-textstyling": "Stile testo",
41 | "codemirror-keymap-bold": "Grassetto",
42 | "codemirror-keymap-italic": "Corsivo",
43 | "codemirror-keymap-link": "Collegamento",
44 | "codemirror-keymap-computercode": "Codice di computer",
45 | "codemirror-keymap-strikethrough": "Barrato",
46 | "codemirror-keymap-subscript": "Pedice",
47 | "codemirror-keymap-superscript": "Apice",
48 | "codemirror-keymap-underline": "Sottolineato",
49 | "codemirror-keymap-history": "Cronologia",
50 | "codemirror-keymap-undo": "Annulla",
51 | "codemirror-keymap-redo": "Ripeti",
52 | "codemirror-keymap-undoselection": "Annulla selezione",
53 | "codemirror-keymap-paragraph": "Formattazione dei paragrafi",
54 | "codemirror-keymap-indent": "Aumenta indentazione",
55 | "codemirror-keymap-outdent": "Riduci indentazione",
56 | "codemirror-keymap-blockquote": "Blocco citazione",
57 | "codemirror-keymap-heading": "Intestazione (1-6)",
58 | "codemirror-keymap-find": "Trova e sostituisci",
59 | "codemirror-keymap-insert": "Inserisci",
60 | "codemirror-keymap-blankline": "Riga vuota",
61 | "codemirror-keymap-reference": "Riferimento",
62 | "codemirror-keymap-comment": "Commento",
63 | "codemirror-keymap-preferences": "Preferenze di evidenziazione della sintassi",
64 | "codemirror-keymap-help": "Scorciatoie da tastiera",
65 | "codemirror-keymap-multicursor": "Tieni premuto $1 e fai clic per creare più cursori.",
66 | "codemirror-keymap-openlinks": "Tieni premuto $1 e clicca sui titoli delle pagine e sugli URL per aprirli in una nuova scheda.",
67 | "prefs-accessibility": "Accessibilità",
68 | "prefs-syntax-highlighting": "Evidenziazione della sintassi",
69 | "codemirror-wikilint-duplicate-parameter": "parametro template duplicato",
70 | "codemirror-wikilint-illegal-attribute-name": "nome attributo non valido",
71 | "codemirror-wikilint-illegal-attribute-value": "valore attributo non valido",
72 | "codemirror-wikilint-imagemap-without-image": "mappa immagine senza immagine",
73 | "codemirror-wikilint-invalid-content": "contenuto non valido in $1",
74 | "codemirror-wikilint-invalid-imagemap-link": "collegamento non valido nella mappa immagine",
75 | "codemirror-wikilint-invalid-parameter": "parametro non valido di $1",
76 | "codemirror-wikilint-newline": "inserisci una nuova riga",
77 | "codemirror-wikilint-nothing-in": "non dovrebbe esserci nulla in $1",
78 | "codemirror-wikilint-remove": "rimuovi",
79 | "codemirror-wikilint-uppercase": "converti in maiuscolo"
80 | }
81 |
--------------------------------------------------------------------------------
]