├── .nvmrc ├── .eslintrc.json ├── resources ├── legacy │ ├── modules │ │ └── ve-cm │ │ │ ├── .eslintrc.json │ │ │ ├── ve.ui.CodeMirrorTool.js │ │ │ └── ve.ui.CodeMirror.less │ ├── mode │ │ └── mediawiki │ │ │ └── tests │ │ │ └── qunit │ │ │ └── .eslintrc.json │ ├── codemirror-fixes.less │ ├── .eslintrc.json │ ├── ext.CodeMirror.js │ └── ext.CodeMirror.less ├── lib │ ├── README │ ├── foreign-resources.yaml │ ├── foreign-resources.cdx.json │ └── codemirror │ │ ├── LICENSE │ │ ├── README.md │ │ └── CONTRIBUTING.md ├── images │ ├── codemirror.wikieditor.check-all.svg │ ├── codemirror.wikieditor.icon.svg │ ├── codemirror.wikieditor.wrapping.svg │ ├── codemirror.wikieditor.pilcrow.svg │ ├── codemirror.wikieditor.gotoLine.svg │ └── codemirror.wikieditor.settings.svg ├── workers │ ├── lua │ │ ├── worker.js │ │ └── .eslintrc.json │ ├── common.js │ ├── mediawiki │ │ ├── .eslintrc.json │ │ └── worker.js │ ├── css │ │ ├── worker.js │ │ └── .eslintrc.json │ ├── javascript │ │ ├── .eslintrc.json │ │ └── worker.js │ └── codemirror.worker.js ├── modes │ ├── codemirror.bundle.modes.js │ ├── codemirror.json.js │ ├── codemirror.vue.js │ ├── codemirror.mode.exporter.js │ ├── codemirror.javascript.js │ ├── codemirror.mode.js │ ├── mediawiki │ │ ├── codemirror.mediawiki.colorblind.less │ │ ├── codemirror.mediawiki.bidiIsolation.js │ │ └── codemirror.mediawiki.openLinks.js │ └── codemirror.css.js ├── codemirror.mixins.less ├── codemirror.styleModule.less ├── codemirror.panel.js ├── .eslintrc.json ├── codemirror.bundle.lib.js ├── ve-cm │ ├── ve.ui.CodeMirrorAction.v6.js │ ├── ve.ui.CodeMirrorTool.v6.js │ ├── ve.ui.CodeMirror.init.js │ └── ve.ui.CodeMirror.v6.less ├── codemirror.keymap.less ├── codemirror.wikieditor.less ├── codemirror.child.js └── codemirror.matchbrackets.js ├── .eslintignore ├── i18n ├── crh-ro.json ├── lt.json ├── mns.json ├── smn.json ├── se.json ├── sms.json ├── km.json ├── nn.json ├── rue.json ├── tly.json ├── azb.json ├── lfn.json ├── sjd.json ├── vec.json ├── cy.json ├── uz.json ├── lij.json ├── ccp.json ├── ksh.json ├── tt-cyrl.json ├── ast.json ├── li.json ├── ta.json ├── en-gb.json ├── da.json ├── eo.json ├── id.json ├── el.json ├── roa-tara.json ├── be.json ├── diq.json ├── scn.json ├── yo.json ├── is.json ├── th.json ├── ml.json ├── ca.json ├── crh-latn.json ├── io.json ├── oc.json ├── isv-latn.json ├── bjn.json ├── bs.json ├── hy.json ├── ky.json ├── ckb.json ├── lv.json ├── sd.json ├── fy.json ├── hu.json ├── sv.json ├── ro.json ├── ba.json ├── ne.json ├── te.json ├── sk.json ├── pa.json ├── sq.json ├── yue-hant.json ├── hi.json ├── sh-latn.json ├── tr.json ├── cs.json ├── ha.json ├── sl.json ├── br.json ├── skr-arab.json ├── kck.json ├── bn.json ├── be-tarask.json ├── ur.json ├── ja.json ├── sr-el.json ├── sr-ec.json ├── bg.json ├── pt-br.json ├── ka.json ├── hr.json ├── ko.json ├── mk.json └── it.json ├── .gitreview ├── CODE_OF_CONDUCT.md ├── .stylelintrc.json ├── .gitignore ├── tests ├── selenium │ ├── .eslintrc.json │ ├── wdio.conf.js │ ├── fixturecontent.js │ ├── README.md │ ├── userpreferences.js │ ├── specs │ │ └── highlighting-wikitext2010.js │ └── pageobjects │ │ └── edit.page.js ├── jest │ ├── .eslintrc.json │ ├── codemirror.init.test.js │ ├── modes │ │ ├── codemirror.lua.codeFolding.test.js │ │ ├── codemirror.css.autocomplete.test.js │ │ ├── codemirror.mediawiki.openLinks.test.js │ │ └── codemirror.javascript.autocomplete.test.js │ ├── codemirror.bidiIsolation.test.js │ ├── codemirror.gotoLine.test.js │ ├── codemirror.child.test.js │ ├── codemirror.matchbrackets.test.js │ └── codemirror.lint.test.js └── phpunit │ ├── unit │ └── HookRunnerTest.php │ └── DataScriptTest.php ├── .phpcs.xml ├── .phan └── config.php ├── composer.json ├── includes └── Hooks │ ├── CodeMirrorSpecialPageHook.php │ ├── CodeMirrorGetModeHook.php │ └── HookRunner.php ├── jest.config.js ├── rollup.config.js ├── README.md ├── package.json └── jsdoc.json /.nvmrc: -------------------------------------------------------------------------------- 1 | 20.19.5 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "wikimedia/server" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /resources/legacy/modules/ve-cm/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "ve": "readonly" 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /resources/lib/* 2 | !/resources/lib/foreign-resources.* 3 | /vendor 4 | /docs 5 | /coverage 6 | -------------------------------------------------------------------------------- /i18n/crh-ro.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Zolgoyo" 5 | ] 6 | }, 7 | "codemirror-find": "Tap" 8 | } 9 | -------------------------------------------------------------------------------- /i18n/lt.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Nokeoo" 5 | ] 6 | }, 7 | "prefs-accessibility": "Prieinamumas" 8 | } 9 | -------------------------------------------------------------------------------- /i18n/mns.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Ewithu" 5 | ] 6 | }, 7 | "codemirror-prefs-help": "нё̄тмил" 8 | } 9 | -------------------------------------------------------------------------------- /i18n/smn.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Yupik" 5 | ] 6 | }, 7 | "prefs-accessibility": "Juksâmvuotâ" 8 | } 9 | -------------------------------------------------------------------------------- /i18n/se.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Yupik" 5 | ] 6 | }, 7 | "prefs-accessibility": "Olahahttivuohta" 8 | } 9 | -------------------------------------------------------------------------------- /i18n/sms.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Yupik" 5 | ] 6 | }, 7 | "prefs-accessibility": "Vuällamvuõtt­" 8 | } 9 | -------------------------------------------------------------------------------- /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=gerrit.wikimedia.org 3 | port=29418 4 | project=mediawiki/extensions/CodeMirror.git 5 | track=1 6 | defaultrebase=0 7 | -------------------------------------------------------------------------------- /i18n/km.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "គីមស៊្រុន" 5 | ] 6 | }, 7 | "codemirror-toggle-label": "ផាត់ពណ៌អក្សរកូដ" 8 | } 9 | -------------------------------------------------------------------------------- /i18n/nn.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Njardarlogar" 5 | ] 6 | }, 7 | "codemirror-toggle-label": "Syntaksframheving" 8 | } 9 | -------------------------------------------------------------------------------- /i18n/rue.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Constraque" 5 | ] 6 | }, 7 | "codemirror-toggle-label-short": "Сінтаксіс" 8 | } 9 | -------------------------------------------------------------------------------- /i18n/tly.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Гусейн" 5 | ] 6 | }, 7 | "codemirror-prefs-section-references": "Səvonon" 8 | } 9 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | The development of this software is covered by a [Code of Conduct](https://www.mediawiki.org/wiki/Special:MyLanguage/Code_of_Conduct). 2 | -------------------------------------------------------------------------------- /i18n/azb.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Alp Er Tunqa" 5 | ] 6 | }, 7 | "codemirror-toggle-label": "ترکیبلری هایلایتلا" 8 | } 9 | -------------------------------------------------------------------------------- /i18n/lfn.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Mafcadio" 5 | ] 6 | }, 7 | "codemirror-toggle-label": "Marcante de sintatica" 8 | } 9 | -------------------------------------------------------------------------------- /i18n/sjd.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Merrahtar" 5 | ] 6 | }, 7 | "codemirror-toggle-label": "Синтаксесс тӯзхуввмушш" 8 | } 9 | -------------------------------------------------------------------------------- /i18n/vec.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Fierodelveneto" 5 | ] 6 | }, 7 | "codemirror-toggle-label": "Rexalta ła sintasi" 8 | } 9 | -------------------------------------------------------------------------------- /i18n/cy.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Afalau", 5 | "Ceirios" 6 | ] 7 | }, 8 | "prefs-accessibility": "Hygyrchedd" 9 | } 10 | -------------------------------------------------------------------------------- /i18n/uz.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Sociologist" 5 | ] 6 | }, 7 | "codemirror-toggle-label": "Sintaksis rangli tarzda ajratib koʻrsatilsin" 8 | } 9 | -------------------------------------------------------------------------------- /resources/legacy/mode/mediawiki/tests/qunit/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "wikimedia/qunit" 4 | ], 5 | "rules": { 6 | "no-jquery/no-global-selector": "off" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /resources/lib/README: -------------------------------------------------------------------------------- 1 | The codemirror/ directory is legacy CM5 and managed by foreign-resources.yaml 2 | 3 | The codemirror6.* files are CM6 managed by Rollup (see rollup.config.js) 4 | -------------------------------------------------------------------------------- /i18n/lij.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Giromin Cangiaxo" 5 | ] 6 | }, 7 | "codemirror-desc": "O permette d'evidençiâ a scintasci inte l'editor de wiki-testo" 8 | } 9 | -------------------------------------------------------------------------------- /.stylelintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-wikimedia", 3 | "rules": { 4 | "rule-empty-line-before": null 5 | }, 6 | "ignoreFiles": [ 7 | "coverage/**/*.css" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /i18n/ccp.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Joychangma" 5 | ] 6 | }, 7 | "codemirror-regexp-invalid": "𑄟𑄳𑄃𑄧𑄉𑄧𑄢𑄟𑄨 𑄇𑄧𑄙 𑄚𑄨𑄠𑄳𑄅𑄧𑄟𑄴 𑄃𑄧𑄚𑄪𑄎𑄠𑄨 𑄝𑄬𑄦𑄠𑄬𑄚𑄨" 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | *.kate-swp 3 | .*.swp 4 | .directory 5 | /node_modules/ 6 | /coverage/ 7 | /tests/selenium/log/ 8 | /vendor/ 9 | /composer.lock 10 | .eslintcache 11 | .env 12 | docs/ 13 | /.stylelintcache 14 | -------------------------------------------------------------------------------- /tests/selenium/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "wikimedia/selenium" 5 | ], 6 | "globals": { 7 | "mw": "readonly" 8 | }, 9 | "rules": { 10 | "wdio/no-pause": "warn" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /i18n/ksh.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Purodha" 5 | ] 6 | }, 7 | "codemirror-desc": "Määd et müjjelesch, em Wikkitäx beim Beärbeide, dem Wikki sing eije Befähle extra erußjehovve aanzezeije." 8 | } 9 | -------------------------------------------------------------------------------- /i18n/tt-cyrl.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Ильнар" 5 | ] 6 | }, 7 | "codemirror-desc": "Вики-текст мөхәррирендә синтаксны аеруп күрсәтә", 8 | "codemirror-toggle-label": "Синтакс-аеру" 9 | } 10 | -------------------------------------------------------------------------------- /i18n/ast.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Xuacu" 5 | ] 6 | }, 7 | "codemirror-desc": "Ufre sintaxis resaltada nel editor de testu wiki", 8 | "codemirror-toggle-label": "Resaltador de sintaxis" 9 | } 10 | -------------------------------------------------------------------------------- /i18n/li.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Ooswesthoesbes" 5 | ] 6 | }, 7 | "codemirror-desc": "Bèd accentuering van teks inne wikiteksbewirker", 8 | "codemirror-toggle-label": "Syntaxisoetleechting" 9 | } 10 | -------------------------------------------------------------------------------- /i18n/ta.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "ElangoRamanujam" 5 | ] 6 | }, 7 | "codemirror-prefs-section-lines": "வரிகள்", 8 | "codemirror-keymap-advanced-preferences": "மேம்பட்ட விருப்பத்தேர்வுகள்" 9 | } 10 | -------------------------------------------------------------------------------- /i18n/en-gb.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Piano1forte2", 5 | "Reedy" 6 | ] 7 | }, 8 | "codemirror-prefs-colorblind": "Enable colour blind-friendly scheme for syntax highlighting when editing wikitext" 9 | } 10 | -------------------------------------------------------------------------------- /.phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | . 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /i18n/da.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Saederup92", 5 | "Weblars" 6 | ] 7 | }, 8 | "codemirror-desc": "Giver syntaksfremhævelse i wikitekst editoren", 9 | "codemirror-toggle-label": "Syntaksfremhævelse" 10 | } 11 | -------------------------------------------------------------------------------- /i18n/eo.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "KuboF", 5 | "Mirin" 6 | ] 7 | }, 8 | "codemirror-desc": "Provizas markadon de sintakso en vikiteksta redaktilo", 9 | "codemirror-toggle-label": "Sintaksa reliefigado" 10 | } 11 | -------------------------------------------------------------------------------- /i18n/id.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Daud I.F. Argana" 5 | ] 6 | }, 7 | "codemirror-desc": "Menyediakan penyorotan sintaksis di penyunting teks wiki", 8 | "codemirror-toggle-label": "Penyorotan sintaksis" 9 | } 10 | -------------------------------------------------------------------------------- /i18n/el.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Geraki", 5 | "Αντιγόνη" 6 | ] 7 | }, 8 | "codemirror-desc": "Παρέχει επισήμανση σύνταξης στον επεξεργαστή κώδικα wiki", 9 | "codemirror-toggle-label": "Επισήμανση σύνταξης" 10 | } 11 | -------------------------------------------------------------------------------- /i18n/roa-tara.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Joetaras" 5 | ] 6 | }, 7 | "codemirror-desc": "Dèje l'evidenziazzione d'a sindasse jndr'à 'u cangiatore de uicchiteste", 8 | "codemirror-toggle-label": "Evidenziatore de sindasse" 9 | } 10 | -------------------------------------------------------------------------------- /i18n/be.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Artsiom91", 5 | "Maksim L." 6 | ] 7 | }, 8 | "codemirror-desc": "Забяспечвае вылучэнне колерам сінтаксісу ў рэдактары вікіразметкі", 9 | "codemirror-toggle-label": "Выдзяленне сінтаксісу" 10 | } 11 | -------------------------------------------------------------------------------- /i18n/diq.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Mirzali" 5 | ] 6 | }, 7 | "codemirror-prefs-section-other": "Sewbi", 8 | "codemirror-keymap-help-close": "Kip ke", 9 | "codemirror-keymap-link": "Gıre", 10 | "codemirror-keymap-history": "Tarix" 11 | } 12 | -------------------------------------------------------------------------------- /i18n/scn.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "GianAntonucci" 5 | ] 6 | }, 7 | "codemirror-prefs-panel-advanced": "avanzati", 8 | "codemirror-prefs-section-lines": "Lini", 9 | "codemirror-prefs-section-characters": "Caràttari", 10 | "codemirror-prefs-section-other": "Autru" 11 | } 12 | -------------------------------------------------------------------------------- /i18n/yo.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "BigDareLibrary" 5 | ] 6 | }, 7 | "codemirror-special-char-backspace": "Aye afẹyinti", 8 | "codemirror-special-char-right-to-left-isolate": "Iyasọtọ-si-osi.", 9 | "codemirror-special-char-object-replacement": "Ohun kikọ rirọpo." 10 | } 11 | -------------------------------------------------------------------------------- /tests/jest/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "wikimedia/jquery", 4 | "wikimedia/mediawiki" 5 | ], 6 | "parserOptions": { 7 | "sourceType": "module" 8 | }, 9 | "globals": { 10 | "mockMwConfigGet": true 11 | }, 12 | "env": { 13 | "browser": true, 14 | "jest": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /i18n/is.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Stefán Örvar Sigmundsson", 5 | "Sveinn í Felli" 6 | ] 7 | }, 8 | "codemirror-toggle-label": "Málskipanarljómun", 9 | "codemirror-prefs-colorblind": "Virkja litblinduvænt litaskema við málskipanarljómun við breytingu á wiki-texta" 10 | } 11 | -------------------------------------------------------------------------------- /resources/legacy/codemirror-fixes.less: -------------------------------------------------------------------------------- 1 | .CodeMirror { 2 | // Fix `font-family: monospace;` caused weird font sizing of browsers. 3 | // Task: https://phabricator.wikimedia.org/T176636 4 | // See: http://code.iamkate.com/html-and-css/fixing-browsers-broken-monospace-font-handling/ 5 | font-family: monospace, monospace; 6 | } 7 | -------------------------------------------------------------------------------- /i18n/th.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Aefgh39622", 5 | "Ans", 6 | "Ekminarin", 7 | "Patsagorn Y." 8 | ] 9 | }, 10 | "codemirror-desc": "จัดเตรียมการเน้นไวยากรณ์ในตัวแก้ไขข้อความวิกิ", 11 | "codemirror-toggle-label": "การเน้นไวยากรณ์ด้วยสี", 12 | "prefs-accessibility": "การเข้าถึง" 13 | } 14 | -------------------------------------------------------------------------------- /i18n/ml.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Praveenp" 5 | ] 6 | }, 7 | "codemirror-desc": "വിക്കിഎഴുത്ത് തിരുത്തൽ സൗകര്യത്തിൽ എഴുത്തുരീതി പ്രമുഖമാക്കി കാണിക്കൽ സൗകര്യം നൽകുന്നു", 8 | "codemirror-toggle-label": "എഴുത്തുരീതി പ്രമുഖമാക്കുക", 9 | "prefs-syntax-highlighting": "എഴുത്തുരീതി പ്രമുഖമാക്കുക" 10 | } 11 | -------------------------------------------------------------------------------- /i18n/ca.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Abella", 5 | "Paucabot", 6 | "Vriullop" 7 | ] 8 | }, 9 | "codemirror-desc": "Proporciona ressaltat de la sintaxi a l'editor de text al wiki", 10 | "codemirror-toggle-label": "Ressaltat de la sintaxi", 11 | "codemirror-beta-feature-title": "Ressaltat de sintaxi millorat" 12 | } 13 | -------------------------------------------------------------------------------- /resources/images/codemirror.wikieditor.check-all.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | check all 5 | 6 | 7 | -------------------------------------------------------------------------------- /resources/images/codemirror.wikieditor.icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | highlight 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /tests/selenium/wdio.conf.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { config } = require( 'wdio-mediawiki/wdio-defaults.conf.js' ); 4 | 5 | exports.config = { ...config, 6 | // Override, or add to, the setting from wdio-mediawiki. 7 | // Learn more at https://webdriver.io/docs/configurationfile/ 8 | // 9 | // Example: 10 | // logLevel: 'info', 11 | 12 | maxInstances: 4 13 | }; 14 | -------------------------------------------------------------------------------- /i18n/crh-latn.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Don Alessandro" 5 | ] 6 | }, 7 | "codemirror-prefs-colorblind": "Vikimetin deñiştirgende tüs soqurlarına (daltoniklerge) uyğun sintaksis shemasını işlet", 8 | "codemirror-prefs-colorblind-help": "Sintaksis yarıqlav gacetini qullansañız, bu funktsiya işlemeycek.", 9 | "prefs-accessibility": "İrişilirlilik" 10 | } 11 | -------------------------------------------------------------------------------- /i18n/io.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Joao Xavier" 5 | ] 6 | }, 7 | "codemirror-toggle-label": "Emfazar sintaxo", 8 | "codemirror-prefs-colorblind": "Kande redaktanta wikitexti, kapabligez ajusti por personi kun daltonismo, por emfazar sintaxo", 9 | "codemirror-prefs-colorblind-help": "Se vu uzas utensilo por emfazar sintaxo, ca preferajo ne funcionos." 10 | } 11 | -------------------------------------------------------------------------------- /i18n/oc.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Lhanars" 5 | ] 6 | }, 7 | "codemirror-toggle-label": "Mesa en evidéncia de la sintaxi", 8 | "codemirror-prefs-colorblind": "Activar l'apercebut accessible als daltonians per la coloracion de sintaxi pendent l'edicion del còdi wiki.", 9 | "codemirror-beta-feature-title": "Mesa en evidéncia ameliorada de la sintaxi" 10 | } 11 | -------------------------------------------------------------------------------- /i18n/isv-latn.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "IJzeren Jan" 5 | ] 6 | }, 7 | "codemirror-prefs-colorblind": "Vključiti kolornu shemu, ktora jest ugodna daltonistam, za podsvětljenja sintaksy pri pravjenju vikiteksta", 8 | "codemirror-prefs-colorblind-help": "Ako vy koristajete gadžet za podsvětljenje sintaksy, tuto nastavjenje ne bude rabotati.", 9 | "prefs-accessibility": "Dostupnost" 10 | } 11 | -------------------------------------------------------------------------------- /i18n/bjn.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Ezagren" 5 | ] 6 | }, 7 | "codemirror-prefs-colorblind": "Nyalaakan bagan nang ramah gasan urang taklit warna gasan panarangan sintaksis wayah mambabak naskah wiki", 8 | "codemirror-prefs-colorblind-help": "Amun Pian mamakai talipun ganggam gasan panarangan sintaksis, kakatujuan ini kada pacangan tapakai.", 9 | "prefs-accessibility": "Aksésibilitas" 10 | } 11 | -------------------------------------------------------------------------------- /i18n/bs.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Srdjan m", 5 | "Srđan" 6 | ] 7 | }, 8 | "codemirror-desc": "Omogućava isticanje sintakse u uređivaču wikiteksta", 9 | "codemirror-toggle-label": "Isticanje sintakse", 10 | "codemirror-prefs-colorblind": "Uključi temu za isticanje sintakse prilagođenu osobama koje imaju poteškoće s prepoznavanjem boja", 11 | "prefs-accessibility": "Pristupačnost" 12 | } 13 | -------------------------------------------------------------------------------- /resources/workers/lua/worker.js: -------------------------------------------------------------------------------- 1 | /* global luacheck */ 2 | const onmessage = require( '../common.js' ); 3 | require( 'luacheck-browserify/dist/es8.min.js' ); 4 | 5 | const Luacheck = luacheck(); 6 | 7 | const setConfig = () => {}; 8 | const getConfig = () => undefined; 9 | const lint = async ( code ) => ( await Luacheck.queue( code ) ) 10 | .filter( ( { severity } ) => severity ); 11 | 12 | onmessage( setConfig, getConfig, lint ); 13 | -------------------------------------------------------------------------------- /tests/selenium/fixturecontent.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Api = require( 'wdio-mediawiki/Api' ); 4 | 5 | const fixture1 = '[]{{template}}'; 6 | 7 | class FixtureContent { 8 | /** 9 | * Create a new fixture for testing syntax highlighting. 10 | * 11 | * @param {string} title 12 | */ 13 | async createFixturePage( title ) { 14 | const bot = await Api.bot(); 15 | await bot.edit( title, fixture1 ); 16 | } 17 | } 18 | 19 | module.exports = new FixtureContent(); 20 | -------------------------------------------------------------------------------- /i18n/hy.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Kareyac", 5 | "Դավիթ Սարոյան" 6 | ] 7 | }, 8 | "codemirror-toggle-label": "Շարահյուսության ընդգծում", 9 | "codemirror-prefs-help": "օգնություն", 10 | "codemirror-previous": "Գտնել նախորդը", 11 | "codemirror-replace": "Փոխարինել", 12 | "codemirror-replace-all": "Փոխարինել բոլորը", 13 | "codemirror-done": "Արված է", 14 | "codemirror-goto-line-go": "Անցնել", 15 | "codemirror-keymap-history": "Պատմություն" 16 | } 17 | -------------------------------------------------------------------------------- /resources/images/codemirror.wikieditor.wrapping.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | wrapping 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /tests/phpunit/unit/HookRunnerTest.php: -------------------------------------------------------------------------------- 1 | [ HookRunner::class ]; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /i18n/ky.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Bosogo" 5 | ] 6 | }, 7 | "codemirror-toggle-label": "Синтаксисти белгилөө", 8 | "codemirror-prefs-section-other": "Башка", 9 | "codemirror-next": "кийинки", 10 | "codemirror-previous": "мурунку", 11 | "codemirror-keymap-help-close": "Жабуу", 12 | "codemirror-keymap-link": "Шилтеме", 13 | "codemirror-keymap-search": "Издөө", 14 | "codemirror-keymap-other": "Башка", 15 | "prefs-syntax-highlighting": "Синтаксисти белгилөө" 16 | } 17 | -------------------------------------------------------------------------------- /i18n/ckb.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Aram" 5 | ] 6 | }, 7 | "codemirror-desc": "ڕۆشنکەری ڕستەسازی لە دەستکاریکەری ویکیدەق دابین دەکات", 8 | "codemirror-toggle-label": "ڕۆشنکردنی ڕستەسازی", 9 | "codemirror-prefs-colorblind": "سیستمی دۆستی ڕەنگکوێر بۆ ڕۆشنکردنی ڕستەسازی لەکاتی دەستکاریکردنی ویکیدەق چالاک بکە", 10 | "codemirror-prefs-colorblind-help": "ئەگەر ئامرازێک بۆ ڕۆشنکردنی ڕستەسازی بەکاردێنیت، ئەم ھەڵبژاردنە کار ناکات.", 11 | "prefs-accessibility": "دەستپێگەیشتن" 12 | } 13 | -------------------------------------------------------------------------------- /i18n/lv.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Papuass" 5 | ] 6 | }, 7 | "codemirror-desc": "Nodrošina sintakses iezīmēšanu vikiteksta redaktorā", 8 | "codemirror-toggle-label": "Sintakses izcelšana", 9 | "codemirror-prefs-colorblind": "Iespējot krāsu aklumam draudzīgu sintakses izcelšanas režīmu, rediģējot vikitekstu", 10 | "codemirror-prefs-colorblind-help": "Ja sintakses iezīmēšanai tiek izmantots sīkrīks, šī preference nedarbosies.", 11 | "prefs-accessibility": "Pieejamība" 12 | } 13 | -------------------------------------------------------------------------------- /i18n/sd.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Mehtab ahmed" 5 | ] 6 | }, 7 | "codemirror-desc": "وڪي ٽيڪسٽ سنوارگاھ ۾ سنٽيڪس کي نمايان ڪرڻ مھيا ڪري ٿو", 8 | "codemirror-toggle-label": "سنٽيڪس نمايان ڪرڻ", 9 | "codemirror-prefs-colorblind": "وڪي ٽيڪسٽ سنوارڻ وقت سنٽيڪس کي نمايان ڪرڻ لاءِ رنگ انڌي سازگار اسڪيم کي فعال ڪريو", 10 | "codemirror-prefs-colorblind-help": "جيڪڏهن توهان سنٽيڪس کي نمايان ڪرڻ لاءِ گيجٽ استعمال ڪندا آهيو، تہ ھي ترجيح ڪم نہ ڪندي.", 11 | "prefs-accessibility": "ايڪسيسبلٽي" 12 | } 13 | -------------------------------------------------------------------------------- /i18n/fy.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "PiefPafPier" 5 | ] 6 | }, 7 | "codemirror-desc": "Jout syntaks-aksintuearring yn 'e wikitekstbewurker", 8 | "codemirror-toggle-label": "Syntaks-aksintuearring", 9 | "codemirror-prefs-colorblind": "Kleureblinefreonlik skema foar syntaks-aksintuearring ynskeakelje by it bewurkjen fan wikitekst", 10 | "codemirror-prefs-colorblind-help": "At jo 'lytsark' foar syntaks-aksintuearring brûke, sil dizze foarkar net wurkje.", 11 | "prefs-accessibility": "Tagonklikens" 12 | } 13 | -------------------------------------------------------------------------------- /resources/images/codemirror.wikieditor.pilcrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | pilcrow 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /resources/legacy/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "wikimedia/client", 5 | "wikimedia/jquery", 6 | "wikimedia/mediawiki" 7 | ], 8 | "globals": { 9 | "CodeMirror": "readonly" 10 | }, 11 | "rules": { 12 | "max-len": "off", 13 | "es-x/no-object-assign": "warn" 14 | }, 15 | "overrides": [ 16 | { 17 | "files": [ 18 | "addon/*.js" 19 | ], 20 | "rules": { 21 | "computed-property-spacing": [ "error", "never" ], 22 | "indent": [ "error", 2 ] 23 | } 24 | } 25 | ] 26 | } 27 | -------------------------------------------------------------------------------- /resources/images/codemirror.wikieditor.gotoLine.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | go to line 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /i18n/hu.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Bencemac", 5 | "Tacsipacsi", 6 | "Urbalazs" 7 | ] 8 | }, 9 | "codemirror-desc": "Szintaxiskiemelő hozzáadása a wikiszöveges szerkesztőhöz", 10 | "codemirror-toggle-label": "Szintaxiskiemelés", 11 | "codemirror-prefs-colorblind": "Színvakbarát színséma használata a wikiszöveges szintaxiskiemeléshez", 12 | "codemirror-prefs-colorblind-help": "Ha egy segédeszközt használsz a szintaxiskiemelésre, arra nincs hatással ez a beállítás.", 13 | "prefs-accessibility": "Akadálymentesség" 14 | } 15 | -------------------------------------------------------------------------------- /i18n/sv.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Lokal Profil", 5 | "Sabelöga", 6 | "WikiPhoenix" 7 | ] 8 | }, 9 | "codemirror-desc": "Ger syntaxmarkerad redigering för redigering av wikitext", 10 | "codemirror-toggle-label": "Syntaxmarkering", 11 | "codemirror-prefs-colorblind": "Aktivera färgblindvänligt färgschema för syntaxmarkering när du redigerar wikitext", 12 | "codemirror-prefs-colorblind-help": "Om du använder en finess för syntaxmarkering kommer inställningen inte fungera.", 13 | "prefs-accessibility": "Tillgänglighet" 14 | } 15 | -------------------------------------------------------------------------------- /tests/selenium/README.md: -------------------------------------------------------------------------------- 1 | # Selenium tests 2 | 3 | For more information see https://www.mediawiki.org/wiki/Selenium 4 | 5 | ## Setup 6 | 7 | See https://www.mediawiki.org/wiki/Extension:CodeMirror 8 | 9 | ## Run all specs 10 | 11 | npm run selenium-test 12 | 13 | ## Run specific tests 14 | 15 | Filter by file name: 16 | 17 | npm run selenium-test -- --spec tests/selenium/specs/[FILE-NAME] 18 | 19 | Filter by file name and test name: 20 | 21 | npm run selenium-test -- --spec tests/selenium/specs/[FILE-NAME] --mochaOpts.grep [TEST-NAME] -------------------------------------------------------------------------------- /i18n/ro.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "NGC 54", 5 | "Paloi Sciurala", 6 | "Strainu" 7 | ] 8 | }, 9 | "codemirror-desc": "Oferă evidențierea sintaxei în editorul wikitext", 10 | "codemirror-toggle-label": "Evidențierea sintaxei", 11 | "codemirror-prefs-colorblind": "Activează schema favorabilă daltoniștilor pentru evidențierea sintaxei atunci când se editează wikitext", 12 | "codemirror-prefs-colorblind-help": "Dacă utilizați un gadget pentru evidențierea sintaxei, această preferință nu va funcționa.", 13 | "prefs-accessibility": "Accesibilitate" 14 | } 15 | -------------------------------------------------------------------------------- /resources/modes/codemirror.bundle.modes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is managed by Rollup and bundles all the listed dependencies 3 | * into the single file resources/lib/codemirror6.bundle.modes.js. 4 | */ 5 | 6 | /* eslint-disable es-x/no-export-ns-from */ 7 | export * from '@codemirror/lang-javascript'; 8 | export * from '@codemirror/lang-css'; 9 | export * from '@codemirror/lang-json'; 10 | export * from '@codemirror/lang-html'; 11 | export * from '@codemirror/lang-vue'; 12 | export * from '@codemirror/legacy-modes/mode/lua'; 13 | export * from '@lezer/common'; 14 | export * from '@lezer/lr'; 15 | -------------------------------------------------------------------------------- /i18n/ba.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Bashkorttan", 5 | "Sagan", 6 | "Азат Хәлилов", 7 | "З. ӘЙЛЕ" 8 | ] 9 | }, 10 | "codemirror-desc": "Вики-тексты мөхәррирләгәндә синтаксисты айырып күрһәтә", 11 | "codemirror-toggle-label": "Синтаксисты айырып күрһәтергә", 12 | "codemirror-prefs-colorblind": "Викитексты мөхәррирләгәндә синтаксисты айырып күрһәтеү өсөн төҫ айырмаусылар схемаһын асырға", 13 | "codemirror-prefs-colorblind-help": "Әгәр синтаксисты айырып күрһәтеү өсөн гаджет ҡулланһағыҙ, был үҙенсәлек эшләмәйәсәк.", 14 | "prefs-accessibility": "Ҡулайлылыҡ" 15 | } 16 | -------------------------------------------------------------------------------- /i18n/ne.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "बडा काजी" 5 | ] 6 | }, 7 | "codemirror-desc": "विकिटेक्स्ट सम्पादकमा सिन्ट्याक्स हाइलाइटिङ प्रदान गर्दछ", 8 | "codemirror-toggle-label": "सिन्ट्याक्स हाइलाइटिङ", 9 | "codemirror-prefs-section-references": "सन्दर्भहरू", 10 | "codemirror-prefs-colorblind": "विकिटेक्स्ट सम्पादन गर्दा सिन्ट्याक्स हाइलाइटिङका लागि कलर ब्लाइन्ड-मैत्री योजना सक्षम गर्नुहोस्", 11 | "codemirror-prefs-colorblind-help": "यदि तपाइँ सिन्ट्याक्स हाइलाइटिङको लागि ग्याजेट प्रयोग गर्नुहुन्छ भने, यो प्राथमिकताले काम गर्दैन।", 12 | "prefs-accessibility": "पहुँच" 13 | } 14 | -------------------------------------------------------------------------------- /resources/images/codemirror.wikieditor.settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | settings 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /i18n/te.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Chaduvari" 5 | ] 6 | }, 7 | "codemirror-toggle-label": "సింటాక్స్ హైలైటింగు", 8 | "codemirror-prefs-summary": "[https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Extension:CodeMirror సహాయం పేజీలో] దీని గురించి మరింత తెలుసుకోవచ్చు", 9 | "codemirror-prefs-enable": "వికీటెక్స్ట్ సింటాక్స్ హైలైటింగును చేతనం చెయ్యి", 10 | "codemirror-prefs-colorblind": "వికీటెక్స్టును సరిదిద్దే సమయంలో సింటాక్సును హైలైటు చేసేలా వర్ణాంధ సహాయకారి వ్యవస్థను చేతనం చెయ్యి", 11 | "codemirror-prefs-colorblind-help": "సింటాక్సును హైలైటు చేసేందుకు మీరు ఏదైనా గాడ్జెటును వాడుతూంటే, ఈ అభిరుచి పనిచెయ్యదు." 12 | } 13 | -------------------------------------------------------------------------------- /i18n/sk.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Luky001", 5 | "TomášPolonec" 6 | ] 7 | }, 8 | "codemirror-desc": "Poskytuje zvýraznenie syntaxe v textovom editore", 9 | "codemirror-toggle-label": "Zvýraznenie syntaxe", 10 | "codemirror-prefs-colorblind": "Povoliť schému pre farboslepých na zvýraznenie syntaxe pri úprave wikitextu", 11 | "prefs-accessibility": "Dostupnosť", 12 | "codemirror-beta-feature-title": "Vylepšené zvýrazňovanie syntaxe", 13 | "codemirror-beta-feature-description": "Získajte predbežný prístup k novým funkciám zvýrazňovania syntaxe a úprav. Medzi nimi je napríklad tzv. code folding alebo automatické dopĺňanie." 14 | } 15 | -------------------------------------------------------------------------------- /.phan/config.php: -------------------------------------------------------------------------------- 1 | { 2 | self.onmessage = async ( { data: [ command, code ] } ) => { 3 | switch ( command ) { 4 | case 'setConfig': 5 | setConfig( code ); 6 | break; 7 | case 'getConfig': 8 | postMessage( [ command, getConfig() ] ); 9 | break; 10 | case 'setI18N': 11 | setI18N( code ); 12 | break; 13 | case 'getI18N': 14 | postMessage( [ command, getI18N() ] ); 15 | break; 16 | case 'setLintConfig': 17 | setLintConfig( code ); 18 | break; 19 | case 'lint': 20 | postMessage( [ command, await lint( code ), code ] ); 21 | } 22 | }; 23 | }; 24 | -------------------------------------------------------------------------------- /i18n/sq.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Xhulianoo" 5 | ] 6 | }, 7 | "codemirror-desc": "Ofron theksim të sintaksës në redaktuesin e wikiteksit", 8 | "codemirror-toggle-label": "Theksimi i sintaksës", 9 | "codemirror-toggle-label-short": "Sintaksa", 10 | "codemirror-prefs-enable": "Aktivizo theksimin e sintaksës", 11 | "codemirror-prefs-title": "Parapëlqimet e theksimit të sintaksës", 12 | "codemirror-prefs-help": "ndihma", 13 | "codemirror-beta-feature-title": "Theksim i përmirësuar i sintaksës", 14 | "codemirror-beta-feature-description": "Merr qasje të hershme te cilësitë e reja të nxjerrjes në pah dhe redaktimit të sintaksës, të tilla si palosja e kodit dhe plotësimi automatik." 15 | } 16 | -------------------------------------------------------------------------------- /resources/workers/mediawiki/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "plugin:es-x/restrict-to-es2017" 5 | ], 6 | "env": { 7 | "worker": true, 8 | "es2017": true 9 | }, 10 | "parserOptions": { 11 | "ecmaVersion": 8 12 | }, 13 | "rules": {}, 14 | "settings": { 15 | "es-x": { 16 | "aggressive": true 17 | } 18 | }, 19 | "overrides": [ 20 | { 21 | "files": [ 22 | "worker.js" 23 | ], 24 | "extends": [ 25 | "wikimedia/client", 26 | "wikimedia/jquery", 27 | "wikimedia/mediawiki" 28 | ] 29 | }, 30 | { 31 | "files": [ 32 | "worker.min.js" 33 | ], 34 | "rules": { 35 | "es-x/no-regexp-unicode-property-escapes": 0 36 | } 37 | } 38 | ] 39 | } 40 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require-dev": { 3 | "mediawiki/mediawiki-codesniffer": "48.0.0", 4 | "mediawiki/mediawiki-phan-config": "0.17.0", 5 | "mediawiki/minus-x": "1.1.3", 6 | "php-parallel-lint/php-console-highlighter": "1.0.0", 7 | "php-parallel-lint/php-parallel-lint": "1.4.0" 8 | }, 9 | "scripts": { 10 | "fix": [ 11 | "minus-x fix .", 12 | "phpcbf" 13 | ], 14 | "test": [ 15 | "parallel-lint . --exclude vendor --exclude node_modules", 16 | "@phpcs", 17 | "minus-x check ." 18 | ], 19 | "phan": "phan -d . --long-progress-bar", 20 | "phpcs": "phpcs -sp --cache" 21 | }, 22 | "config": { 23 | "allow-plugins": { 24 | "dealerdirect/phpcodesniffer-composer-installer": true 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /resources/lib/foreign-resources.cdx.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://cyclonedx.org/schema/bom-1.6.schema.json", 3 | "bomFormat": "CycloneDX", 4 | "specVersion": "1.6", 5 | "serialNumber": "urn:uuid:20f741ef-73a8-45ce-b04c-faa5aa7a6252", 6 | "version": 1, 7 | "components": [ 8 | { 9 | "type": "library", 10 | "name": "codemirror", 11 | "version": "5.65.15", 12 | "licenses": [ 13 | { 14 | "license": { 15 | "id": "MIT" 16 | } 17 | } 18 | ], 19 | "authors": [ 20 | { 21 | "name": "CodeMirror authors" 22 | } 23 | ], 24 | "externalReferences": [ 25 | { 26 | "url": "https://github.com/codemirror/codemirror5", 27 | "type": "website" 28 | } 29 | ] 30 | } 31 | ] 32 | } 33 | -------------------------------------------------------------------------------- /tests/jest/codemirror.init.test.js: -------------------------------------------------------------------------------- 1 | describe( 'CodeMirror.init.js', () => { 2 | it( 'should load only ext.WikiEditor for read-only pages with usebetatoolbar=1', () => { 3 | mw.config.get = jest.fn().mockImplementation( ( key ) => { 4 | if ( key === 'cmRLModules' ) { 5 | return [ 'ext.CodeMirror.v6', 'ext.CodeMirror.v6.WikiEditor' ]; 6 | } else if ( key === 'cmReadOnly' ) { 7 | return true; 8 | } 9 | } ); 10 | mw.user.options.get = jest.fn().mockImplementation( ( key ) => { 11 | if ( key === 'usecodemirror' ) { 12 | return 0; 13 | } 14 | } ); 15 | const spy = jest.spyOn( mw.loader, 'load' ); 16 | require( '../../resources/codemirror.init.js' ); 17 | expect( spy ).toHaveBeenCalledWith( 'ext.wikiEditor' ); 18 | } ); 19 | } ); 20 | -------------------------------------------------------------------------------- /resources/workers/css/worker.js: -------------------------------------------------------------------------------- 1 | /* global stylelint */ 2 | const { rules } = require( 'stylelint-config-recommended' ); 3 | const onmessage = require( '../common.js' ); 4 | require( '@bhsd/stylelint-browserify/bundle/stylelint-es8.min.js' ); 5 | 6 | let customRules = rules; 7 | 8 | const setConfig = ( config ) => { 9 | customRules = Object.assign( {}, rules, config ); 10 | }; 11 | const getConfig = () => customRules; 12 | const lint = ( code ) => stylelint.lint( { 13 | code, 14 | config: { 15 | defaultSeverity: 'warning', 16 | rules: customRules, 17 | computeEditInfo: true 18 | } 19 | } ).then( ( { results } ) => results[ 0 ].warnings 20 | .filter( ( { text } ) => !text.startsWith( 'Unknown rule ' ) ) ); 21 | 22 | onmessage( setConfig, getConfig, lint ); 23 | -------------------------------------------------------------------------------- /resources/workers/css/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "plugin:es-x/restrict-to-es2017" 5 | ], 6 | "env": { 7 | "worker": true, 8 | "es2017": true 9 | }, 10 | "parserOptions": { 11 | "ecmaVersion": 8 12 | }, 13 | "rules": { 14 | "es-x/no-array-prototype-findlast-findlastindex": 0, 15 | "es-x/no-array-string-prototype-at": 0, 16 | "es-x/no-symbol-prototype-description": 0, 17 | "es-x/no-array-prototype-at": "warn", 18 | "es-x/no-string-prototype-at": "warn" 19 | }, 20 | "settings": { 21 | "es-x": { 22 | "aggressive": true 23 | } 24 | }, 25 | "overrides": [ 26 | { 27 | "files": [ 28 | "worker.js" 29 | ], 30 | "extends": [ 31 | "wikimedia/client", 32 | "wikimedia/jquery", 33 | "wikimedia/mediawiki" 34 | ] 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /includes/Hooks/CodeMirrorSpecialPageHook.php: -------------------------------------------------------------------------------- 1 | hookContainer->run( 'CodeMirrorGetMode', [ $title, &$mode, $model ] ); 24 | } 25 | 26 | /** 27 | * @inheritDoc 28 | */ 29 | public function onCodeMirrorSpecialPage( SpecialPage $special, array &$textareas ): bool { 30 | return $this->hookContainer->run( 'CodeMirrorSpecialPage', [ $special, &$textareas ] ); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /resources/codemirror.styleModule.less: -------------------------------------------------------------------------------- 1 | @import 'mediawiki.skin.variables.less'; 2 | 3 | // Styles here are render-blocking. 4 | // Only add styles that are essential for initial rendering. 5 | 6 | .client-js { 7 | // From Extension:WikiEditor (GPL-2.0+) 8 | .mw-editform { 9 | /* Disable margin collapse, e.g. with messages boxes */ 10 | padding: 0.05px; 11 | 12 | > #wpTextbox1 { /* stylelint-disable-line selector-max-id */ 13 | /* Toolbar height + padding + bottom border = 26 + 2*3 + 1 */ 14 | margin-top: 33px; 15 | } 16 | } 17 | 18 | > .cm-mw-wikieditor-loading { 19 | // Adjust textarea to make room for CodeMirror status bar and gutter. 20 | // This makes room for a gutter with 3-digit line numbers + linting markers, 21 | // which is the same assumption made by CodeEditor's style module. 22 | #wpTextbox1 { /* stylelint-disable-line selector-max-id */ 23 | padding-bottom: @spacing-150; 24 | padding-left: 70px; 25 | padding-top: @spacing-25; 26 | line-height: 1.4; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /resources/modes/codemirror.json.js: -------------------------------------------------------------------------------- 1 | const { jsonLanguage, jsonParseLinter } = require( '../lib/codemirror6.bundle.modes.js' ); 2 | const CodeMirrorMode = require( './codemirror.mode.js' ); 3 | 4 | /** 5 | * JSON language support for CodeMirror. 6 | * 7 | * @example 8 | * const require = await mw.loader.using( [ 'ext.CodeMirror.v6', 'ext.CodeMirror.v6.modes' ] ); 9 | * const CodeMirror = require( 'ext.CodeMirror.v6' ); 10 | * const { json } = require( 'ext.CodeMirror.v6.modes' ); 11 | * const cm = new CodeMirror( myTextarea, json() ); 12 | * cm.initialize(); 13 | * @extends CodeMirrorMode 14 | * @hideconstructor 15 | */ 16 | class CodeMirrorJson extends CodeMirrorMode { 17 | 18 | /** @inheritDoc */ 19 | get language() { 20 | return jsonLanguage; 21 | } 22 | 23 | /** @inheritDoc */ 24 | get lintSource() { 25 | return jsonParseLinter(); 26 | } 27 | 28 | /** @inheritDoc */ 29 | get hasWorker() { 30 | // JSON linting is done in the main thread. 31 | return false; 32 | } 33 | } 34 | 35 | module.exports = CodeMirrorJson; 36 | -------------------------------------------------------------------------------- /resources/modes/codemirror.vue.js: -------------------------------------------------------------------------------- 1 | const { vue, vueLanguage } = require( '../lib/codemirror6.bundle.modes.js' ); 2 | const CodeMirrorMode = require( './codemirror.mode.js' ); 3 | 4 | /** 5 | * Vue language support for CodeMirror. 6 | * 7 | * @example 8 | * const require = await mw.loader.using( [ 'ext.CodeMirror.v6', 'ext.CodeMirror.v6.modes' ] ); 9 | * const CodeMirror = require( 'ext.CodeMirror.v6' ); 10 | * const { vue } = require( 'ext.CodeMirror.v6.modes' ); 11 | * const cm = new CodeMirror( myTextarea, vue() ); 12 | * cm.initialize(); 13 | * @extends CodeMirrorMode 14 | * @hideconstructor 15 | */ 16 | class CodeMirrorVue extends CodeMirrorMode { 17 | /** @inheritDoc */ 18 | get language() { 19 | return vueLanguage; 20 | } 21 | 22 | /** @inheritDoc */ 23 | get support() { 24 | return vue().support; 25 | } 26 | 27 | /** @inheritDoc */ 28 | get lintSource() { 29 | // TODO: Implement linting for Vue files. 30 | return undefined; 31 | } 32 | 33 | /** @inheritDoc */ 34 | get hasWorker() { 35 | return false; 36 | } 37 | } 38 | 39 | module.exports = CodeMirrorVue; 40 | -------------------------------------------------------------------------------- /i18n/tr.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "BaRaN6161 TURK", 5 | "Diyapazon", 6 | "LisafBia", 7 | "SaldırganSincap", 8 | "Vito Genovese" 9 | ] 10 | }, 11 | "codemirror-desc": "Vikimetin düzenleyicide sözdizim vurgulaması sağlar", 12 | "codemirror-toggle-label": "Sözdizim vurgulama", 13 | "codemirror-prefs-colorblind": "Vikimetni düzenlerken sözdizimi vurgulaması için renk körü dostu düzeni etkinleştir", 14 | "codemirror-prefs-colorblind-help": "Sözdizimi vurgulaması için bir küçük aracı kullanıyorsanız, bu tercih çalışmayacaktır.", 15 | "codemirror-find": "Bul", 16 | "codemirror-next": "Sonrakini bul", 17 | "codemirror-previous": "Öncekini bul", 18 | "codemirror-all-tooltip": "Tüm eşleşmeleri bul", 19 | "codemirror-match-case": "Büyük/küçük harf duyarlı", 20 | "codemirror-regexp": "Düzenli ifade", 21 | "codemirror-by-word": "Tüm kelime", 22 | "codemirror-replace": "Değiştir", 23 | "codemirror-replace-placeholder": "Değiştir", 24 | "codemirror-replace-all": "Hepsini değiştir", 25 | "codemirror-done": "Bitti", 26 | "prefs-accessibility": "Erişilebilirlik" 27 | } 28 | -------------------------------------------------------------------------------- /i18n/cs.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Meliganai", 5 | "Mormegil" 6 | ] 7 | }, 8 | "codemirror-desc": "Poskytuje zvýrazňování syntaxe v editoru wikitextu", 9 | "codemirror-toggle-label": "Zvýraznění syntaxe", 10 | "codemirror-toggle-label-short": "Syntaxe", 11 | "codemirror-prefs-summary": "Více informací o této funkci najdete na [https://www.mediawiki.org/wiki/Special:MyLanguage/Help:Extension:CodeMirror stránce nápovědy].", 12 | "codemirror-prefs-enable": "Zapnout zvýrazňování syntaxe", 13 | "codemirror-prefs-title": "Nastavení zvýrazňování syntaxe", 14 | "codemirror-prefs-help": "nápověda", 15 | "codemirror-prefs-colorblind": "Zapnout schéma pro barvoslepé u zvýrazňování syntaxe při editaci wikitextu", 16 | "codemirror-prefs-colorblind-help": "Pokud používáte pro zvýrazňování syntaxe udělátko, nebude toto nastavení fungovat.", 17 | "prefs-accessibility": "Přístupnost", 18 | "codemirror-beta-feature-title": "Vylepšené zvýrazňování syntaxe", 19 | "codemirror-beta-feature-description": "Získejte předběžný přístup k novému zvýrazňování syntaxe a editačním funkcím jako sbalování a automatické dokončování kódu." 20 | } 21 | -------------------------------------------------------------------------------- /tests/jest/modes/codemirror.lua.codeFolding.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable-next-line n/no-missing-require */ 2 | const { foldable } = require( 'ext.CodeMirror.v6.lib' ); 3 | const CodeMirror = require( '../../../resources/codemirror.js' ); 4 | const { lua } = require( '../../../resources/modes/codemirror.mode.exporter.js' ); 5 | 6 | // Setup CodeMirror instance. 7 | const textarea = document.createElement( 'textarea' ); 8 | document.body.appendChild( textarea ); 9 | const cm = new CodeMirror( textarea, lua() ); 10 | cm.initialize(); 11 | 12 | const test = ( insert, result ) => { 13 | cm.view.dispatch( { 14 | changes: { from: 0, to: cm.view.state.doc.length, insert } 15 | } ); 16 | expect( foldable( cm.view.state, 0, insert.indexOf( '\n' ) ) ).toEqual( result ); 17 | }; 18 | 19 | describe( 'Lua folding', () => { 20 | it( 'no folding', () => { 21 | test( 'a\n\nb', null ); 22 | test( '\ta\n\t\t\n b', null ); 23 | test( '\ta\nb', null ); 24 | } ); 25 | it( 'folding', () => { 26 | test( 'a\n\tb\n\tc\nd', { from: 1, to: 7 } ); 27 | test( 'a\n b\n c\n d\ne', { from: 1, to: 15 } ); 28 | test( '\ta\n\t\tb\n\tc', { from: 2, to: 6 } ); 29 | } ); 30 | } ); 31 | -------------------------------------------------------------------------------- /resources/codemirror.panel.js: -------------------------------------------------------------------------------- 1 | const { EditorView, Extension, Panel } = require( 'ext.CodeMirror.v6.lib' ); 2 | const CodeMirrorCodex = require( './codemirror.codex.js' ); 3 | 4 | /** 5 | * Abstract class for a panel that can be used with CodeMirror. 6 | * This class provides methods to create CSS-only Codex components. 7 | * 8 | * @see https://codemirror.net/docs/ref/#h_panels 9 | * @todo Move HTML generation to Mustache templates. 10 | * @abstract 11 | */ 12 | class CodeMirrorPanel extends CodeMirrorCodex { 13 | /** 14 | * @constructor 15 | */ 16 | constructor() { 17 | super(); 18 | /** @type {EditorView} */ 19 | this.view = undefined; 20 | } 21 | 22 | /** 23 | * Get the panel and any associated keymaps as an Extension. 24 | * For use only during CodeMirror initialization. 25 | * 26 | * @abstract 27 | * @type {Extension} 28 | * @internal 29 | */ 30 | // eslint-disable-next-line getter-return 31 | get extension() {} 32 | 33 | /** 34 | * Get the Panel object. 35 | * 36 | * @abstract 37 | * @type {Panel} 38 | */ 39 | // eslint-disable-next-line getter-return 40 | get panel() {} 41 | } 42 | 43 | module.exports = CodeMirrorPanel; 44 | -------------------------------------------------------------------------------- /resources/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": [ 4 | "wikimedia/client", 5 | "wikimedia/jquery", 6 | "wikimedia/mediawiki" 7 | ], 8 | "parserOptions": { 9 | "sourceType": "module" 10 | }, 11 | "env": { 12 | "browser": true, 13 | "commonjs": true 14 | }, 15 | "globals": { 16 | "Tree": "readonly", 17 | "ve": "readonly" 18 | }, 19 | "rules": { 20 | "es-x/no-array-prototype-includes": "off", 21 | "jsdoc/no-undefined-types": [ 22 | "warn", 23 | { 24 | "definedTypes": [ 25 | "CodeMirror", 26 | "CodeMirrorChild", 27 | "CodeMirrorKeyBinding", 28 | "CodeMirrorKeymap", 29 | "CodeMirrorMediaWiki", 30 | "CodeMirrorPreferences", 31 | "CodeMirrorTextSelection", 32 | "Command", 33 | "Hook", 34 | "LanguageSupport", 35 | "StateCommand" 36 | ] 37 | } 38 | ], 39 | "es-x/no-string-prototype-replaceall": "warn" 40 | }, 41 | "overrides": [ 42 | { 43 | "files": [ 44 | "addon/edit/*.js" 45 | ], 46 | "rules": { 47 | "computed-property-spacing": [ 48 | "error", 49 | "never" 50 | ], 51 | "indent": [ 52 | "error", 53 | 2 54 | ] 55 | } 56 | } 57 | ] 58 | } 59 | -------------------------------------------------------------------------------- /i18n/ha.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Ezeone10", 5 | "Ummabruka" 6 | ] 7 | }, 8 | "codemirror-find": "Nemo", 9 | "codemirror-next": "Nagaba", 10 | "codemirror-all": "Duka", 11 | "codemirror-match-case": "Daidaituwar lamari", 12 | "codemirror-replace": "Maye gurbi", 13 | "codemirror-replace-all": "Mayar da gurbin duka", 14 | "codemirror-special-char-backspace": "Gogewa", 15 | "codemirror-special-char-newline": "\nSabon jeri", 16 | "codemirror-special-char-vertical-tab": "Shafin Dake fitowa a tasye", 17 | "codemirror-special-char-nbsp": "Tazara mara yankewa", 18 | "codemirror-special-char-left-to-right-mark": "Alamar hagu zuwa dama", 19 | "codemirror-special-char-right-to-left-mark": "Alama daga dama zuwa hagu", 20 | "codemirror-special-char-left-to-right-override": " Shafewa daga hagu zuwa dama", 21 | "codemirror-special-char-left-to-right-isolate": "Kebe hagu zuwa dama", 22 | "codemirror-special-char-right-to-left-isolate": "Kebe Daman xuwa hagu", 23 | "codemirror-special-char-paragraph-separator": "Mai raba dakin layi", 24 | "codemirror-keymap-nowiki": "Babu tsarin wiki", 25 | "codemirror-keymap-copyline": "Kwafin zaɓaɓɓun layin zuwa layin sama ko ƙasa" 26 | } 27 | -------------------------------------------------------------------------------- /resources/lib/codemirror/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (C) 2017 by Marijn Haverbeke and others 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /i18n/sl.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Eleassar" 5 | ] 6 | }, 7 | "codemirror-desc": "Omogoča označevanje skladnje v urejevalniku vikibesedila", 8 | "codemirror-toggle-label": "Označevanje skladnje", 9 | "codemirror-prefs-enable": "Omogoči označevanje skladnje", 10 | "codemirror-prefs-title": "Nastavitve označevanja skladnje", 11 | "codemirror-prefs-colorblind": "Omogoči barvno slepim prijazno shemo za označevanje skladnje pri urejanju vikibesedila", 12 | "codemirror-prefs-colorblind-help": "Če uporabljate pripomoček za označevanje skladnje, ta nastavitev ne bo delovala.", 13 | "codemirror-unfold": "razširjenje", 14 | "codemirror-folded-code": "strnjena koda", 15 | "codemirror-keymap-computercode": "Računalniška koda", 16 | "codemirror-keymap-blockquote": "Citatni blok", 17 | "codemirror-keymap-preferences": "Nastavitve označevanja skladnje", 18 | "prefs-accessibility": "Dostopnost", 19 | "prefs-syntax-highlighting": "Označevanje skladnje", 20 | "codemirror-wikilint-remove": "odstrani", 21 | "codemirror-beta-feature-title": "Izboljšano označevanje skladnje", 22 | "codemirror-beta-feature-description": "Pridobite zgodnji dostop do novih funkcij označevanja in urejanja skladnje, kot sta zlaganje kode in samodejno dokončanje." 23 | } 24 | -------------------------------------------------------------------------------- /resources/legacy/ext.CodeMirror.less: -------------------------------------------------------------------------------- 1 | @import 'mediawiki.skin.variables.less'; 2 | 3 | @matching-bracket-border-color: #eee; 4 | @matching-bracket-box-shadow-color: #999; 5 | 6 | // CM5 dark mode fixes, see T365311 7 | .mw-body-content .CodeMirror { 8 | background-color: inherit; 9 | color: inherit; 10 | } 11 | 12 | .wikiEditor-ui .CodeMirror { 13 | line-height: 1.5em; 14 | padding: 0.1em; 15 | clear: both; 16 | box-sizing: border-box; 17 | 18 | pre, 19 | .CodeMirror-lines { 20 | padding: 0; 21 | } 22 | } 23 | 24 | .CodeMirror-gutters { 25 | background-color: @background-color-interactive-subtle; 26 | border-right-color: @border-color-subtle; 27 | } 28 | 29 | .CodeMirror-line::selection, 30 | .CodeMirror-line > span::selection, 31 | .CodeMirror-line > span > span::selection { 32 | background: #d9d9d9; 33 | 34 | @media screen { 35 | html.skin-theme-clientpref-night & { 36 | background: #233; 37 | } 38 | } 39 | 40 | @media screen and ( prefers-color-scheme: dark ) { 41 | html.skin-theme-clientpref-os { 42 | background: #233; 43 | } 44 | } 45 | } 46 | 47 | .cm-mw-matchingbracket { 48 | background-color: @matching-bracket-border-color; 49 | box-shadow: inset 0 0 1px 1px @matching-bracket-box-shadow-color; 50 | font-weight: bold; 51 | } 52 | -------------------------------------------------------------------------------- /resources/workers/javascript/worker.js: -------------------------------------------------------------------------------- 1 | /* global eslint */ 2 | const onmessage = require( '../common.js' ); 3 | require( '@bhsd/eslint-browserify/bundle/eslint-es8.min.js' ); 4 | 5 | const linter = new eslint.Linter(); 6 | const config = { 7 | parserOptions: { 8 | ecmaVersion: 15 9 | }, 10 | env: { 11 | browser: true, 12 | es2024: true 13 | }, 14 | globals: { 15 | mw: 'readonly', 16 | mediaWiki: 'readonly', 17 | $: 'readonly', 18 | jQuery: 'readonly', 19 | OO: 'readonly', 20 | addOnloadHook: 'readonly', 21 | importScriptURI: 'readonly', 22 | importScript: 'readonly', 23 | importStylesheet: 'readonly', 24 | importStylesheetURI: 'readonly', 25 | RLQ: 'readonly', 26 | require: 'readonly', 27 | module: 'readonly' 28 | }, 29 | rules: {} 30 | }; 31 | for ( const [ name, { meta } ] of linter.getRules() ) { 32 | if ( meta && meta.docs && meta.docs.recommended ) { 33 | config.rules[ name ] = 1; 34 | } 35 | } 36 | 37 | let customConfig = config; 38 | 39 | const setConfig = ( newConfig ) => { 40 | customConfig = Object.assign( {}, newConfig, { 41 | parserOptions: Object.assign( {}, config.parserOptions, newConfig.parserOptions ), 42 | env: Object.assign( {}, config.env, newConfig.env ), 43 | globals: Object.assign( {}, config.globals, newConfig.globals ), 44 | rules: Object.assign( {}, config.rules, newConfig.rules ) 45 | } ); 46 | }; 47 | const getConfig = () => customConfig; 48 | const lint = ( code ) => linter.verify( code, customConfig ); 49 | 50 | onmessage( setConfig, getConfig, lint ); 51 | -------------------------------------------------------------------------------- /tests/jest/modes/codemirror.css.autocomplete.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable-next-line n/no-missing-require */ 2 | const { CompletionContext } = require( 'ext.CodeMirror.v6.lib' ); 3 | const CodeMirror = require( '../../../resources/codemirror.js' ); 4 | const { css } = require( '../../../resources/modes/codemirror.mode.exporter.js' ); 5 | 6 | // Setup CodeMirror instance. 7 | const textarea = document.createElement( 'textarea' ); 8 | document.body.appendChild( textarea ); 9 | const cm = new CodeMirror( textarea, css() ); 10 | cm.initialize(); 11 | const [ source ] = cm.view.state.languageDataAt( 'autocomplete' ); 12 | 13 | /** 14 | * Create a completion context at a specific position. 15 | * 16 | * @return {CompletionContext} 17 | */ 18 | const createCompletionContext = () => new CompletionContext( 19 | cm.view.state, 20 | /** @see https://github.com/codemirror/autocomplete/blob/62dead94d0f4b256f0b437b4733cfef6449e8453/src/completion.ts#L273 */ 21 | cm.view.state.selection.main.from, 22 | true, 23 | cm.view 24 | ); 25 | 26 | describe( 'CSS autocomplete', () => { 27 | it( 'should boost supported CSS property values', () => { 28 | cm.view.dispatch( { 29 | changes: { from: 0, to: cm.view.state.doc.length, insert: 'a { top: i' }, 30 | selection: { anchor: 10, head: 10 } 31 | } ); 32 | expect( 33 | source( createCompletionContext() ).options.filter( ( { boost } ) => boost > 0 ) 34 | ).toEqual( [ 35 | { label: 'inherit', type: 'keyword', boost: 50 }, 36 | { label: 'initial', type: 'keyword', boost: 50 } 37 | ] ); 38 | } ); 39 | } ); 40 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = { 4 | // Automatically clear mock calls and instances between every test 5 | clearMocks: true, 6 | 7 | // Indicates whether the coverage information should be collected while executing the test 8 | collectCoverage: true, 9 | 10 | // An array of glob patterns indicating a set of files fo 11 | // which coverage information should be collected 12 | collectCoverageFrom: [ 13 | 'resources/**/*.js' 14 | ], 15 | 16 | // The directory where Jest should output its coverage files 17 | coverageDirectory: 'coverage', 18 | 19 | // An array of regexp pattern strings used to skip coverage collection 20 | coveragePathIgnorePatterns: [ 21 | '/node_modules/', 22 | '/resources/legacy/', 23 | '/resources/lib/', 24 | '/resources/ve-cm/', 25 | '/resources/workers/' 26 | ], 27 | 28 | // An object that configures minimum threshold enforcement for coverage results 29 | coverageThreshold: { 30 | global: { 31 | branches: 50, 32 | functions: 58, 33 | lines: 65, 34 | statements: 65 35 | } 36 | }, 37 | 38 | // An array of file extensions your modules use 39 | moduleFileExtensions: [ 40 | 'js', 41 | 'json' 42 | ], 43 | 44 | // The paths to modules that run some code to configure or 45 | // set up the testing environment before each test 46 | setupFiles: [ 47 | './tests/jest/setup.js' 48 | ], 49 | 50 | // Simulates a real browser environment. 51 | testEnvironment: 'jsdom', 52 | 53 | // Ignore these directories when locating tests to run. 54 | testPathIgnorePatterns: [ 55 | '/node_modules/', 56 | '/resources/' 57 | ] 58 | }; 59 | -------------------------------------------------------------------------------- /resources/codemirror.bundle.lib.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file is managed by Rollup and bundles all the CodeMirror dependencies 3 | * into the single file resources/lib/codemirror6.bundle.lib.js. 4 | */ 5 | 6 | /** 7 | * @module ext.CodeMirror.v6.lib 8 | * @description 9 | * This module provides the core upstream CodeMirror library. 10 | * You shouldn't need to require this directly unless you want 11 | * access to the upstream {@link https://codemirror.net/docs/ref/ CodeMirror API}. 12 | * @example 13 | * await require = mw.loader.using( [ 'ext.CodeMirror.v6', 'ext.CodeMirror.v6.mode.mediawiki' ] ); 14 | * const CodeMirror = require( 'ext.CodeMirror.v6' ); 15 | * const { mediawiki } = require( 'ext.CodeMirror.v6.mode.mediawiki' ); 16 | * // ext.CodeMirror.v6.lib is a dependency of ext.CodeMirror.v6, so it's already loaded. 17 | * const { EditorView } = require( 'ext.CodeMirror.v6.lib' ); 18 | * const myExtension = EditorView.updateListener.of( ( update ) => { 19 | * if ( update.docChanged ) { 20 | * // do something 21 | * console.log( update.changes ); 22 | * } 23 | * } ); 24 | * const cm = new CodeMirror( myTextarea, mediawiki() ); 25 | * cm.initialize( [ cm.defaultExtensions, myExtension ] ); 26 | */ 27 | 28 | /* eslint-disable es-x/no-export-ns-from */ 29 | export * from '@codemirror/autocomplete'; 30 | export * from '@codemirror/commands'; 31 | export * from '@codemirror/language'; 32 | export * from '@codemirror/lint'; 33 | export * from '@codemirror/search'; 34 | export * from '@codemirror/state'; 35 | export * from '@codemirror/theme-one-dark'; 36 | export * from '@codemirror/view'; 37 | export * from '@lezer/highlight'; 38 | -------------------------------------------------------------------------------- /tests/phpunit/DataScriptTest.php: -------------------------------------------------------------------------------- 1 | createMock( ResourceLoader::class ), new FauxRequest() ); 18 | $script = DataScript::makeScript( $context ); 19 | $this->assertStringContainsString( '"extCodeMirrorConfig":', $script ); 20 | $this->assertStringContainsString( '"legacyLineNumberingNamespaces":', $script ); 21 | $this->assertStringContainsString( '"pluginModules":', $script ); 22 | $this->assertStringContainsString( '"tagModes":', $script ); 23 | $this->assertStringContainsString( '"tags":', $script ); 24 | $this->assertStringContainsString( '"doubleUnderscore":', $script ); 25 | $this->assertStringContainsString( '"functionSynonyms":', $script ); 26 | $this->assertStringContainsString( '"functionHooks":', $script ); 27 | $this->assertStringContainsString( '"variableIDs"', $script ); 28 | $this->assertStringContainsString( '"redirection":', $script ); 29 | $this->assertStringContainsString( '"subst":', $script ); 30 | $this->assertStringContainsString( '"urlProtocols":', $script ); 31 | $this->assertStringContainsString( '"linkTrailCharacters":', $script ); 32 | $this->assertStringContainsString( '"imageKeywords":', $script ); 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /tests/selenium/userpreferences.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Api = require( 'wdio-mediawiki/Api' ), 4 | BlankPage = require( 'wdio-mediawiki/BlankPage' ), 5 | LoginPage = require( 'wdio-mediawiki/LoginPage' ), 6 | Util = require( 'wdio-mediawiki/Util' ); 7 | 8 | class UserPreferences { 9 | async loginAsOther() { 10 | const username = Util.getTestString( 'User-' ); 11 | const password = Util.getTestString(); 12 | await Api.createAccount( await Api.bot(), username, password ); 13 | await LoginPage.login( username, password ); 14 | } 15 | 16 | async setPreferences( preferences ) { 17 | Util.waitForModuleState( 'mediawiki.base' ); 18 | 19 | return await browser.execute( ( prefs ) => mw.loader.using( 'mediawiki.api' ).then( () => new mw.Api().saveOptions( prefs ) ), preferences ); 20 | } 21 | 22 | async enableWikitext2010EditorWithCodeMirror() { 23 | await BlankPage.open(); 24 | await this.setPreferences( { 25 | usebetatoolbar: '1', 26 | usecodemirror: '1', 27 | 'codemirror-preferences': '{"bracketMatching":1,"lineWrapping":1,"activeLine":0,"specialChars":1,"bidiIsolation":0}', 28 | 'visualeditor-enable': '0', 29 | 'visualeditor-newwikitext': '0' 30 | } ); 31 | } 32 | 33 | async enableWikitext2017EditorWithCodeMirror( preferences = {} ) { 34 | await BlankPage.open(); 35 | await this.setPreferences( Object.assign( { 36 | usebetatoolbar: '0', 37 | usecodemirror: '1', 38 | 'codemirror-preferences': '{"bracketMatching":1,"lineWrapping":1,"activeLine":0,"specialChars":1,"bidiIsolation":0}', 39 | 'visualeditor-enable': '1', 40 | 'visualeditor-newwikitext': '1' 41 | }, preferences ) ); 42 | } 43 | } 44 | 45 | module.exports = new UserPreferences(); 46 | -------------------------------------------------------------------------------- /tests/jest/modes/codemirror.mediawiki.openLinks.test.js: -------------------------------------------------------------------------------- 1 | const CodeMirror = require( '../../../resources/codemirror.js' ); 2 | const { mediawiki } = require( '../../../resources/modes/mediawiki/codemirror.mediawiki.js' ); 3 | 4 | describe( 'CodeMirrorOpenLinks', () => { 5 | let cm; 6 | 7 | beforeEach( () => { 8 | const textarea = document.createElement( 'textarea' ); 9 | document.body.appendChild( textarea ); 10 | cm = new CodeMirror( textarea, mediawiki() ); 11 | cm.initialize(); 12 | cm.textSelection.setContents( '[[Foo]] {{bar}} https://example.org' ); 13 | } ); 14 | 15 | it( 'should add .cm-mw-open-links to page titles', () => { 16 | cm.view.contentDOM.dispatchEvent( new KeyboardEvent( 'keydown', { key: 'Control', bubbles: true } ) ); 17 | expect( cm.view.contentDOM.classList ).toContain( 'cm-mw-open-links' ); 18 | cm.view.contentDOM.dispatchEvent( new KeyboardEvent( 'keyup', { key: 'Control', bubbles: true } ) ); 19 | expect( cm.view.contentDOM.classList ).not.toContain( 'cm-mw-open-links' ); 20 | } ); 21 | 22 | it( 'should remove .cm-mw-open-links if the document becomes hidden', () => { 23 | cm.view.contentDOM.dispatchEvent( new KeyboardEvent( 'keydown', { key: 'Control', bubbles: true } ) ); 24 | expect( cm.view.contentDOM.classList ).toContain( 'cm-mw-open-links' ); 25 | expect( document.hidden ).toBe( false ); 26 | Object.defineProperty( document, 'hidden', { value: true, writable: true } ); 27 | document.dispatchEvent( new Event( 'visibilitychange' ) ); 28 | expect( cm.view.contentDOM.classList ).not.toContain( 'cm-mw-open-links' ); 29 | // Reset the hidden property 30 | Object.defineProperty( document, 'hidden', { value: false, writable: true } ); 31 | } ); 32 | } ); 33 | -------------------------------------------------------------------------------- /tests/jest/modes/codemirror.javascript.autocomplete.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable-next-line n/no-missing-require */ 2 | const { CompletionContext } = require( 'ext.CodeMirror.v6.lib' ); 3 | const CodeMirror = require( '../../../resources/codemirror.js' ); 4 | const { javascript } = require( '../../../resources/modes/codemirror.mode.exporter.js' ); 5 | 6 | // Setup CodeMirror instance. 7 | const textarea = document.createElement( 'textarea' ); 8 | document.body.appendChild( textarea ); 9 | const cm = new CodeMirror( textarea, javascript() ); 10 | cm.initialize(); 11 | const [ ,, source ] = cm.view.state.languageDataAt( 'autocomplete' ); 12 | 13 | /** 14 | * Create a completion context at a specific position. 15 | * 16 | * @return {CompletionContext} 17 | */ 18 | const createCompletionContext = () => new CompletionContext( 19 | cm.view.state, 20 | /** @see https://github.com/codemirror/autocomplete/blob/62dead94d0f4b256f0b437b4733cfef6449e8453/src/completion.ts#L273 */ 21 | cm.view.state.selection.main.from, 22 | true, 23 | cm.view 24 | ); 25 | 26 | describe( 'JavaScript autocomplete', () => { 27 | it( 'should support scope completion', () => { 28 | cm.view.dispatch( { 29 | changes: { from: 0, to: cm.view.state.doc.length, insert: 'mw.storage.' }, 30 | selection: { anchor: 11, head: 11 } 31 | } ); 32 | expect( source( createCompletionContext() ) ).toEqual( { 33 | from: 11, 34 | options: [ 35 | { label: 'set', type: 'method', boost: -0 }, 36 | { label: 'get', type: 'method', boost: -0 }, 37 | { label: 'getObject', type: 'method', boost: -0 }, 38 | { label: 'setObject', type: 'method', boost: -0 } 39 | ], 40 | validFor: /^[\w$\xa1-\uffff][\w$\d\xa1-\uffff]*$/ 41 | } ); 42 | } ); 43 | } ); 44 | -------------------------------------------------------------------------------- /i18n/br.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Adriendelucca", 5 | "Huñvreüs" 6 | ] 7 | }, 8 | "codemirror-desc": "Pourchas a ra livañ ereadurezh en aozer wikitestenn", 9 | "codemirror-toggle-label": "Livañ ereadurezh", 10 | "codemirror-toggle-label-short": "Ereadurezh", 11 | "codemirror-prefs-enable": "Gweredekaat livañ ereadurezh", 12 | "codemirror-prefs-title": "Dibaboù livañ ereadurezh", 13 | "codemirror-prefs-help": "skoazell", 14 | "codemirror-prefs-codefolding": "Gweredekaat plegañ ar c'hod", 15 | "codemirror-prefs-colorblind": "Gweredekaat livioù dereat d’ar re na wel ket an holl anezho evit livañ an ereadurezh en ur aozañ wikitestenn", 16 | "codemirror-prefs-colorblind-help": "Marteze ne yelo ket en-dro an ereadur-mañ ma rit gant ur bitrak livañ ereadurezh.", 17 | "codemirror-close": "Serriñ", 18 | "codemirror-find": "Klask", 19 | "codemirror-all": "An holl", 20 | "codemirror-all-tooltip": "Diuzañ an holl glotadurioù", 21 | "codemirror-match-case": "Diforc'hañ Pennlizherennoù/Lizherennoù bihan", 22 | "codemirror-regexp": "Jedad reoliek", 23 | "codemirror-regexp-invalid": "Jedad reoliek direizh", 24 | "codemirror-by-word": "Ger a-bezh", 25 | "codemirror-replace": "Erlec'hiañ", 26 | "codemirror-replace-placeholder": "Erlec'hiañ", 27 | "codemirror-replace-all": "Erlec'hiañ pep tra", 28 | "codemirror-done": "Echu", 29 | "codemirror-find-results": "$1 diwar $2", 30 | "codemirror-goto-line": "Mont d'al linenn", 31 | "codemirror-goto-line-go": "Mont", 32 | "prefs-accessibility": "Diraezañ", 33 | "prefs-syntax-highlighting": "Livañ ereadurezh", 34 | "codemirror-beta-feature-title": "Livañ ereadurezh gwellaet", 35 | "codemirror-beta-feature-description": "Grit abred gant arc’hweladurioù livañ ereadurezh nevez evel plegañ ar c’hod hag klokaat emgefre" 36 | } 37 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const nodeResolve = require( '@rollup/plugin-node-resolve' ); 4 | const alias = require( '@rollup/plugin-alias' ); 5 | 6 | module.exports = [ 7 | // ext.CodeMirror.v6.lib 8 | { 9 | input: 'resources/codemirror.bundle.lib.js', 10 | output: { 11 | file: 'resources/lib/codemirror6.bundle.lib.js', 12 | format: 'cjs' 13 | }, 14 | plugins: [ 15 | nodeResolve() 16 | ] 17 | }, 18 | 19 | // ext.CodeMirror.v6.modes 20 | { 21 | input: 'resources/modes/codemirror.bundle.modes.js', 22 | output: { 23 | file: 'resources/lib/codemirror6.bundle.modes.js', 24 | format: 'cjs' 25 | }, 26 | plugins: [ 27 | alias( { 28 | entries: [ 29 | { find: '@codemirror/autocomplete', replacement: 'ext.CodeMirror.v6.lib' }, 30 | { find: '@codemirror/commands', replacement: 'ext.CodeMirror.v6.lib' }, 31 | { find: '@codemirror/language', replacement: 'ext.CodeMirror.v6.lib' }, 32 | { find: '@codemirror/lint', replacement: 'ext.CodeMirror.v6.lib' }, 33 | { find: '@codemirror/search', replacement: 'ext.CodeMirror.v6.lib' }, 34 | { find: '@codemirror/state', replacement: 'ext.CodeMirror.v6.lib' }, 35 | { find: '@codemirror/view', replacement: 'ext.CodeMirror.v6.lib' }, 36 | { find: '@lezer/highlight', replacement: 'ext.CodeMirror.v6.lib' } 37 | ] 38 | } ), 39 | nodeResolve( { 40 | resolveOnly: [ 41 | '@codemirror/lang-html', 42 | '@codemirror/lang-javascript', 43 | '@codemirror/lang-css', 44 | '@codemirror/lang-json', 45 | '@codemirror/lang-vue', 46 | '@codemirror/legacy-modes', 47 | '@lezer/html', 48 | '@lezer/javascript', 49 | '@lezer/css', 50 | '@lezer/json', 51 | '@lezer/common', 52 | '@lezer/lr' 53 | ] 54 | } ) 55 | ] 56 | } 57 | ]; 58 | -------------------------------------------------------------------------------- /tests/selenium/specs/highlighting-wikitext2010.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const assert = require( 'assert' ), 4 | EditPage = require( '../pageobjects/edit.page' ), 5 | FixtureContent = require( '../fixturecontent' ), 6 | UserPreferences = require( '../userpreferences' ), 7 | Api = require( 'wdio-mediawiki/Api.js' ), 8 | Util = require( 'wdio-mediawiki/Util' ); 9 | 10 | describe( 'CodeMirror bracket match highlighting for the wikitext 2010 editor', () => { 11 | let title; 12 | 13 | before( async () => { 14 | title = Util.getTestString( 'CodeMirror-fixture1-' ); 15 | await UserPreferences.loginAsOther(); 16 | await FixtureContent.createFixturePage( title ); 17 | await UserPreferences.enableWikitext2010EditorWithCodeMirror(); 18 | } ); 19 | 20 | beforeEach( async () => { 21 | await EditPage.openForEditing( title ); 22 | await EditPage.wikiEditorToolbar.waitForDisplayed(); 23 | await EditPage.clickText(); 24 | } ); 25 | 26 | it( 'highlights matching bracket', async () => { 27 | await browser.execute( () => { 28 | $( '.cm-editor' ).textSelection( 'setSelection', { start: 0, end: 0 } ); 29 | } ); 30 | assert( await EditPage.highlightedBracket.waitForDisplayed() ); 31 | assert.strictEqual( await EditPage.getHighlightedMatchingBrackets(), '[]' ); 32 | } ); 33 | 34 | it( 'matches according to cursor movement', async () => { 35 | await browser.execute( () => { 36 | $( '.cm-editor' ).textSelection( 'setSelection', { start: 3, end: 3 } ); 37 | } ); 38 | assert( await EditPage.highlightedBracket.waitForDisplayed() ); 39 | assert.strictEqual( await EditPage.getHighlightedMatchingBrackets(), '{}' ); 40 | } ); 41 | 42 | after( async () => { 43 | const bot = await Api.bot(); 44 | bot.delete( title, 'Test cleanup' ).catch( ( e ) => console.error( e ) ); 45 | } ); 46 | } ); 47 | -------------------------------------------------------------------------------- /i18n/skr-arab.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Saraiki" 5 | ] 6 | }, 7 | "codemirror-toggle-label-short": "نحو", 8 | "codemirror-prefs-help": "مدد", 9 | "codemirror-prefs-panel-advanced": "ودھائے", 10 | "codemirror-prefs-section-lines": "سطراں", 11 | "codemirror-prefs-section-other": "ٻیا", 12 | "codemirror-close": "بند کرو", 13 | "codemirror-next": "اڳلا لبھو", 14 | "codemirror-previous": "پچھلا لبھو", 15 | "codemirror-all": "یکے", 16 | "codemirror-regexp": "باقاعدہ اظہار", 17 | "codemirror-replace": "بدل گھنو", 18 | "codemirror-replace-all": "سارے بدل گھنو", 19 | "codemirror-done": "تھی ڳیا", 20 | "codemirror-find-results": "$2 وچوں $1 واں", 21 | "codemirror-goto-line-go": "ڄلو", 22 | "codemirror-keymap-help-title": "کی بورڈ شارٹ کٹ", 23 | "codemirror-keymap-help-close": "بند کرو", 24 | "codemirror-keymap-bold": "موٹا", 25 | "codemirror-keymap-italic": "ترچھا", 26 | "codemirror-keymap-link": "لنک", 27 | "codemirror-keymap-computercode": "کمپیوٹر کوڈ", 28 | "codemirror-keymap-superscript": "اتے لکھت", 29 | "codemirror-keymap-underline": "ہیٹھاں لکیر", 30 | "codemirror-keymap-history": "تاریخ", 31 | "codemirror-keymap-undo": "واپس", 32 | "codemirror-keymap-redo": "ولدا کرو", 33 | "codemirror-keymap-indent": "حاشیہ ودھاؤ", 34 | "codemirror-keymap-heading-n": "سرخی $1", 35 | "codemirror-keymap-search": "ڳولݨ", 36 | "codemirror-keymap-find": "لبھو تے تبدیل کرو", 37 | "codemirror-keymap-insert": "درج کرو", 38 | "codemirror-keymap-reference": "حوالہ", 39 | "codemirror-keymap-comment": "تبصرہ", 40 | "codemirror-keymap-other": "ٻیا", 41 | "codemirror-keymap-advanced-preferences": "وِدھیک ترجیحات", 42 | "codemirror-keymap-help": "کی بورڈ شارٹ کٹ", 43 | "prefs-accessibility": "رسائیت", 44 | "codemirror-wikilint-close": "بند کرو", 45 | "codemirror-wikilint-escape": "ایسکیپ", 46 | "codemirror-wikilint-open": "کھولو" 47 | } 48 | -------------------------------------------------------------------------------- /resources/modes/codemirror.mode.exporter.js: -------------------------------------------------------------------------------- 1 | module.exports = {}; 2 | 3 | /** 4 | * @module ext.CodeMirror.v6.modes 5 | * @description 6 | * This module provides syntax highlighting for JavaScript, JSON, CSS, Lua, and Vue in CodeMirror. 7 | * Each mode is exposed as a method that returns a {@link LanguageSupport}-compatible instance 8 | * that can be used with the {@link CodeMirror} constructor. 9 | * 10 | * For MediaWiki wikitext syntax highlighting, use 11 | * {@link module:ext.CodeMirror.v6.mode.mediawiki ext.CodeMirror.v6.mode.mediawiki}. 12 | * @example 13 | * const require = await mw.loader.using( [ 'ext.CodeMirror.v6', 'ext.CodeMirror.v6.modes' ] ); 14 | * const CodeMirror = require( 'ext.CodeMirror.v6' ); 15 | * const { javascript, css } = require( 'ext.CodeMirror.v6.modes' ); 16 | * const cmJs = new CodeMirror( myJsTextarea, javascript() ); 17 | * cmJs.initialize(); 18 | * const cmCss = new CodeMirror( myCssTextarea, css() ); 19 | * cmCss.initialize(); 20 | */ 21 | 22 | /* eslint-disable jsdoc/no-undefined-types */ 23 | /** 24 | * @method javascript 25 | * @return {CodeMirrorJavaScript|LanguageSupport} LanguageSupport for the JavaScript mode. 26 | */ 27 | /** 28 | * @method json 29 | * @return {CodeMirrorJson|LanguageSupport} LanguageSupport for the JSON mode. 30 | */ 31 | /** 32 | * @method css 33 | * @return {CodeMirrorCss|LanguageSupport} LanguageSupport for the CSS mode. 34 | */ 35 | /** 36 | * @method lua 37 | * @return {CodeMirrorLua|LanguageSupport} LanguageSupport for the Lua mode. 38 | */ 39 | /** 40 | * @method vue 41 | * @return {CodeMirrorVue|LanguageSupport} LanguageSupport for the Vue mode. 42 | */ 43 | /* eslint-enable jsdoc/no-undefined-types */ 44 | 45 | for ( const mode of [ 'javascript', 'json', 'css', 'lua', 'vue' ] ) { 46 | module.exports[ mode ] = function () { 47 | // eslint-disable-next-line security/detect-non-literal-require 48 | const ModeClass = require( `./codemirror.${ mode }.js` ); 49 | return new ModeClass( mode ); 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /resources/lib/codemirror/README.md: -------------------------------------------------------------------------------- 1 | # CodeMirror 2 | 3 | [![Build Status](https://github.com/codemirror/codemirror5/workflows/main/badge.svg)](https://github.com/codemirror/codemirror5/actions) 4 | 5 | CodeMirror is a versatile text editor implemented in JavaScript for 6 | the browser. It is specialized for editing code, and comes with over 7 | 100 language modes and various addons that implement more advanced 8 | editing functionality. Every language comes with fully-featured code 9 | and syntax highlighting to help with reading and editing complex code. 10 | 11 | A rich programming API and a CSS theming system are available for 12 | customizing CodeMirror to fit your application, and extending it with 13 | new functionality. 14 | 15 | You can find more information (and the 16 | [manual](https://codemirror.net/5/doc/manual.html)) on the [project 17 | page](https://codemirror.net/5/). For questions and discussion, use the 18 | [discussion forum](https://discuss.codemirror.net/). 19 | 20 | See 21 | [CONTRIBUTING.md](https://github.com/codemirror/CodeMirror/blob/master/CONTRIBUTING.md) 22 | for contributing guidelines. 23 | 24 | The CodeMirror community aims to be welcoming to everybody. We use the 25 | [Contributor Covenant 26 | (1.1)](http://contributor-covenant.org/version/1/1/0/) as our code of 27 | conduct. 28 | 29 | ### Installation 30 | 31 | Either get the [zip file](https://codemirror.net/5/codemirror.zip) with 32 | the latest version, or make sure you have [Node](https://nodejs.org/) 33 | installed and run: 34 | 35 | npm install codemirror@5 36 | 37 | **NOTE**: This is the source repository for the library, and not the 38 | distribution channel. Cloning it is not the recommended way to install 39 | the library, and will in fact not work unless you also run the build 40 | step. 41 | 42 | ### Quickstart 43 | 44 | To build the project, make sure you have Node.js installed (at least version 6) 45 | and then `npm install`. To run, just open `index.html` in your 46 | browser (you don't need to run a webserver). Run the tests with `npm test`. 47 | -------------------------------------------------------------------------------- /resources/ve-cm/ve.ui.CodeMirrorAction.v6.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * VisualEditor UserInterface CodeMirrorAction class. 3 | */ 4 | 5 | require( './ve.ui.CodeMirrorTool.v6.js' ); 6 | 7 | /** 8 | * CodeMirror action 9 | * 10 | * @class 11 | * @extends ve.ui.Action 12 | * @constructor 13 | * @param {ve.ui.Surface} surface Surface to act on 14 | */ 15 | ve.ui.CodeMirrorAction = function VeUiCodeMirrorAction() { 16 | // Parent constructor 17 | ve.ui.CodeMirrorAction.super.apply( this, arguments ); 18 | }; 19 | 20 | /* Inheritance */ 21 | 22 | OO.inheritClass( ve.ui.CodeMirrorAction, ve.ui.Action ); 23 | 24 | /* Static Properties */ 25 | 26 | ve.ui.CodeMirrorAction.static.name = 'codeMirror'; 27 | 28 | /** 29 | * @inheritdoc 30 | */ 31 | ve.ui.CodeMirrorAction.static.methods = [ 'toggle' ]; 32 | 33 | /* Methods */ 34 | 35 | /** 36 | * @method 37 | * @param {boolean} [enable] State to force toggle to, inverts current state if undefined 38 | * @return {Promise} Action was executed 39 | */ 40 | ve.ui.CodeMirrorAction.prototype.toggle = async function ( enable ) { 41 | if ( !this.surface.mirror && ( enable || enable === undefined ) ) { 42 | await mw.loader.using( [ 'ext.CodeMirror.v6.mode.mediawiki', 'jquery.client' ] ); 43 | if ( this.surface.mirror ) { 44 | mw.log( '[CodeMirror] VE mirror already initialized by another action.' ); 45 | return; 46 | } 47 | const CodeMirrorVisualEditor = require( '../codemirror.visualEditor.js' ); 48 | CodeMirrorVisualEditor.setCodeMirrorPreference( true ); 49 | const { mediawiki } = require( 'ext.CodeMirror.v6.mode.mediawiki' ); 50 | this.surface.mirror = new CodeMirrorVisualEditor( 51 | this.surface, 52 | mediawiki( { 53 | bidiIsolation: false, 54 | codeFolding: false, 55 | autocomplete: false, 56 | openLinks: false 57 | } ) 58 | ); 59 | this.surface.mirror.initialize(); 60 | } else if ( this.surface.mirror ) { 61 | this.surface.mirror.toggle( enable ); 62 | this.surface.mirror.constructor.setCodeMirrorPreference( this.surface.mirror.isActive ); 63 | } 64 | }; 65 | 66 | /* Registration */ 67 | 68 | ve.ui.actionFactory.register( ve.ui.CodeMirrorAction ); 69 | -------------------------------------------------------------------------------- /i18n/kck.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "McDutchie", 5 | "MthulisiNcube" 6 | ] 7 | }, 8 | "codemirror-find": "Wana", 9 | "codemirror-next": "kunotebela", 10 | "codemirror-previous": "hule", 11 | "codemirror-all": "kose", 12 | "codemirror-match-case": "kunofanana khesi", 13 | "codemirror-regexp": "Rigesipi", 14 | "codemirror-by-word": "nde bala", 15 | "codemirror-replace": "Mmisa", 16 | "codemirror-replace-placeholder": "Mmisa", 17 | "codemirror-replace-all": "mmisa kose", 18 | "codemirror-control-character": "Lawula bunhu $1", 19 | "codemirror-special-char-null": "Bheli bala", 20 | "codemirror-special-char-bell": "Bheli bala", 21 | "codemirror-special-char-backspace": "Bhekispesi", 22 | "codemirror-special-char-newline": "Layini itshwa", 23 | "codemirror-special-char-vertical-tab": "thebhu imile", 24 | "codemirror-special-char-carriage-return": "Kheriyegi retheni", 25 | "codemirror-special-char-escape": "tjuluka bala", 26 | "codemirror-special-char-nbsp": "Sipeyisi sisingapalalanyiwe", 27 | "codemirror-special-char-zero-width-space": "Zero - bupabi sipeyisi", 28 | "codemirror-special-char-zero-width-non-joiner": "Zero - bupabi kusingahanganye", 29 | "codemirror-special-char-zero-width-joiner": "Zero - bupabi kusingahanganye", 30 | "codemirror-special-char-left-to-right-mark": "Lumehho - kuyenda - kuludli nyola", 31 | "codemirror-special-char-right-to-left-mark": "Ludli - kuyenda - kulumehho nyola", 32 | "codemirror-special-char-line-separator": "Kunopalalanya layini", 33 | "codemirror-special-char-left-to-right-override": "Lumehho - kuyenda - kuludli kuhhaligwa", 34 | "codemirror-special-char-right-to-left-override": "Ludli - kuyenda - kulumehho ukodusa kuyapo", 35 | "codemirror-special-char-narrow-nbsp": "Kutehlanana sipesi tjisazohhalilika", 36 | "codemirror-special-char-left-to-right-isolate": "Lumehho - kuyenda - kuludli ayisolethe", 37 | "codemirror-special-char-right-to-left-isolate": "Ludli - kuyenda - Kulumehho ayisolethi", 38 | "codemirror-special-char-pop-directional-isolate": "Phophu kwayinolakidza ayisolethi", 39 | "codemirror-special-char-paragraph-separator": "Kunopaladza pharagrafu", 40 | "codemirror-special-char-zero-width-no-break-space": "Kunohanganya Bala" 41 | } 42 | -------------------------------------------------------------------------------- /resources/codemirror.keymap.less: -------------------------------------------------------------------------------- 1 | @import 'mediawiki.skin.variables.less'; 2 | 3 | /*! 4 | * Styles for keyboard shortcuts dialog in CodeMirror. 5 | * 6 | * Some CSS adapted from VisualEditor's ve.ui.CommandHelpDialog.less 7 | * Courtesy of VisualEditor team (MIT license) 8 | */ 9 | 10 | .cdx-dialog.cm-mw-keymap-dialog { 11 | // The width is intentionally increased for desktop screens compared to Codex defaults. 12 | max-width: @min-width-breakpoint-desktop; 13 | 14 | .cm-mw-keymap-section { 15 | /* 16 | * HACK: Prevent splitting over columns. This should be done with 17 | * column-break-inside but it's not well supported yet. 18 | */ 19 | display: inline-block; 20 | width: 100%; 21 | 22 | h4 { 23 | text-align: center; 24 | margin: 0; 25 | padding: 0; 26 | } 27 | } 28 | 29 | .cm-mw-keymap-list { 30 | margin: 0.5em 0 1.5em 0; 31 | 32 | dd { 33 | display: inline-block; 34 | margin: 0; 35 | vertical-align: top; 36 | width: calc( 45% - 1em ); 37 | } 38 | 39 | dt { 40 | display: inline-block; 41 | padding-right: 1em; 42 | text-align: right; 43 | vertical-align: top; 44 | width: 55%; 45 | 46 | > kbd { 47 | display: block; 48 | 49 | /* Enlarge vertical spacing in a list of shortcuts for one action */ 50 | 51 | ~ kbd { 52 | margin-top: 0.5em; 53 | } 54 | } 55 | } 56 | 57 | dd, 58 | dt { 59 | line-height: @line-height-xx-small; 60 | margin: 0.5em 0; 61 | } 62 | } 63 | 64 | .cm-mw-keymap-key { 65 | border: 0; 66 | font-style: normal; 67 | font-weight: bold; 68 | 69 | > kbd { /* stylelint-disable-line no-descending-specificity */ 70 | background-color: @background-color-neutral-subtle; 71 | color: @color-base; 72 | border: @border-width-base @border-style-base @border-color-subtle; 73 | border-radius: @border-radius-base; 74 | box-shadow: @box-shadow-drop-small, @box-shadow-inset-medium @box-shadow-color-inverted; 75 | display: inline-block; 76 | font-family: @font-family-monospace--fallback; 77 | line-height: @line-height-xx-small; 78 | padding: 0.1em 0.4em; 79 | margin: -0.1em 0.3em 0; 80 | text-shadow: 0 1px 0 @color-inverted; 81 | text-transform: uppercase; 82 | text-align: center; 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /i18n/bn.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Aftabuzzaman", 5 | "আজিজ", 6 | "আফতাবুজ্জামান" 7 | ] 8 | }, 9 | "codemirror-desc": "উইকিপাঠ্য সম্পাদকে সিনট্যাক্স আলোকপাতকরণ প্রদান করে", 10 | "codemirror-toggle-label": "সিনট্যাক্স আলোকপাতকরণ", 11 | "codemirror-toggle-label-short": "সিনট্যাক্স", 12 | "codemirror-prefs-panel-advanced": "উচ্চতর", 13 | "codemirror-prefs-section-lines": "লাইন", 14 | "codemirror-prefs-section-characters": "অক্ষর", 15 | "codemirror-prefs-section-code-assistance": "কোড সহায়তা", 16 | "codemirror-prefs-section-references": "তথ্যসূত্র", 17 | "codemirror-prefs-section-other": "অন্যান্য", 18 | "codemirror-prefs-reset": "পূর্বনির্ধারিত অবস্থায় যান", 19 | "codemirror-prefs-colorblind": "উইকিপাঠ্য সম্পাদনার সময় সিনট্যাক্স আলোকপাতকরণের জন্য বর্ণান্ধ-বন্ধুত্বপূর্ণ স্কিম সক্রিয় করুন", 20 | "codemirror-prefs-colorblind-help": "আপনি যদি সিনট্যাক্স আলোকপাতের জন্য কোনও গ্যাজেট ব্যবহার করেন, তবে এই পছন্দটি কাজ করবে না।", 21 | "codemirror-find": "খুঁজুন", 22 | "codemirror-next": "পরবর্তী খুঁজুন", 23 | "codemirror-previous": "পূর্ববর্তী খুঁজুন", 24 | "codemirror-all": "সব", 25 | "codemirror-regexp": "রেগুলার এক্সপ্রেশন", 26 | "codemirror-regexp-invalid": "অবৈধ রেগুলার এক্সপ্রেশন", 27 | "codemirror-by-word": "সম্পূর্ণ শব্দ", 28 | "codemirror-replace": "প্রতিস্থাপন করুন", 29 | "codemirror-replace-placeholder": "প্রতিস্থাপন করুন", 30 | "codemirror-replace-all": "সব প্রতিস্থাপন করুন", 31 | "codemirror-done": "সম্পন্ন", 32 | "codemirror-goto-line": "এই লাইনে যান", 33 | "codemirror-goto-line-go": "চলো", 34 | "codemirror-control-character": "নিয়ন্ত্রণ অক্ষর $1", 35 | "codemirror-special-char-backspace": "ব্যাকস্পেস", 36 | "codemirror-special-char-newline": "নতুন লাইন", 37 | "codemirror-special-char-vertical-tab": "উল্লম্ব ট্যাব", 38 | "codemirror-special-char-zero-width-space": "শূন্য-প্রস্থ স্থান", 39 | "codemirror-special-char-zero-width-joiner": "শূন্য-প্রস্থের সংযুক্তকারী", 40 | "codemirror-special-char-right-to-left-mark": "ডান-থেকে-বাম চিহ্ন", 41 | "codemirror-special-char-line-separator": "লাইন বিভাজক", 42 | "codemirror-special-char-object-replacement": "বস্তু প্রতিস্থাপন অক্ষর", 43 | "codemirror-keymap-advanced-preferences": "উচ্চতর পছন্দ", 44 | "codemirror-keymap-lint": "লিন্টার", 45 | "prefs-accessibility": "অভিগম্যতা" 46 | } 47 | -------------------------------------------------------------------------------- /i18n/be-tarask.json: -------------------------------------------------------------------------------- 1 | { 2 | "@metadata": { 3 | "authors": [ 4 | "Red Winged Duck", 5 | "Renessaince" 6 | ] 7 | }, 8 | "codemirror-desc": "Забясьпечвае вылучэньне сынтаксысу ў рэдактары вікітэксту", 9 | "codemirror-toggle-label": "Вылучэньне сынтаксысу", 10 | "codemirror-toggle-label-short": "Сынтакс", 11 | "codemirror-prefs-enable": "Уключыць падсьветку сынтаксу", 12 | "codemirror-prefs-title": "Налады падсьветкі сынтаксу", 13 | "codemirror-prefs-help": "даведка", 14 | "codemirror-prefs-panel-advanced": "пашыраныя", 15 | "codemirror-prefs-section-lines": "Радкі", 16 | "codemirror-prefs-section-characters": "Сымбалі", 17 | "codemirror-prefs-section-code-assistance": "Дапамога ў кодзе", 18 | "codemirror-prefs-section-other": "Іншае", 19 | "codemirror-prefs-codefolding": "Уключыць згортваньне коду", 20 | "codemirror-prefs-autocomplete": "Уключыць аўтазапаўненьне", 21 | "codemirror-prefs-bracketmatching": "Уключыць супастаўленьне дужак", 22 | "codemirror-prefs-closebrackets": "Уключыць аўтазакрыцьцё дужак", 23 | "codemirror-prefs-linenumbering": "Паказваць нумары радкоў", 24 | "codemirror-prefs-linewrapping": "Пераносіць радкі", 25 | "codemirror-prefs-lint": "Правяраць памылкі коду", 26 | "codemirror-prefs-activeline": "Падсьвечваць актыўны радок", 27 | "codemirror-prefs-specialchars": "Паказваць спэцыяльныя сымбалі", 28 | "codemirror-prefs-whitespace": "Падсьвечваць пропусты", 29 | "codemirror-prefs-colorblind": "Уключыць прыдатную для дальтонікаў схему падсьвятленьня сынтаксу пры рэдагаваньні вікітэксту", 30 | "codemirror-prefs-colorblind-help": "Калі вы карыстаецеся інструмэнтам падсьветкі сынтаксу, гэтая налада не працавацьме.", 31 | "codemirror-keymap-foldref": "Згарнуць усе цэтлікі ", 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 | --------------------------------------------------------------------------------