Skip to content

Commit 204aafa

Browse files
authoredMay 11, 2023
feat: Custom empty message when no completion found (#5158)
Empty message for no completion suggestions
1 parent 2bb7fa0 commit 204aafa

File tree

5 files changed

+54
-11
lines changed

5 files changed

+54
-11
lines changed
 

‎ace.d.ts

+3
Original file line numberDiff line numberDiff line change
@@ -1054,8 +1054,11 @@ export namespace Ace {
10541054
constructor();
10551055
autoInsert?: boolean;
10561056
autoSelect?: boolean;
1057+
autoShown?: boolean;
10571058
exactMatch?: boolean;
10581059
inlineEnabled?: boolean;
1060+
parentNode?: HTMLElement;
1061+
emptyMessage?(prefix: String): String;
10591062
getPopup(): AcePopup;
10601063
showPopup(editor: Editor, options: CompletionOptions): void;
10611064
detach(): void;

‎src/autocomplete.js

+23-4
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,12 @@ class Autocomplete {
5555
constructor() {
5656
this.autoInsert = false;
5757
this.autoSelect = true;
58+
this.autoShown = false;
5859
this.exactMatch = false;
5960
this.inlineEnabled = false;
6061
this.keyboardHandler = new HashHandler();
6162
this.keyboardHandler.bindKeys(this.commands);
63+
this.parentNode = null;
6264

6365
this.blurListener = this.blurListener.bind(this);
6466
this.changeListener = this.changeListener.bind(this);
@@ -73,7 +75,7 @@ class Autocomplete {
7375
}
7476

7577
$init() {
76-
this.popup = new AcePopup(document.body || document.documentElement);
78+
this.popup = new AcePopup(this.parentNode || document.body || document.documentElement);
7779
this.popup.on("click", function(e) {
7880
this.insertMatch();
7981
e.stop();
@@ -255,6 +257,8 @@ class Autocomplete {
255257
data = this.popup.getData(this.popup.getRow());
256258
if (!data)
257259
return false;
260+
if (data.value === "") // Explicitly given nothing to insert, e.g. "No suggestion state"
261+
return this.detach();
258262
var completions = this.completions;
259263
var result = this.getCompletionProvider().insertMatch(this.editor, data, completions.filterText, options);
260264
// detach only if new popup was not opened while inserting match
@@ -341,15 +345,28 @@ class Autocomplete {
341345

342346
if (finished) {
343347
// No results
344-
if (!filtered.length)
348+
if (!filtered.length) {
349+
var emptyMessage = !this.autoShown && this.emptyMessage;
350+
if ( typeof emptyMessage == "function")
351+
emptyMessage = this.emptyMessage(prefix);
352+
if (emptyMessage) {
353+
var completionsForEmpty = [{
354+
caption: this.emptyMessage(prefix),
355+
value: ""
356+
}];
357+
this.completions = new FilteredList(completionsForEmpty);
358+
this.openPopup(this.editor, prefix, keepPopupPosition);
359+
return;
360+
}
345361
return this.detach();
362+
}
346363

347364
// One result equals to the prefix
348365
if (filtered.length == 1 && filtered[0].value == prefix && !filtered[0].snippet)
349366
return this.detach();
350367

351368
// Autoinsert if one result
352-
if (this.autoInsert && filtered.length == 1)
369+
if (this.autoInsert && !this.autoShown && filtered.length == 1)
353370
return this.insertMatch(filtered[0]);
354371
}
355372
this.completions = completions;
@@ -408,7 +425,8 @@ class Autocomplete {
408425
}
409426

410427
if (!tooltipNode.parentNode)
411-
document.body.appendChild(tooltipNode);
428+
this.popup.container.appendChild(this.tooltipNode);
429+
412430
var popup = this.popup;
413431
var rect = popup.container.getBoundingClientRect();
414432
tooltipNode.style.top = popup.container.style.top;
@@ -525,6 +543,7 @@ Autocomplete.startCommand = {
525543
var completer = Autocomplete.for(editor);
526544
completer.autoInsert = false;
527545
completer.autoSelect = true;
546+
completer.autoShown = false;
528547
completer.showPopup(editor, options);
529548
// prevent ctrl-space opening context menu on firefox on mac
530549
completer.cancelContextMenu();

‎src/autocomplete/popup.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ class AcePopup {
4242
var el = dom.createElement("div");
4343
var popup = new $singleLineEditor(el);
4444

45-
if (parentNode)
45+
if (parentNode) {
4646
parentNode.appendChild(el);
47+
}
4748
el.style.display = "none";
4849
popup.renderer.content.style.cursor = "default";
4950
popup.renderer.setStyle("ace_autocomplete");

‎src/autocomplete_test.js

+24-4
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ if (typeof process !== "undefined") {
77
var sendKey = require("./test/user").type;
88
var ace = require("./ace");
99
var assert = require("./test/assertions");
10+
var user = require("./test/user");
1011
var Range = require("./range").Range;
1112
require("./ext/language_tools");
13+
var Autocomplete = require("./autocomplete").Autocomplete;
1214

1315
function initEditor(value) {
1416
var editor = ace.edit(null, {
@@ -174,16 +176,16 @@ module.exports = {
174176
var popup = editor.completer.popup;
175177
check(function () {
176178
assert.equal(popup.data.length, 4);
177-
assert.equal(document.body.lastChild.innerHTML, firstDoc);
179+
assert.equal(popup.container.lastChild.innerHTML, firstDoc);
178180
sendKey("Down");
179181
check(function () {
180-
assert.equal(document.body.lastChild.innerHTML, secondDoc);
182+
assert.equal(popup.container.lastChild.innerHTML, secondDoc);
181183
sendKey("Down");
182184
check(function () {
183-
assert.equal(document.body.lastChild.innerHTML, firstDoc);
185+
assert.equal(popup.container.lastChild.innerHTML, firstDoc);
184186
sendKey("Down");
185187
check(function () {
186-
assert.equal(document.body.lastChild.innerHTML, secondDoc);
188+
assert.equal(popup.container.lastChild.innerHTML, secondDoc);
187189
editor.destroy();
188190
editor.container.remove();
189191
done();
@@ -246,6 +248,24 @@ module.exports = {
246248
editor.container.remove();
247249
done();
248250
}, 10);
251+
},
252+
"test: empty message if no suggestions available": function(done) {
253+
var editor = initEditor("");
254+
var emptyMessageText = "No suggestions.";
255+
var autocomplete = Autocomplete.for(editor);
256+
autocomplete.emptyMessage = () => emptyMessageText;
257+
258+
user.type("thereisnoautosuggestionforthisword");
259+
260+
// Open autocompletion via key-binding and verify empty message
261+
user.type("Ctrl-Space");
262+
assert.equal(editor.completer.popup.isOpen, true);
263+
assert.equal(editor.completer.popup.data[0].caption, emptyMessageText);
264+
265+
user.type("Return");
266+
assert.equal(editor.completer.popup.isOpen, false);
267+
268+
done();
249269
}
250270
};
251271

‎src/ext/language_tools.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,8 @@ var doLiveAutocomplete = function(e) {
146146
// Only autocomplete if there's a prefix that can be matched
147147
if (prefix && !hasCompleter) {
148148
var completer = Autocomplete.for(editor);
149-
// Disable autoInsert
150-
completer.autoInsert = false;
149+
// Set a flag for auto shown
150+
completer.autoShown = true;
151151
completer.showPopup(editor);
152152
}
153153
}

0 commit comments

Comments
 (0)
Please sign in to comment.