diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index a5b7c4fe..584bb716 100644
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -91,6 +91,10 @@
"message": "Checking...",
"description": "Text to display when checking a style for an update"
},
+ "cm_autocompleteOnTyping": {
+ "message": "Autocomplete on typing",
+ "description": "Label for the checkbox in the style editor."
+ },
"cm_indentWithTabs": {
"message": "Use tabs with smart indentation",
"description": "Label for the checkbox controlling tabs with smart indentation option for the style editor."
diff --git a/edit.html b/edit.html
index e3d9ae4c..c8ae2927 100644
--- a/edit.html
+++ b/edit.html
@@ -704,6 +704,10 @@
+
+
+
+
diff --git a/edit.js b/edit.js
index 3f939c58..f100b177 100644
--- a/edit.js
+++ b/edit.js
@@ -161,6 +161,7 @@ function initCodeMirror() {
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter", "CodeMirror-lint-markers"],
matchBrackets: true,
highlightSelectionMatches: {showToken: /[#.\-\w]/, annotateScrollbar: true},
+ hintOptions: {},
lint: {getAnnotations: CodeMirror.lint.css, delay: prefs.get("editor.lintDelay")},
lintReportDelay: prefs.get("editor.lintReportDelay"),
styleActiveLine: true,
@@ -319,6 +320,13 @@ function acmeEventListener(event) {
}, 100);
})();
return;
+ case 'autocompleteOnTyping':
+ editors.forEach(cm => {
+ const onOff = el.checked ? 'on' : 'off';
+ cm[onOff]('change', autocompleteOnTyping);
+ cm[onOff]('pick', autocompletePicked);
+ });
+ return;
case "matchHighlight":
switch (value) {
case 'token':
@@ -338,6 +346,10 @@ function setupCodeMirror(textarea, index) {
var cm = CodeMirror.fromTextArea(textarea, {lint: null});
cm.on("change", indicateCodeChange);
+ if (prefs.get('editor.autocompleteOnTyping')) {
+ cm.on('change', autocompleteOnTyping);
+ cm.on('pick', autocompletePicked);
+ }
cm.on("blur", function(cm) {
editors.lastActive = cm;
hotkeyRerouter.setState(true);
@@ -874,6 +886,34 @@ function toggleStyle() {
save();
}
+function autocompleteOnTyping(cm, info, debounced) {
+ if (cm.state.completionActive
+ || info.origin && !info.origin.includes('input')
+ || !info.text.last) {
+ return;
+ }
+ if (cm.state.autocompletePicked) {
+ cm.state.autocompletePicked = false;
+ return;
+ }
+ if (!debounced) {
+ debounce(autocompleteOnTyping, 100, cm, info, true);
+ return;
+ }
+ if (info.text.last.match(/[-\w!]+$/)) {
+ cm.state.autocompletePicked = false;
+ cm.options.hintOptions.completeSingle = false;
+ cm.execCommand('autocomplete');
+ setTimeout(() => {
+ cm.options.hintOptions.completeSingle = true;
+ });
+ }
+}
+
+function autocompletePicked(cm) {
+ cm.state.autocompletePicked = true;
+}
+
function refocusMinidialog(cm) {
var section = cm.getSection();
if (!section.querySelector(".CodeMirror-dialog")) {
diff --git a/prefs.js b/prefs.js
index 00b74663..a84cb52d 100644
--- a/prefs.js
+++ b/prefs.js
@@ -44,6 +44,7 @@ var prefs = new function Prefs() {
'editor.matchHighlight': 'token', // token = token/word under cursor even if nothing is selected
// selection = only when something is selected
// '' (empty string) = disabled
+ 'editor.autocompleteOnTyping': false, // show autocomplete dropdown on typing a word token
'editor.contextDelete': contextDeleteMissing(), // "Delete" item in context menu
'badgeDisabled': '#8B0000', // badge background color when disabled