Add editor options

(1) Add UI controls for `keyMap`, `tabSize`, `indentWithTabs`, and
`lineWrapping`; `indentUnit` tracks `tabSize`.
(2) Dispatch `change` events from `loadPrefs` to initialize CM options
from the controls' event listener.
(3) Move stock options from the `CM.fromTextArea` call into `CM.defaults`.
Add `CM.setOption` method, analogous to the instance method, which updates
`CM.defaults` and sets the option in all instances; add `CM.getOption`
which simply returns `CM.defaults[option]`.
(4) Move the new editor functions into `CM.commands` and replace the
functions with commands.
This commit is contained in:
hideheader 2015-03-08 01:21:43 -05:00
parent d9ceb98f56
commit 284d4b8ec8
4 changed files with 151 additions and 33 deletions

View File

@ -72,6 +72,26 @@
"message": "Checking...",
"description": "Text to display when checking a style for an update"
},
"cm_indentWithTabs": {
"message": "Use tabs with smart indentation",
"description": "Label for the checkbox controlling tabs with smart indentation option for the style editor."
},
"cm_keyMap": {
"message": "Keymap",
"description": "Label for the drop-down list controlling the keymap for the style editor."
},
"cm_lineWrapping": {
"message": "Word wrap",
"description": "Label for the checkbox controlling word wrap option for the style editor."
},
"cm_smartIndent": {
"message": "Use smart indentation",
"description": "Label for the checkbox controlling smart indentation option for the style editor."
},
"cm_tabSize": {
"message": "Tab size",
"description": "Label for the text box controlling tab size option for the style editor."
},
"dbError": {
"message": "An error has occurred using the Stylish database. Would you like to visit a web page with possible solutions?",
"description": "Prompt when a DB error is encountered"
@ -169,10 +189,6 @@
"message": "Show number of styles active for the current site on the toolbar button",
"description": "Label for the checkbox controlling toolbar badge text."
},
"prefSmartIndent": {
"message": "Use smart indentation",
"description": "Label for the checkbox controlling smart indentation option for the style editor."
},
"sectionAdd": {
"message": "Add another section",
"description": "Label for the button to add a section"

View File

@ -173,6 +173,16 @@
}
}
/* editor options */
[type="number"] {
max-width: 2.8em;
text-align: right;
}
table, input, select {
font-size: inherit;
}
table td:first-child {min-width: 60px}
</style>
<script src="storage.js"></script>
<script src="messaging.js"></script>
@ -191,7 +201,34 @@
<a href="manage.html"><button id="cancel-button"></button></a>
<div id="options">
<h2 id="options-heading"></h2>
<input id="smart-indent" type="checkbox"><label id="smart-indent-label" for="smart-indent"></label>
<table cols="2">
<tr>
<td colspan="2">
<input data-option="lineWrapping" id="editor.lineWrapping" type="checkbox">
<label id="lineWrapping-label" for="editor.lineWrapping"></label>
</td>
</tr>
<tr>
<td colspan="2">
<input data-option="smartIndent" id="editor.smartIndent" type="checkbox">
<label id="smartIndent-label" for="editor.smartIndent"></label>
</td>
</tr>
<tr>
<td colspan="2">
<input data-option="indentWithTabs" id="editor.indentWithTabs" type="checkbox">
<label id="indentWithTabs-label" for="editor.indentWithTabs"></label>
</td>
</tr>
<tr>
<td><label id="tabSize-label" for="editor.tabSize"></label></td>
<td><input data-option="tabSize" id="editor.tabSize" type="number" min="0"></td>
</tr>
<tr>
<td><label id="keyMap-label" for="editor.keyMap"></label></td>
<td><select data-option="keyMap" id="editor.keyMap"></select></td>
</tr>
</table>
</div>
</div>
<section id="sections">

111
edit.js
View File

@ -12,11 +12,13 @@ appliesToEverythingTemplate.innerHTML = t("appliesToEverything") + ' <button cla
var sectionTemplate = document.createElement("div");
sectionTemplate.innerHTML = '<label>' + t('sectionCode') + '</label><textarea class="code"></textarea><br><div class="applies-to"><label>' + t("appliesLabel") + ' <img class="applies-to-help" src="help.png" alt="' + t('helpAlt') + '"></label><ul class="applies-to-list"></ul></div><button class="remove-section">' + t('sectionRemove') + '</button><button class="add-section">' + t('sectionAdd') + '</button>';
var editors = []; // array of all CodeMirror instances
function initCodeMirror() {
var CM = CodeMirror;
var editors = [] // array of all CodeMirror instances
// replace given textarea with the CodeMirror editor
function setupCodeMirror(textarea) {
var cm = CodeMirror.fromTextArea(textarea, {
// default option values
var userOptions = prefs.getPref("editor.options");
var stylishOptions = {
mode: 'css',
lineNumbers: true,
lineWrapping: true,
@ -24,26 +26,70 @@ function setupCodeMirror(textarea) {
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "CodeMirror-lint-markers"],
matchBrackets: true,
lint: CodeMirror.lint.css,
smartIndent: prefs.getPref("smart-indent"),
keyMap: "sublime",
extraKeys: {"Ctrl-Space": "autocomplete"}
};
mergeOptions(stylishOptions, CM.defaults);
mergeOptions(userOptions, CM.defaults);
function mergeOptions(source, target) {
for (var key in source) target[key] = source[key];
return target;
}
// additional commands
var cc = CM.commands;
cc.jumpToLine = jumpToLine;
cc.nextBuffer = nextBuffer;
cc.prevBuffer = prevBuffer;
// cc.save = save;
// user option values
CM.getOption = function (o) {
return CodeMirror.defaults[o];
}
CM.setOption = function (o, v) {
CodeMirror.defaults[o] = v;
editors.forEach(function(editor) {
editor.setOption(o, v);
});
}
// initialize global editor controls
document.getElementById("options").addEventListener("change", acmeEventListener, false);
var keymapControl = document.getElementById("editor.keyMap");
Object.keys(CodeMirror.keyMap).sort().forEach(function(map) {
keymapControl.appendChild(document.createElement("option")).textContent = map;
});
var controlPrefs = {},
controlOptions = ["smartIndent", "indentWithTabs", "tabSize", "keyMap", "lineWrapping"];
controlOptions.forEach(function(option) {
controlPrefs["editor." + option] = CM.defaults[option];
tE(option + "-label", "cm_" + option);
});
loadPrefs(controlPrefs);
}
initCodeMirror();
function acmeEventListener(event) {
var option = event.target.dataset.option;
console.log("acmeEventListener heard %s on %s", event.type, event.target.id);
if (!option) console.error("acmeEventListener: no 'cm_option' %O", event.target);
else CodeMirror.setOption(option, event.target[isCheckbox(event.target) ? "checked" : "value"]);
if ("tabSize" === option) CodeMirror.setOption("indentUnit", CodeMirror.getOption("tabSize"));
}
// replace given textarea with the CodeMirror editor
function setupCodeMirror(textarea) {
var cm = CodeMirror.fromTextArea(textarea);
cm.addKeyMap({
"Ctrl-G": function(cm) {
var cur = cm.getCursor();
cm.openDialog(t('editGotoLine') + ': <input type="text" style="width: 5em"/>', function(str) {
var m = str.match(/^\s*(\d+)(?:\s*:\s*(\d+))?\s*$/);
if (m) {
cm.setCursor(m[1] - 1, m[2] ? m[2] - 1 : cur.ch);
}
}, {value: cur.line+1});
},
"Alt-PageDown": function(cm) {
editors[(editors.indexOf(cm) + 1) % editors.length].focus();
},
"Alt-PageUp": function(cm) {
editors[(editors.indexOf(cm) - 1 + editors.length) % editors.length].focus();
}
"Ctrl-G": "jumpToLine",
"Alt-PageDown": "nextBuffer",
"Alt-PageUp": "prevBuffer"
});
cm.lastChange = cm.changeGeneration();
cm.on("change", indicateCodeChange);
@ -325,11 +371,27 @@ function setupGlobalSearch() {
CodeMirror.commands.findPrev = function(cm) { findNext(cm, true) }
}
function jumpToLine(cm) {
var cur = cm.getCursor();
cm.openDialog(t('editGotoLine') + ': <input type="text" style="width: 5em"/>', function(str) {
var m = str.match(/^\s*(\d+)(?:\s*:\s*(\d+))?\s*$/);
if (m) {
cm.setCursor(m[1] - 1, m[2] ? m[2] - 1 : cur.ch);
}
}, {value: cur.line+1});
}
function nextBuffer(cm) {
editors[(editors.indexOf(cm) + 1) % editors.length].focus();
}
function prevBuffer(cm) {
editors[(editors.indexOf(cm) - 1 + editors.length) % editors.length].focus();
}
window.addEventListener("load", init, false);
function init() {
tE("sections-help", "helpAlt", "alt");
loadPrefs({"smart-indent": true});
var params = getParams();
if (!params.id) { // match should be 2 - one for the whole thing, one for the parentheses
// This is an add
@ -554,10 +616,8 @@ chrome.extension.onMessage.addListener(function(request, sender, sendResponse) {
}
break;
case "prefChanged":
if (request.prefName == "smart-indent") {
editors.forEach(function(editor) {
editor.setOption("smartIndent", request.value);
});
if (request.prefName == "editor.smartIndent") {
CodeMirror.setOption("smartIndent", request.value);
}
}
});
@ -569,7 +629,6 @@ tE("save-button", "styleSaveLabel");
tE("cancel-button", "styleCancelEditLabel");
tE("sections-heading", "styleSectionsTitle");
tE("options-heading", "optionsHeading");
tE("smart-indent-label", "prefSmartIndent");
document.getElementById("name").addEventListener("change", makeDirty, false);
document.getElementById("enabled").addEventListener("change", makeDirty, false);

View File

@ -162,14 +162,20 @@ var prefs = {
// defaults
"openEditInWindow": false, // new editor opens in a own browser window
"show-badge": true, // display text on popup menu icon
"smart-indent": true, // CodeMirror smart indent
"popup.breadcrumbs": true, // display "New style" links as URL breadcrumbs
"popup.breadcrumbs.usePath": false, // use URL path for "this URL"
"popup.enabledFirst": true, // display enabled styles before disabled styles
"manage.onlyEnabled": false, // display only enabled styles
"manage.onlyEdited": false,// display only styles created locally
"manage.onlyEdited": false, // display only styles created locally
"editor.options": null, // CodeMirror.defaults.*
"editor.lineWrapping": true, // word wrap
"editor.smartIndent": true, // "smart" indent
"editor.indentWithTabs": false,// smart indent with tabs
"editor.tabSize": 4, // tab width, in spaces
"editor.keyMap": "sublime", // keymap
NO_DEFAULT_PREFERENCE: "No default preference for '%s'",
UNHANDLED_DATA_TYPE: "Default '%s' is of type '%s' - what should be done with it?",