@bhsd/codemirror-mediawiki
Version:
Modified CodeMirror mode based on wikimedia/mediawiki-extensions-CodeMirror
345 lines (344 loc) • 12.4 kB
JavaScript
import { keymap, highlightSpecialChars, highlightActiveLine, highlightWhitespace, highlightTrailingWhitespace, scrollPastEnd, drawSelection, rectangularSelection, crosshairCursor, } from '@codemirror/view';
import { EditorState } from '@codemirror/state';
import { highlightSelectionMatches } from '@codemirror/search';
import { closeBrackets, autocompletion, acceptCompletion, completionKeymap, startCompletion, } from '@codemirror/autocomplete';
import { json } from '@codemirror/lang-json';
import { autoCloseTags } from '@codemirror/lang-html';
import { getLSP } from '@bhsd/browser';
import { colorPicker as cssColorPicker, colorPickerTheme, makeColorPicker } from '@bhsd/codemirror-css-color-picker';
import colorPicker, { discoverColors } from './color';
import { mediawiki } from './mediawiki';
import escape from './escape';
import codeFolding, { mediaWikiFold, foldHandler } from './fold';
import tagMatchingState from './matchTag';
import refHover from './ref';
import magicWordHover from './hover';
import signatureHelp from './signature';
import inlayHints from './inlay';
import { getWikiLintSource, getJsLintSource, getCssLintSource, getJsonLintSource, getLuaLintSource, getVueLintSource, getHTMLLintSource, } from './lintsource';
import openLinks from './openLinks';
import { tagModes, getStaticMwConfig } from './static';
import bidiIsolation from './bidi';
import toolKeymap from './keymap';
import bracketMatching from './matchBrackets';
import statusBar from './statusBar';
import { detectIndent } from './indent';
import javascript from './javascript';
import css from './css';
import lua from './lua';
import vue from './vue';
import html from './html';
import { CodeMirror6, avail, languages, linterRegistry, destroyListeners, plain, optionalFunctions, themes, } from './codemirror';
export { CodeMirror6 };
/**
* 注册通用扩展
* @param name 扩展名
* @param ext 扩展
*/
const registerExtension = (name, ext) => {
avail[name] ??= [];
const addon = avail[name];
addon[0] = ext;
};
/** Register the `highlightSpecialChars` extension */
export const registerHighlightSpecialChars = () => {
registerExtension('highlightSpecialChars', highlightSpecialChars);
};
/** Register the `highlightActiveLine` extension */
export const registerHighlightActiveLine = () => {
registerExtension('highlightActiveLine', highlightActiveLine);
};
/** Register the `highlightWhitespace` extension */
export const registerHighlightWhitespace = () => {
registerExtension('highlightWhitespace', highlightWhitespace);
};
/** Register the `highlightTrailingWhitespace` extension */
export const registerHighlightTrailingWhitespace = () => {
registerExtension('highlightTrailingWhitespace', highlightTrailingWhitespace);
};
/** Register the `highlightSelectionMatches` extension */
export const registerHighlightSelectionMatches = () => {
registerExtension('highlightSelectionMatches', highlightSelectionMatches);
};
/** Register the `bracketMatching` extension */
export const registerBracketMatching = () => {
registerExtension('bracketMatching', ([config, e = []] = []) => [
bracketMatching(config),
e,
]);
};
/** Register the `closeBrackets` extension */
export const registerCloseBrackets = () => {
registerExtension('closeBrackets', (e = []) => [closeBrackets(), e]);
};
/** Register the `scrollPastEnd` extension */
export const registerScrollPastEnd = () => {
registerExtension('scrollPastEnd', scrollPastEnd);
};
/** Register the `allowMultipleSelections` extension */
export const registerAllowMultipleSelections = () => {
registerExtension('allowMultipleSelections', () => [
EditorState.allowMultipleSelections.of(true),
drawSelection(),
rectangularSelection(),
crosshairCursor(),
]);
};
/** Register the `autocompletion` extension */
export const registerAutocompletion = () => {
registerExtension('autocompletion', () => [
autocompletion({ defaultKeymap: false }),
keymap.of([
...completionKeymap.filter(({ run }) => run !== startCompletion),
{ key: 'Shift-Enter', run: startCompletion },
{ key: 'Tab', run: acceptCompletion },
]),
]);
};
/** Register the `codeFolding` extension */
export const registerCodeFolding = () => {
registerExtension('codeFolding', codeFolding);
};
/** Register the `colorPicker` extension */
export const registerColorPicker = () => {
registerExtension('colorPicker', colorPicker);
};
/** 注册所有通用扩展(除`colorPicker`) */
const registerExtensions = () => {
registerHighlightSpecialChars();
registerHighlightActiveLine();
registerHighlightWhitespace();
registerHighlightTrailingWhitespace();
registerHighlightSelectionMatches();
registerBracketMatching();
registerCloseBrackets();
registerScrollPastEnd();
registerAllowMultipleSelections();
registerAutocompletion();
registerCodeFolding();
};
/** Register all common extensions */
export const registerCommonExtensions = () => {
registerExtensions();
registerColorPicker();
};
function mediawikiOnly(ext) {
return typeof ext === 'function'
? [(enable, cm) => enable ? ext(cm) : [], { mediawiki: true }]
: [(e = []) => e, { mediawiki: ext }];
}
/**
* 注册特定语言的扩展
* @param lang 语言
* @param name 扩展名
* @param ext 扩展
*/
const registerLangExtension = (lang, name, ext) => {
avail[name] ??= [() => []];
const addon = avail[name];
addon[1] ??= {};
addon[1][lang] = ext;
};
/** Register MediaWiki language support */
export const registerMediaWiki = () => {
registerCommonExtensions();
registerMediaWikiCore();
registerOpenLinks();
registerEscape();
registerRefHover();
registerHover();
registerSignatureHelp();
registerInlayHints();
registerColorPickerForMediaWiki();
registerBracketMatchingForMediaWiki();
registerCodeFoldingForMediaWiki();
};
/**
* 注册MediaWiki专用扩展
* @param name 扩展名
* @param ext 扩展
*/
const registerExtensionForMediaWiki = (name, ext) => {
avail[name] ??= mediawikiOnly(ext);
};
/** Register the `openLinks` extension */
export const registerOpenLinks = () => {
registerExtensionForMediaWiki('openLinks', openLinks);
};
/** Register the `escape` extension */
export const registerEscape = () => {
registerExtensionForMediaWiki('escape', escape);
};
/** Register the `refHover` extension */
export const registerRefHover = () => {
registerExtensionForMediaWiki('refHover', refHover);
};
/** Register the `hover` extension */
export const registerHover = () => {
registerExtensionForMediaWiki('hover', magicWordHover);
};
/** Register the `signatureHelp` extension */
export const registerSignatureHelp = () => {
registerExtensionForMediaWiki('signatureHelp', signatureHelp);
};
/** Register the `inlayHints` extension */
export const registerInlayHints = () => {
registerExtensionForMediaWiki('inlayHints', inlayHints);
};
/** Register the `colorPicker` extension for MediaWiki */
export const registerColorPickerForMediaWiki = () => {
registerLangExtension('mediawiki', 'colorPicker', [
[makeColorPicker({ discoverColors }), colorPickerTheme],
{ marginLeft: '.6ch' },
]);
};
/** Register the `bracketMatching` extension for MediaWiki */
export const registerBracketMatchingForMediaWiki = () => {
registerLangExtension('mediawiki', 'bracketMatching', [
{ brackets: '()[]{}()【】[]{}' },
tagMatchingState,
]);
};
/** Register the `codeFolding` extension for MediaWiki */
export const registerCodeFoldingForMediaWiki = () => {
registerLangExtension('mediawiki', 'codeFolding', mediaWikiFold);
optionalFunctions.foldHandler = foldHandler;
};
/**
* 注册LintSource
* @param lang 语言
* @param lintSource
*/
const registerLintSource = (lang, lintSource) => {
linterRegistry[lang] = lintSource;
optionalFunctions.statusBar = statusBar;
};
/** Register MediaWiki core language support */
export const registerMediaWikiCore = () => {
CodeMirror6.getMwConfig = (config) => getStaticMwConfig(config, tagModes);
languages['mediawiki'] = (config) => [
mediawiki(config),
plain(),
bidiIsolation,
toolKeymap,
];
registerLintSource('mediawiki', getWikiLintSource);
destroyListeners.push(view => getLSP(view)?.destroy());
};
/** Register mixed MediaWiki-HTML language support */
export const registerHTML = () => {
registerCommonExtensions();
registerHTMLCore();
registerCloseBracketsForHTML();
registerColorPickerForHTML();
};
/** Register the `closeBrackets` extension for mixed MediaWiki-HTML */
export const registerCloseBracketsForHTML = () => {
registerLangExtension('html', 'closeBrackets', autoCloseTags);
};
/** Register the `colorPicker` extension for mixed MediaWiki-HTML */
export const registerColorPickerForHTML = () => {
registerLangExtension('html', 'colorPicker', [cssColorPicker]);
};
/** Register mixed MediaWiki-HTML core language support */
export const registerHTMLCore = () => {
languages['html'] = html;
registerLintSource('html', getHTMLLintSource);
optionalFunctions.detectIndent = detectIndent;
};
/** Register JavaScript language support */
export const registerJavaScript = () => {
registerExtensions();
registerJavaScriptCore();
};
/** Register JavaScript core language support */
export const registerJavaScriptCore = () => {
languages['javascript'] = javascript;
registerLintSource('javascript', getJsLintSource);
optionalFunctions.detectIndent = detectIndent;
};
/** Register CSS language support */
export const registerCSS = () => {
registerCommonExtensions();
registerCSSCore();
registerColorPickerForCSS();
};
/** Register the `colorPicker` extension for CSS */
export const registerColorPickerForCSS = () => {
registerLangExtension('css', 'colorPicker', [cssColorPicker]);
};
/** Register CSS core language support */
export const registerCSSCore = () => {
languages['css'] = css;
registerLintSource('css', getCssLintSource);
optionalFunctions.detectIndent = detectIndent;
};
/** Register JSON language support */
export const registerJSON = () => {
registerExtensions();
registerJSONCore();
};
/** Register JSON core language support */
export const registerJSONCore = () => {
languages['json'] = json;
registerLintSource('json', getJsonLintSource);
optionalFunctions.detectIndent = detectIndent;
};
/** Register Lua language support */
export const registerLua = () => {
registerExtensions();
registerLuaCore();
};
/** Register Lua core language support */
export const registerLuaCore = () => {
languages['lua'] = lua;
registerLintSource('lua', getLuaLintSource);
optionalFunctions.detectIndent = detectIndent;
};
/** Register Vue language support */
export const registerVue = () => {
registerCommonExtensions();
registerVueCore();
registerCloseBracketsForVue();
registerColorPickerForVue();
};
/** Register the `closeBrackets` extension for Vue */
export const registerCloseBracketsForVue = () => {
registerLangExtension('vue', 'closeBrackets', autoCloseTags);
};
/** Register the `colorPicker` extension for Vue */
export const registerColorPickerForVue = () => {
registerLangExtension('vue', 'colorPicker', [cssColorPicker]);
};
/** Register Vue core language support */
export const registerVueCore = () => {
languages['vue'] = vue;
registerLintSource('vue', getVueLintSource);
optionalFunctions.detectIndent = detectIndent;
};
/**
* Register a custom language support
* @param name language name
* @param lang language support
* @param lintSource optional linter
*/
export const registerLanguage = (name, lang, lintSource) => {
registerCommonExtensions();
registerLanguageCore(name, lang, lintSource);
};
/**
* Register a custom language support without common extensions
* @param name language name
* @param lang language support
* @param lintSource optional linter
*/
export const registerLanguageCore = (name, lang, lintSource) => {
languages[name] = lang;
if (lintSource) {
registerLintSource(name, lintSource);
}
};
export const registerTheme = (name, theme) => {
themes[name] = theme;
};
export { nord } from './theme';