stylus/edit/codemirror-default.js
tophf 657798d219 improve bookmarking + rework codemirror-factory.js
* pull editing-only stuff from codemirror-default.js
* switch throttledSetOption to IntersectionObserver
2020-11-24 13:16:51 +03:00

153 lines
5.1 KiB
JavaScript

/* global
$
CodeMirror
prefs
t
*/
'use strict';
(function () {
// CodeMirror miserably fails on keyMap='' so let's ensure it's not
if (!prefs.get('editor.keyMap')) {
prefs.reset('editor.keyMap');
}
const defaults = {
autoCloseBrackets: prefs.get('editor.autoCloseBrackets'),
mode: 'css',
lineNumbers: true,
lineWrapping: prefs.get('editor.lineWrapping'),
foldGutter: true,
gutters: [
'CodeMirror-linenumbers',
'CodeMirror-foldgutter',
...(prefs.get('editor.linter') ? ['CodeMirror-lint-markers'] : []),
],
matchBrackets: true,
hintOptions: {},
lintReportDelay: prefs.get('editor.lintReportDelay'),
styleActiveLine: true,
theme: prefs.get('editor.theme'),
keyMap: prefs.get('editor.keyMap'),
extraKeys: Object.assign(CodeMirror.defaults.extraKeys || {}, {
// independent of current keyMap; some are implemented only for the edit page
'Alt-Enter': 'toggleStyle',
'Alt-PageDown': 'nextEditor',
'Alt-PageUp': 'prevEditor',
'Ctrl-Pause': 'toggleEditorFocus',
}),
maxHighlightLength: 100e3,
};
Object.assign(CodeMirror.defaults, defaults, prefs.get('editor.options'));
// Adding hotkeys to some keymaps except 'basic' which is primitive by design
const KM = CodeMirror.keyMap;
const extras = Object.values(CodeMirror.defaults.extraKeys);
if (!extras.includes('jumpToLine')) {
KM.sublime['Ctrl-G'] = 'jumpToLine';
KM.emacsy['Ctrl-G'] = 'jumpToLine';
KM.pcDefault['Ctrl-J'] = 'jumpToLine';
KM.macDefault['Cmd-J'] = 'jumpToLine';
}
if (!extras.includes('autocomplete')) {
// will be used by 'sublime' on PC via fallthrough
KM.pcDefault['Ctrl-Space'] = 'autocomplete';
// OSX uses Ctrl-Space and Cmd-Space for something else
KM.macDefault['Alt-Space'] = 'autocomplete';
// copied from 'emacs' keymap
KM.emacsy['Alt-/'] = 'autocomplete';
// 'vim' and 'emacs' define their own autocomplete hotkeys
}
if (!extras.includes('blockComment')) {
KM.sublime['Shift-Ctrl-/'] = 'commentSelection';
}
if (navigator.appVersion.includes('Windows')) {
// 'pcDefault' keymap on Windows should have F3/Shift-F3/Ctrl-R
if (!extras.includes('findNext')) KM.pcDefault['F3'] = 'findNext';
if (!extras.includes('findPrev')) KM.pcDefault['Shift-F3'] = 'findPrev';
if (!extras.includes('replace')) KM.pcDefault['Ctrl-R'] = 'replace';
// try to remap non-interceptable (Shift-)Ctrl-N/T/W hotkeys
// Note: modifier order in CodeMirror is S-C-A
for (const char of ['N', 'T', 'W']) {
for (const remap of [
{from: 'Ctrl-', to: ['Alt-', 'Ctrl-Alt-']},
{from: 'Shift-Ctrl-', to: ['Ctrl-Alt-', 'Shift-Ctrl-Alt-']},
]) {
const oldKey = remap.from + char;
for (const km of Object.values(KM)) {
const command = km[oldKey];
if (!command) continue;
for (const newMod of remap.to) {
const newKey = newMod + char;
if (newKey in km) continue;
km[newKey] = command;
delete km[oldKey];
break;
}
}
}
}
}
Object.assign(CodeMirror.mimeModes['text/css'].propertyKeywords, {
'content-visibility': true,
'overflow-anchor': true,
'overscroll-behavior': true,
});
Object.assign(CodeMirror.mimeModes['text/css'].colorKeywords, {
'darkgrey': true,
'darkslategrey': true,
'dimgrey': true,
'lightgrey': true,
'lightslategrey': true,
'slategrey': true,
});
Object.assign(CodeMirror.prototype, {
/**
* @param {'less' | 'stylus' | ?} [pp] - any value besides `less` or `stylus` sets `css` mode
* @param {boolean} [force]
*/
setPreprocessor(pp, force) {
const name = pp === 'less' ? 'text/x-less' : pp === 'stylus' ? pp : 'css';
const m = this.doc.mode;
if (force || (m.helperType ? m.helperType !== pp : m.name !== name)) {
this.setOption('mode', name);
}
},
/** Superfast GC-friendly check that runs until the first non-space line */
isBlank() {
let filled;
this.eachLine(({text}) => (filled = text && /\S/.test(text)));
return !filled;
},
/**
* Sets cursor and centers it in view if `pos` was out of view
* @param {CodeMirror.Pos} pos
*/
jumpToPos(pos) {
const coords = this.cursorCoords(pos, 'page');
const b = this.display.wrapper.getBoundingClientRect();
if (coords.top < b.top + this.defaultTextHeight() * 2 ||
coords.bottom > b.bottom - 100) {
this.scrollIntoView(pos, b.height / 2);
}
this.setCursor(pos, null, {scroll: false});
},
});
Object.assign(CodeMirror.commands, {
jumpToLine(cm) {
const cur = cm.getCursor();
const oldDialog = $('.CodeMirror-dialog', cm.display.wrapper);
if (oldDialog) cm.focus(); // close the currently opened minidialog
cm.openDialog(t.template.jumpToLine.cloneNode(true), str => {
const [line, ch] = str.match(/^\s*(\d+)(?:\s*:\s*(\d+))?\s*$|$/);
if (line) cm.setCursor(line - 1, ch ? ch - 1 : cur.ch);
}, {value: cur.line + 1});
},
});
})();