add a hotkey & right-click to beautify silently (#972)
* add a hotkey & right-click to beautify silently * fix closestVisible
This commit is contained in:
parent
e1ed3bf222
commit
60a37af0e0
|
@ -1273,6 +1273,10 @@
|
|||
"message": "Beautify",
|
||||
"description": "Label for the CSS-beautifier button on the edit style page"
|
||||
},
|
||||
"styleBeautifyHint": {
|
||||
"message": "Hint: right-click the “Beautify” button or use the keyboard shortcut defined below to beautify without showing this panel",
|
||||
"description": "Hint shown inside the CSS-beautifier panel"
|
||||
},
|
||||
"styleBeautifyIndentConditional": {
|
||||
"message": "Indent @media, @supports",
|
||||
"description": "CSS-beautifier option"
|
||||
|
|
109
edit/beautify.js
109
edit/beautify.js
|
@ -1,8 +1,49 @@
|
|||
/* global loadScript css_beautify showHelp prefs t $ $create */
|
||||
/* exported beautify */
|
||||
/* global editor createHotkeyInput moveFocus CodeMirror */
|
||||
/* exported initBeautifyButton */
|
||||
'use strict';
|
||||
|
||||
function beautify(scope) {
|
||||
const HOTKEY_ID = 'editor.beautify.hotkey';
|
||||
|
||||
prefs.initializing.then(() => {
|
||||
CodeMirror.defaults.extraKeys[prefs.get(HOTKEY_ID) || ''] = 'beautify';
|
||||
CodeMirror.commands.beautify = cm => {
|
||||
// using per-section mode when code editor or applies-to block is focused
|
||||
const isPerSection = cm.display.wrapper.parentElement.contains(document.activeElement);
|
||||
beautify(isPerSection ? [cm] : editor.getEditors(), false);
|
||||
};
|
||||
});
|
||||
|
||||
prefs.subscribe([HOTKEY_ID], (key, value) => {
|
||||
const {extraKeys} = CodeMirror.defaults;
|
||||
for (const [key, cmd] of Object.entries(extraKeys)) {
|
||||
if (cmd === 'beautify') {
|
||||
delete extraKeys[key];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (value) {
|
||||
extraKeys[value] = 'beautify';
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {HTMLElement} btn - the button element shown in the UI
|
||||
* @param {function():CodeMirror[]} getScope
|
||||
*/
|
||||
function initBeautifyButton(btn, getScope) {
|
||||
btn.addEventListener('click', () => beautify(getScope()));
|
||||
btn.addEventListener('contextmenu', e => {
|
||||
e.preventDefault();
|
||||
beautify(getScope(), false);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {CodeMirror[]} scope
|
||||
* @param {?boolean} ui
|
||||
*/
|
||||
function beautify(scope, ui = true) {
|
||||
loadScript('/vendor-overwrites/beautify/beautify-css-mod.js')
|
||||
.then(() => {
|
||||
if (!window.css_beautify && window.exports) {
|
||||
|
@ -19,7 +60,41 @@ function beautify(scope) {
|
|||
}
|
||||
options.indent_size = tabs ? 1 : prefs.get('editor.tabSize');
|
||||
options.indent_char = tabs ? '\t' : ' ';
|
||||
if (ui) {
|
||||
createBeautifyUI(scope, options);
|
||||
}
|
||||
for (const cm of scope) {
|
||||
setTimeout(doBeautifyEditor, 0, cm, options);
|
||||
}
|
||||
}
|
||||
|
||||
function doBeautifyEditor(cm, options) {
|
||||
const pos = options.translate_positions =
|
||||
[].concat.apply([], cm.doc.sel.ranges.map(r =>
|
||||
[Object.assign({}, r.anchor), Object.assign({}, r.head)]));
|
||||
const text = cm.getValue();
|
||||
const newText = css_beautify(text, options);
|
||||
if (newText !== text) {
|
||||
if (!cm.beautifyChange || !cm.beautifyChange[cm.changeGeneration()]) {
|
||||
// clear the list if last change wasn't a css-beautify
|
||||
cm.beautifyChange = {};
|
||||
}
|
||||
cm.setValue(newText);
|
||||
const selections = [];
|
||||
for (let i = 0; i < pos.length; i += 2) {
|
||||
selections.push({anchor: pos[i], head: pos[i + 1]});
|
||||
}
|
||||
const {scrollX, scrollY} = window;
|
||||
cm.setSelections(selections);
|
||||
window.scrollTo(scrollX, scrollY);
|
||||
cm.beautifyChange[cm.changeGeneration()] = true;
|
||||
if (ui) {
|
||||
$('#help-popup button[role="close"]').disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createBeautifyUI(scope, options) {
|
||||
showHelp(t('styleBeautify'),
|
||||
$create([
|
||||
$create('.beautify-options', [
|
||||
|
@ -32,6 +107,10 @@ function beautify(scope) {
|
|||
$createLabeledCheckbox('preserve_newlines', 'styleBeautifyPreserveNewlines'),
|
||||
$createLabeledCheckbox('indent_conditional', 'styleBeautifyIndentConditional'),
|
||||
]),
|
||||
$create('p.beautify-hint', [
|
||||
$create('span', t('styleBeautifyHint') + '\u00A0'),
|
||||
createHotkeyInput(HOTKEY_ID, () => moveFocus($('#help-popup'), 1)),
|
||||
]),
|
||||
$create('.buttons', [
|
||||
$create('button', {
|
||||
attributes: {role: 'close'},
|
||||
|
@ -60,32 +139,6 @@ function beautify(scope) {
|
|||
|
||||
$('#help-popup').className = 'wide';
|
||||
|
||||
scope.forEach(cm => {
|
||||
setTimeout(() => {
|
||||
const pos = options.translate_positions =
|
||||
[].concat.apply([], cm.doc.sel.ranges.map(r =>
|
||||
[Object.assign({}, r.anchor), Object.assign({}, r.head)]));
|
||||
const text = cm.getValue();
|
||||
const newText = css_beautify(text, options);
|
||||
if (newText !== text) {
|
||||
if (!cm.beautifyChange || !cm.beautifyChange[cm.changeGeneration()]) {
|
||||
// clear the list if last change wasn't a css-beautify
|
||||
cm.beautifyChange = {};
|
||||
}
|
||||
cm.setValue(newText);
|
||||
const selections = [];
|
||||
for (let i = 0; i < pos.length; i += 2) {
|
||||
selections.push({anchor: pos[i], head: pos[i + 1]});
|
||||
}
|
||||
const {scrollX, scrollY} = window;
|
||||
cm.setSelections(selections);
|
||||
window.scrollTo(scrollX, scrollY);
|
||||
cm.beautifyChange[cm.changeGeneration()] = true;
|
||||
$('#help-popup button[role="close"]').disabled = false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('.beautify-options').onchange = ({target}) => {
|
||||
const value = target.type === 'checkbox' ? target.checked : target.selectedIndex > 0;
|
||||
prefs.set('editor.beautify', Object.assign(options, {[target.dataset.option]: value}));
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* global CodeMirror showHelp cmFactory onDOMready $ $create prefs t */
|
||||
/* global CodeMirror showHelp cmFactory onDOMready $ prefs t createHotkeyInput */
|
||||
'use strict';
|
||||
|
||||
(() => {
|
||||
|
@ -62,46 +62,8 @@
|
|||
|
||||
function configureColorpicker(event) {
|
||||
event.preventDefault();
|
||||
const input = $create('input', {
|
||||
type: 'search',
|
||||
spellcheck: false,
|
||||
value: prefs.get('editor.colorpicker.hotkey'),
|
||||
onkeydown(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
const key = CodeMirror.keyName(event);
|
||||
switch (key) {
|
||||
case 'Enter':
|
||||
if (this.checkValidity()) {
|
||||
$('#help-popup .dismiss').onclick();
|
||||
}
|
||||
return;
|
||||
case 'Esc':
|
||||
$('#help-popup .dismiss').onclick();
|
||||
return;
|
||||
default:
|
||||
// disallow: [Shift?] characters, modifiers-only, [modifiers?] + Esc, Tab, nav keys
|
||||
if (!key || new RegExp('^(' + [
|
||||
'(Back)?Space',
|
||||
'(Shift-)?.', // a single character
|
||||
'(Shift-?|Ctrl-?|Alt-?|Cmd-?){0,2}(|Esc|Tab|(Page)?(Up|Down)|Left|Right|Home|End|Insert|Delete)',
|
||||
].join('|') + ')$', 'i').test(key)) {
|
||||
this.value = key || this.value;
|
||||
this.setCustomValidity('Not allowed');
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.value = key;
|
||||
this.setCustomValidity('');
|
||||
prefs.set('editor.colorpicker.hotkey', key);
|
||||
},
|
||||
oninput() {
|
||||
// fired on pressing "x" to clear the field
|
||||
prefs.set('editor.colorpicker.hotkey', '');
|
||||
},
|
||||
onpaste(event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
const input = createHotkeyInput('editor.colorpicker.hotkey', () => {
|
||||
$('#help-popup .dismiss').onclick();
|
||||
});
|
||||
const popup = showHelp(t('helpKeyMapHotkey'), input);
|
||||
if (this instanceof Element) {
|
||||
|
|
|
@ -779,6 +779,11 @@ body:not(.find-open) [data-match-highlight-count="1"] .CodeMirror-selection-high
|
|||
padding-left: 4px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
.beautify-hint {
|
||||
width: 0;
|
||||
min-width: 100%;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
/************ single editor **************/
|
||||
.usercss body {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* global CodeMirror onDOMready prefs setupLivePrefs $ $$ $create t tHTML
|
||||
createSourceEditor queryTabs sessionStorageHash getOwnTab FIREFOX API tryCatch
|
||||
closeCurrentTab messageBox debounce workerUtil
|
||||
beautify ignoreChromeError
|
||||
initBeautifyButton ignoreChromeError
|
||||
moveFocus msg createSectionsEditor rerouteHotkeys CODEMIRROR_THEMES */
|
||||
/* exported showCodeMirrorPopup editorWorker toggleContextMenuDelete */
|
||||
'use strict';
|
||||
|
@ -170,10 +170,8 @@ preinit();
|
|||
$('#heading').textContent = t(style.id ? 'editStyleHeading' : 'addStyleTitle');
|
||||
$('#name').placeholder = t(usercss ? 'usercssEditorNamePlaceholder' : 'styleMissingName');
|
||||
$('#name').title = usercss ? t('usercssReplaceTemplateName') : '';
|
||||
|
||||
$('#preview-label').classList.toggle('hidden', !style.id);
|
||||
|
||||
$('#beautify').onclick = () => beautify(editor.getEditors());
|
||||
initBeautifyButton($('#beautify'), () => editor.getEditors());
|
||||
window.addEventListener('resize', () => {
|
||||
debounce(rememberWindowSize, 100);
|
||||
detectLayout();
|
||||
|
|
|
@ -12,6 +12,7 @@ const rerouteHotkeys = (() => {
|
|||
'toggleEditorFocus',
|
||||
'find', 'findNext', 'findPrev', 'replace', 'replaceAll',
|
||||
'colorpicker',
|
||||
'beautify',
|
||||
]);
|
||||
|
||||
return rerouteHotkeys;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* global template cmFactory $ propertyToCss CssToProperty linter regExpTester
|
||||
FIREFOX toggleContextMenuDelete beautify showHelp t tryRegExp */
|
||||
FIREFOX toggleContextMenuDelete initBeautifyButton showHelp t tryRegExp */
|
||||
/* exported createSection */
|
||||
'use strict';
|
||||
|
||||
|
@ -94,6 +94,7 @@ function createSection({
|
|||
const cm = cmFactory.create(wrapper => {
|
||||
el.insertBefore(wrapper, $('.code-label', el).nextSibling);
|
||||
}, {value: originalSection.code});
|
||||
el.CodeMirror = cm; // used by getAssociatedEditor
|
||||
|
||||
const changeListeners = new Set();
|
||||
|
||||
|
@ -196,12 +197,12 @@ function createSection({
|
|||
$('.clone-section', el).addEventListener('click', () => insertSectionAfter(getModel(), section));
|
||||
$('.move-section-up', el).addEventListener('click', () => moveSectionUp(section));
|
||||
$('.move-section-down', el).addEventListener('click', () => moveSectionDown(section));
|
||||
$('.beautify-section', el).addEventListener('click', () => beautify([cm]));
|
||||
$('.restore-section', el).addEventListener('click', () => restoreSection(section));
|
||||
$('.test-regexp', el).addEventListener('click', () => {
|
||||
regExpTester.toggle();
|
||||
updateRegexpTester();
|
||||
});
|
||||
initBeautifyButton($('.beautify-section', el), () => [cm]);
|
||||
}
|
||||
|
||||
function handleKeydown(cm, event) {
|
||||
|
|
|
@ -154,9 +154,7 @@ function createSectionsEditor({style, onTitleChanged}) {
|
|||
function closestVisible(nearbyElement) {
|
||||
const cm =
|
||||
nearbyElement instanceof CodeMirror ? nearbyElement :
|
||||
nearbyElement instanceof Node &&
|
||||
(nearbyElement.closest('#sections > .section') || {}).CodeMirror ||
|
||||
getLastActivatedEditor();
|
||||
nearbyElement instanceof Node && getAssociatedEditor(nearbyElement) || getLastActivatedEditor();
|
||||
if (nearbyElement instanceof Node && cm) {
|
||||
const {left, top} = nearbyElement.getBoundingClientRect();
|
||||
const bounds = cm.display.wrapper.getBoundingClientRect();
|
||||
|
@ -228,6 +226,15 @@ function createSectionsEditor({style, onTitleChanged}) {
|
|||
}
|
||||
}
|
||||
|
||||
function getAssociatedEditor(nearbyElement) {
|
||||
for (let el = nearbyElement; el; el = el.parentElement) {
|
||||
// added by createSection
|
||||
if (el.CodeMirror) {
|
||||
return el.CodeMirror;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getEditors() {
|
||||
return sections.filter(s => !s.isRemoved()).map(s => s.cm);
|
||||
}
|
||||
|
|
52
edit/util.js
52
edit/util.js
|
@ -1,4 +1,5 @@
|
|||
/* exported dirtyReporter memoize clipString sectionsToMozFormat */
|
||||
/* global CodeMirror $create prefs */
|
||||
/* exported dirtyReporter memoize clipString sectionsToMozFormat createHotkeyInput */
|
||||
'use strict';
|
||||
|
||||
function dirtyReporter() {
|
||||
|
@ -135,3 +136,52 @@ function memoize(fn) {
|
|||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {!string} prefId
|
||||
* @param {?function(isEnter:boolean)} onDone
|
||||
*/
|
||||
function createHotkeyInput(prefId, onDone = () => {}) {
|
||||
return $create('input', {
|
||||
type: 'search',
|
||||
spellcheck: false,
|
||||
value: prefs.get(prefId),
|
||||
onkeydown(event) {
|
||||
const key = CodeMirror.keyName(event);
|
||||
if (key === 'Tab' || key === 'Shift-Tab') {
|
||||
return;
|
||||
}
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
switch (key) {
|
||||
case 'Enter':
|
||||
if (this.checkValidity()) onDone(true);
|
||||
return;
|
||||
case 'Esc':
|
||||
onDone(false);
|
||||
return;
|
||||
default:
|
||||
// disallow: [Shift?] characters, modifiers-only, [modifiers?] + Esc, Tab, nav keys
|
||||
if (!key || new RegExp('^(' + [
|
||||
'(Back)?Space',
|
||||
'(Shift-)?.', // a single character
|
||||
'(Shift-?|Ctrl-?|Alt-?|Cmd-?){0,2}(|Esc|Tab|(Page)?(Up|Down)|Left|Right|Home|End|Insert|Delete)',
|
||||
].join('|') + ')$', 'i').test(key)) {
|
||||
this.value = key || this.value;
|
||||
this.setCustomValidity('Not allowed');
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.value = key;
|
||||
this.setCustomValidity('');
|
||||
prefs.set(prefId, key);
|
||||
},
|
||||
oninput() {
|
||||
// fired on pressing "x" to clear the field
|
||||
prefs.set(prefId, '');
|
||||
},
|
||||
onpaste(event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -59,6 +59,7 @@ self.prefs = self.INJECTED === 1 ? self.prefs : (() => {
|
|||
end_with_newline: false,
|
||||
indent_conditional: true,
|
||||
},
|
||||
'editor.beautify.hotkey': '',
|
||||
'editor.lintDelay': 300, // lint gutter marker update delay, ms
|
||||
'editor.linter': 'csslint', // 'csslint' or 'stylelint' or ''
|
||||
'editor.lintReportDelay': 500, // lint report update delay, ms
|
||||
|
|
Loading…
Reference in New Issue
Block a user