|
@@ -15,7 +15,11 @@ import { CodeEditor, CodeEditorWrapper } from '@jupyterlab/codeeditor';
|
|
|
|
|
|
import { CodeMirrorEditor } from '@jupyterlab/codemirror';
|
|
import { CodeMirrorEditor } from '@jupyterlab/codemirror';
|
|
|
|
|
|
-import { Completer, CompleterModel } from '@jupyterlab/completer';
|
|
|
|
|
|
+import {
|
|
|
|
+ Completer,
|
|
|
|
+ CompletionHandler,
|
|
|
|
+ CompleterModel
|
|
|
|
+} from '@jupyterlab/completer';
|
|
|
|
|
|
import { framePromise, sleep } from '@jupyterlab/testutils';
|
|
import { framePromise, sleep } from '@jupyterlab/testutils';
|
|
|
|
|
|
@@ -34,6 +38,15 @@ function createEditorWidget(): CodeEditorWrapper {
|
|
}
|
|
}
|
|
|
|
|
|
class CustomRenderer extends Completer.Renderer {
|
|
class CustomRenderer extends Completer.Renderer {
|
|
|
|
+ createCompletionItemNode(
|
|
|
|
+ item: CompletionHandler.ICompletionItem,
|
|
|
|
+ orderedTypes: string[]
|
|
|
|
+ ): HTMLLIElement {
|
|
|
|
+ let li = super.createCompletionItemNode!(item, orderedTypes);
|
|
|
|
+ li.classList.add(TEST_ITEM_CLASS);
|
|
|
|
+ return li;
|
|
|
|
+ }
|
|
|
|
+
|
|
createItemNode(
|
|
createItemNode(
|
|
item: Completer.IItem,
|
|
item: Completer.IItem,
|
|
typeMap: Completer.TypeMap,
|
|
typeMap: Completer.TypeMap,
|
|
@@ -101,6 +114,25 @@ describe('completer/widget', () => {
|
|
expect(items).to.have.length(2);
|
|
expect(items).to.have.length(2);
|
|
expect(Array.from(items[0].classList)).to.contain(TEST_ITEM_CLASS);
|
|
expect(Array.from(items[0].classList)).to.contain(TEST_ITEM_CLASS);
|
|
});
|
|
});
|
|
|
|
+
|
|
|
|
+ it('should accept completion items with a renderer', () => {
|
|
|
|
+ let options: Completer.IOptions = {
|
|
|
|
+ editor: null,
|
|
|
|
+ model: new CompleterModel(),
|
|
|
|
+ renderer: new CustomRenderer()
|
|
|
|
+ };
|
|
|
|
+ options.model!.setCompletionItems!({
|
|
|
|
+ items: [{ label: 'foo' }, { label: 'bar' }]
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ let widget = new Completer(options);
|
|
|
|
+ expect(widget).to.be.an.instanceof(Completer);
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+
|
|
|
|
+ let items = widget.node.querySelectorAll(`.${ITEM_CLASS}`);
|
|
|
|
+ expect(items).to.have.length(2);
|
|
|
|
+ expect(Array.from(items[0].classList)).to.contain(TEST_ITEM_CLASS);
|
|
|
|
+ });
|
|
});
|
|
});
|
|
|
|
|
|
describe('#selected', () => {
|
|
describe('#selected', () => {
|
|
@@ -128,6 +160,62 @@ describe('completer/widget', () => {
|
|
widget.dispose();
|
|
widget.dispose();
|
|
anchor.dispose();
|
|
anchor.dispose();
|
|
});
|
|
});
|
|
|
|
+
|
|
|
|
+ describe('#selected with completion items', () => {
|
|
|
|
+ it('should emit the insert text if it is present', () => {
|
|
|
|
+ let anchor = createEditorWidget();
|
|
|
|
+ let options: Completer.IOptions = {
|
|
|
|
+ editor: anchor.editor,
|
|
|
|
+ model: new CompleterModel()
|
|
|
|
+ };
|
|
|
|
+ let value = '';
|
|
|
|
+ let listener = (sender: any, selected: string) => {
|
|
|
|
+ value = selected;
|
|
|
|
+ };
|
|
|
|
+ options.model!.setCompletionItems!({
|
|
|
|
+ items: [{ label: 'foo', insertText: 'bar' }, { label: 'baz' }]
|
|
|
|
+ });
|
|
|
|
+ Widget.attach(anchor, document.body);
|
|
|
|
+
|
|
|
|
+ let widget = new Completer(options);
|
|
|
|
+
|
|
|
|
+ widget.selected.connect(listener);
|
|
|
|
+ Widget.attach(widget, document.body);
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+ expect(value).to.equal('');
|
|
|
|
+ widget.selectActive();
|
|
|
|
+ expect(value).to.equal('bar');
|
|
|
|
+ widget.dispose();
|
|
|
|
+ anchor.dispose();
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ it('should emit the label if insert text is not present', () => {
|
|
|
|
+ let anchor = createEditorWidget();
|
|
|
|
+ let options: Completer.IOptions = {
|
|
|
|
+ editor: anchor.editor,
|
|
|
|
+ model: new CompleterModel()
|
|
|
|
+ };
|
|
|
|
+ let value = '';
|
|
|
|
+ let listener = (sender: any, selected: string) => {
|
|
|
|
+ value = selected;
|
|
|
|
+ };
|
|
|
|
+ options.model!.setCompletionItems!({
|
|
|
|
+ items: [{ label: 'foo' }, { label: 'baz' }]
|
|
|
|
+ });
|
|
|
|
+ Widget.attach(anchor, document.body);
|
|
|
|
+
|
|
|
|
+ let widget = new Completer(options);
|
|
|
|
+
|
|
|
|
+ widget.selected.connect(listener);
|
|
|
|
+ Widget.attach(widget, document.body);
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+ expect(value).to.equal('');
|
|
|
|
+ widget.selectActive();
|
|
|
|
+ expect(value).to.equal('foo');
|
|
|
|
+ widget.dispose();
|
|
|
|
+ anchor.dispose();
|
|
|
|
+ });
|
|
|
|
+ });
|
|
});
|
|
});
|
|
|
|
|
|
describe('#visibilityChanged', () => {
|
|
describe('#visibilityChanged', () => {
|
|
@@ -180,6 +268,63 @@ describe('completer/widget', () => {
|
|
code.dispose();
|
|
code.dispose();
|
|
panel.dispose();
|
|
panel.dispose();
|
|
});
|
|
});
|
|
|
|
+
|
|
|
|
+ it('should emit a signal when completion items completer visibility changes', async () => {
|
|
|
|
+ let panel = new Panel();
|
|
|
|
+ let code = createEditorWidget();
|
|
|
|
+ let editor = code.editor;
|
|
|
|
+ let model = new CompleterModel();
|
|
|
|
+ let called = false;
|
|
|
|
+
|
|
|
|
+ editor.model.value.text = 'a';
|
|
|
|
+ panel.node.style.position = 'absolute';
|
|
|
|
+ panel.node.style.top = '0px';
|
|
|
|
+ panel.node.style.left = '0px';
|
|
|
|
+ panel.node.style.height = '1000px';
|
|
|
|
+ code.node.style.height = '900px';
|
|
|
|
+ panel.addWidget(code);
|
|
|
|
+ Widget.attach(panel, document.body);
|
|
|
|
+ panel.node.scrollTop = 0;
|
|
|
|
+ document.body.scrollTop = 0;
|
|
|
|
+
|
|
|
|
+ let position = code.editor.getPositionAt(1)!;
|
|
|
|
+
|
|
|
|
+ editor.setCursorPosition(position);
|
|
|
|
+
|
|
|
|
+ let request: Completer.ITextState = {
|
|
|
|
+ column: position.column,
|
|
|
|
+ lineHeight: editor.lineHeight,
|
|
|
|
+ charWidth: editor.charWidth,
|
|
|
|
+ line: position.line,
|
|
|
|
+ text: 'a'
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ model.original = request;
|
|
|
|
+ model.cursor = { start: 0, end: 1 };
|
|
|
|
+ model.setCompletionItems!({
|
|
|
|
+ items: [
|
|
|
|
+ { label: 'abc' },
|
|
|
|
+ { label: 'abd' },
|
|
|
|
+ { label: 'abe' },
|
|
|
|
+ { label: 'abi' }
|
|
|
|
+ ]
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ let widget = new Completer({ model, editor: code.editor });
|
|
|
|
+ widget.hide();
|
|
|
|
+ expect(called).to.equal(false);
|
|
|
|
+ widget.visibilityChanged.connect(() => {
|
|
|
|
+ called = true;
|
|
|
|
+ });
|
|
|
|
+ Widget.attach(widget, document.body);
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+
|
|
|
|
+ await framePromise();
|
|
|
|
+ expect(called).to.equal(true);
|
|
|
|
+ widget.dispose();
|
|
|
|
+ code.dispose();
|
|
|
|
+ panel.dispose();
|
|
|
|
+ });
|
|
});
|
|
});
|
|
|
|
|
|
describe('#model', () => {
|
|
describe('#model', () => {
|
|
@@ -269,6 +414,36 @@ describe('completer/widget', () => {
|
|
widget.dispose();
|
|
widget.dispose();
|
|
anchor.dispose();
|
|
anchor.dispose();
|
|
});
|
|
});
|
|
|
|
+
|
|
|
|
+ it('should reset the completer widget and its completion items', () => {
|
|
|
|
+ let anchor = createEditorWidget();
|
|
|
|
+ let model = new CompleterModel();
|
|
|
|
+ let options: Completer.IOptions = {
|
|
|
|
+ editor: anchor.editor,
|
|
|
|
+ model
|
|
|
|
+ };
|
|
|
|
+ model.setCompletionItems!({
|
|
|
|
+ items: [{ label: 'foo' }, { label: 'bar' }]
|
|
|
|
+ });
|
|
|
|
+ Widget.attach(anchor, document.body);
|
|
|
|
+
|
|
|
|
+ let widget = new Completer(options);
|
|
|
|
+
|
|
|
|
+ Widget.attach(widget, document.body);
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+ expect(widget.isHidden).to.equal(false);
|
|
|
|
+ expect(model.completionItems!()).to.deep.equal({
|
|
|
|
+ items: [{ label: 'foo' }, { label: 'bar' }]
|
|
|
|
+ });
|
|
|
|
+ widget.reset();
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+ expect(widget.isHidden).to.equal(true);
|
|
|
|
+ expect(model.completionItems!()).to.deep.equal({
|
|
|
|
+ items: []
|
|
|
|
+ });
|
|
|
|
+ widget.dispose();
|
|
|
|
+ anchor.dispose();
|
|
|
|
+ });
|
|
});
|
|
});
|
|
|
|
|
|
describe('#handleEvent()', () => {
|
|
describe('#handleEvent()', () => {
|
|
@@ -313,6 +488,36 @@ describe('completer/widget', () => {
|
|
anchor.dispose();
|
|
anchor.dispose();
|
|
});
|
|
});
|
|
|
|
|
|
|
|
+ it('should reset completion items if keydown is outside anchor', () => {
|
|
|
|
+ let model = new CompleterModel();
|
|
|
|
+ let anchor = createEditorWidget();
|
|
|
|
+ let options: Completer.IOptions = {
|
|
|
|
+ editor: anchor.editor,
|
|
|
|
+ model
|
|
|
|
+ };
|
|
|
|
+ model.setCompletionItems!({
|
|
|
|
+ items: [{ label: 'foo' }, { label: 'bar' }]
|
|
|
|
+ });
|
|
|
|
+ Widget.attach(anchor, document.body);
|
|
|
|
+
|
|
|
|
+ let widget = new Completer(options);
|
|
|
|
+
|
|
|
|
+ Widget.attach(widget, document.body);
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+ expect(widget.isHidden).to.equal(false);
|
|
|
|
+ expect(model.completionItems!()).to.deep.equal({
|
|
|
|
+ items: [{ label: 'foo' }, { label: 'bar' }]
|
|
|
|
+ });
|
|
|
|
+ simulate(document.body, 'keydown', { keyCode: 70 }); // F
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+ expect(widget.isHidden).to.equal(true);
|
|
|
|
+ expect(model.completionItems!()).to.deep.equal({
|
|
|
|
+ items: []
|
|
|
|
+ });
|
|
|
|
+ widget.dispose();
|
|
|
|
+ anchor.dispose();
|
|
|
|
+ });
|
|
|
|
+
|
|
it('should select the item below and not progress past last', () => {
|
|
it('should select the item below and not progress past last', () => {
|
|
let anchor = createEditorWidget();
|
|
let anchor = createEditorWidget();
|
|
let model = new CompleterModel();
|
|
let model = new CompleterModel();
|
|
@@ -354,6 +559,46 @@ describe('completer/widget', () => {
|
|
anchor.dispose();
|
|
anchor.dispose();
|
|
});
|
|
});
|
|
|
|
|
|
|
|
+ it('should select the completion item below and not progress past last', () => {
|
|
|
|
+ let anchor = createEditorWidget();
|
|
|
|
+ let model = new CompleterModel();
|
|
|
|
+ let options: Completer.IOptions = {
|
|
|
|
+ editor: anchor.editor,
|
|
|
|
+ model
|
|
|
|
+ };
|
|
|
|
+ model.setCompletionItems!({
|
|
|
|
+ items: [{ label: 'foo' }, { label: 'bar' }, { label: 'baz' }]
|
|
|
|
+ });
|
|
|
|
+ Widget.attach(anchor, document.body);
|
|
|
|
+
|
|
|
|
+ let widget = new Completer(options);
|
|
|
|
+ let target = document.createElement('div');
|
|
|
|
+
|
|
|
|
+ anchor.node.appendChild(target);
|
|
|
|
+ Widget.attach(widget, document.body);
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+
|
|
|
|
+ let items = widget.node.querySelectorAll(`.${ITEM_CLASS}`);
|
|
|
|
+
|
|
|
|
+ expect(Array.from(items[0].classList)).to.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[1].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[2].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ simulate(target, 'keydown', { keyCode: 40 }); // Down
|
|
|
|
+ expect(Array.from(items[0].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[1].classList)).to.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[2].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ simulate(target, 'keydown', { keyCode: 40 }); // Down
|
|
|
|
+ expect(Array.from(items[0].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[1].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[2].classList)).to.contain(ACTIVE_CLASS);
|
|
|
|
+ simulate(target, 'keydown', { keyCode: 40 }); // Down
|
|
|
|
+ expect(Array.from(items[0].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[1].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[2].classList)).to.contain(ACTIVE_CLASS);
|
|
|
|
+ widget.dispose();
|
|
|
|
+ anchor.dispose();
|
|
|
|
+ });
|
|
|
|
+
|
|
it('should select the item above and not progress beyond first', () => {
|
|
it('should select the item above and not progress beyond first', () => {
|
|
let anchor = createEditorWidget();
|
|
let anchor = createEditorWidget();
|
|
let model = new CompleterModel();
|
|
let model = new CompleterModel();
|
|
@@ -401,6 +646,52 @@ describe('completer/widget', () => {
|
|
anchor.dispose();
|
|
anchor.dispose();
|
|
});
|
|
});
|
|
|
|
|
|
|
|
+ it('should select the completion item above and not progress beyond first', () => {
|
|
|
|
+ let anchor = createEditorWidget();
|
|
|
|
+ let model = new CompleterModel();
|
|
|
|
+ let options: Completer.IOptions = {
|
|
|
|
+ editor: anchor.editor,
|
|
|
|
+ model
|
|
|
|
+ };
|
|
|
|
+ model.setCompletionItems!({
|
|
|
|
+ items: [{ label: 'foo' }, { label: 'bar' }, { label: 'baz' }]
|
|
|
|
+ });
|
|
|
|
+ Widget.attach(anchor, document.body);
|
|
|
|
+
|
|
|
|
+ let widget = new Completer(options);
|
|
|
|
+
|
|
|
|
+ Widget.attach(widget, document.body);
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+
|
|
|
|
+ let items = widget.node.querySelectorAll(`.${ITEM_CLASS}`);
|
|
|
|
+
|
|
|
|
+ expect(Array.from(items[0].classList)).to.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[1].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[2].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ simulate(anchor.node, 'keydown', { keyCode: 40 }); // Down
|
|
|
|
+ expect(Array.from(items[0].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[1].classList)).to.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[2].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ simulate(anchor.node, 'keydown', { keyCode: 40 }); // Down
|
|
|
|
+ expect(Array.from(items[0].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[1].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[2].classList)).to.contain(ACTIVE_CLASS);
|
|
|
|
+ simulate(anchor.node, 'keydown', { keyCode: 38 }); // Up
|
|
|
|
+ expect(Array.from(items[0].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[1].classList)).to.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[2].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ simulate(anchor.node, 'keydown', { keyCode: 38 }); // Up
|
|
|
|
+ expect(Array.from(items[0].classList)).to.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[1].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[2].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ simulate(anchor.node, 'keydown', { keyCode: 38 }); // Up
|
|
|
|
+ expect(Array.from(items[0].classList)).to.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[1].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ expect(Array.from(items[2].classList)).to.not.contain(ACTIVE_CLASS);
|
|
|
|
+ widget.dispose();
|
|
|
|
+ anchor.dispose();
|
|
|
|
+ });
|
|
|
|
+
|
|
it('should mark common subset on start and complete that subset on tab', async () => {
|
|
it('should mark common subset on start and complete that subset on tab', async () => {
|
|
let anchor = createEditorWidget();
|
|
let anchor = createEditorWidget();
|
|
let model = new CompleterModel();
|
|
let model = new CompleterModel();
|
|
@@ -437,6 +728,47 @@ describe('completer/widget', () => {
|
|
widget.dispose();
|
|
widget.dispose();
|
|
anchor.dispose();
|
|
anchor.dispose();
|
|
});
|
|
});
|
|
|
|
+
|
|
|
|
+ it('should mark common subset of completion items on start and complete that subset on tab', async () => {
|
|
|
|
+ let anchor = createEditorWidget();
|
|
|
|
+ let model = new CompleterModel();
|
|
|
|
+ let options: Completer.IOptions = {
|
|
|
|
+ editor: anchor.editor,
|
|
|
|
+ model
|
|
|
|
+ };
|
|
|
|
+ let value = '';
|
|
|
|
+ let listener = (sender: any, selected: string) => {
|
|
|
|
+ value = selected;
|
|
|
|
+ };
|
|
|
|
+ model.setCompletionItems!({
|
|
|
|
+ items: [
|
|
|
|
+ { label: 'fo' },
|
|
|
|
+ { label: 'foo' },
|
|
|
|
+ { label: 'foo' },
|
|
|
|
+ { label: 'fooo' }
|
|
|
|
+ ]
|
|
|
|
+ });
|
|
|
|
+ Widget.attach(anchor, document.body);
|
|
|
|
+
|
|
|
|
+ let widget = new Completer(options);
|
|
|
|
+
|
|
|
|
+ widget.selected.connect(listener);
|
|
|
|
+ Widget.attach(widget, document.body);
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+ await framePromise();
|
|
|
|
+ let marked = widget.node.querySelectorAll(`.${ITEM_CLASS} mark`);
|
|
|
|
+ expect(value).to.be.empty;
|
|
|
|
+ expect(marked).to.have.length(4);
|
|
|
|
+ expect(marked[0].textContent).to.equal('fo');
|
|
|
|
+ expect(marked[1].textContent).to.equal('fo');
|
|
|
|
+ expect(marked[2].textContent).to.equal('fo');
|
|
|
|
+ expect(marked[3].textContent).to.equal('fo');
|
|
|
|
+ simulate(anchor.node, 'keydown', { keyCode: 9 }); // Tab key
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+ expect(value).to.equal('fo');
|
|
|
|
+ widget.dispose();
|
|
|
|
+ anchor.dispose();
|
|
|
|
+ });
|
|
});
|
|
});
|
|
|
|
|
|
describe('mousedown', () => {
|
|
describe('mousedown', () => {
|
|
@@ -474,6 +806,39 @@ describe('completer/widget', () => {
|
|
anchor.dispose();
|
|
anchor.dispose();
|
|
});
|
|
});
|
|
|
|
|
|
|
|
+ it('should trigger a selected signal on mouse down of completion item', () => {
|
|
|
|
+ let anchor = createEditorWidget();
|
|
|
|
+ let model = new CompleterModel();
|
|
|
|
+ let options: Completer.IOptions = {
|
|
|
|
+ editor: anchor.editor,
|
|
|
|
+ model
|
|
|
|
+ };
|
|
|
|
+ let value = '';
|
|
|
|
+ let listener = (sender: any, selected: string) => {
|
|
|
|
+ value = selected;
|
|
|
|
+ };
|
|
|
|
+ model.setCompletionItems!({
|
|
|
|
+ items: [{ label: 'foo' }, { label: 'bar' }, { label: 'baz' }]
|
|
|
|
+ });
|
|
|
|
+ model.query = 'b';
|
|
|
|
+ Widget.attach(anchor, document.body);
|
|
|
|
+
|
|
|
|
+ let widget = new Completer(options);
|
|
|
|
+
|
|
|
|
+ widget.selected.connect(listener);
|
|
|
|
+ Widget.attach(widget, document.body);
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+
|
|
|
|
+ let item = widget.node.querySelectorAll(`.${ITEM_CLASS} mark`)[1];
|
|
|
|
+
|
|
|
|
+ simulate(anchor.node, 'keydown', { keyCode: 9 }); // Tab key
|
|
|
|
+ expect(model.query).to.equal('ba');
|
|
|
|
+ simulate(item, 'mousedown');
|
|
|
|
+ expect(value).to.equal('baz');
|
|
|
|
+ widget.dispose();
|
|
|
|
+ anchor.dispose();
|
|
|
|
+ });
|
|
|
|
+
|
|
it('should ignore nonstandard mouse clicks (e.g., right click)', () => {
|
|
it('should ignore nonstandard mouse clicks (e.g., right click)', () => {
|
|
let anchor = createEditorWidget();
|
|
let anchor = createEditorWidget();
|
|
let model = new CompleterModel();
|
|
let model = new CompleterModel();
|
|
@@ -500,6 +865,34 @@ describe('completer/widget', () => {
|
|
anchor.dispose();
|
|
anchor.dispose();
|
|
});
|
|
});
|
|
|
|
|
|
|
|
+ it('should ignore nonstandard mouse clicks (e.g., right click) on completion item', () => {
|
|
|
|
+ let anchor = createEditorWidget();
|
|
|
|
+ let model = new CompleterModel();
|
|
|
|
+ let options: Completer.IOptions = {
|
|
|
|
+ editor: anchor.editor,
|
|
|
|
+ model
|
|
|
|
+ };
|
|
|
|
+ let value = '';
|
|
|
|
+ let listener = (sender: any, selected: string) => {
|
|
|
|
+ value = selected;
|
|
|
|
+ };
|
|
|
|
+ model.setCompletionItems!({
|
|
|
|
+ items: [{ label: 'foo' }, { label: 'bar' }]
|
|
|
|
+ });
|
|
|
|
+ Widget.attach(anchor, document.body);
|
|
|
|
+
|
|
|
|
+ let widget = new Completer(options);
|
|
|
|
+
|
|
|
|
+ widget.selected.connect(listener);
|
|
|
|
+ Widget.attach(widget, document.body);
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+ expect(value).to.equal('');
|
|
|
|
+ simulate(widget.node, 'mousedown', { button: 1 });
|
|
|
|
+ expect(value).to.equal('');
|
|
|
|
+ widget.dispose();
|
|
|
|
+ anchor.dispose();
|
|
|
|
+ });
|
|
|
|
+
|
|
it('should ignore a mouse down that misses an item', () => {
|
|
it('should ignore a mouse down that misses an item', () => {
|
|
let anchor = createEditorWidget();
|
|
let anchor = createEditorWidget();
|
|
let model = new CompleterModel();
|
|
let model = new CompleterModel();
|
|
@@ -526,6 +919,34 @@ describe('completer/widget', () => {
|
|
anchor.dispose();
|
|
anchor.dispose();
|
|
});
|
|
});
|
|
|
|
|
|
|
|
+ it('should ignore a mouse down that misses a completion item', () => {
|
|
|
|
+ let anchor = createEditorWidget();
|
|
|
|
+ let model = new CompleterModel();
|
|
|
|
+ let options: Completer.IOptions = {
|
|
|
|
+ editor: anchor.editor,
|
|
|
|
+ model
|
|
|
|
+ };
|
|
|
|
+ let value = '';
|
|
|
|
+ let listener = (sender: any, selected: string) => {
|
|
|
|
+ value = selected;
|
|
|
|
+ };
|
|
|
|
+ model.setCompletionItems!({
|
|
|
|
+ items: [{ label: 'foo' }, { label: 'bar' }]
|
|
|
|
+ });
|
|
|
|
+ Widget.attach(anchor, document.body);
|
|
|
|
+
|
|
|
|
+ let widget = new Completer(options);
|
|
|
|
+
|
|
|
|
+ widget.selected.connect(listener);
|
|
|
|
+ Widget.attach(widget, document.body);
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+ expect(value).to.equal('');
|
|
|
|
+ simulate(widget.node, 'mousedown');
|
|
|
|
+ expect(value).to.equal('');
|
|
|
|
+ widget.dispose();
|
|
|
|
+ anchor.dispose();
|
|
|
|
+ });
|
|
|
|
+
|
|
it('should hide widget if mouse down misses it', () => {
|
|
it('should hide widget if mouse down misses it', () => {
|
|
let anchor = createEditorWidget();
|
|
let anchor = createEditorWidget();
|
|
let model = new CompleterModel();
|
|
let model = new CompleterModel();
|
|
@@ -551,6 +972,34 @@ describe('completer/widget', () => {
|
|
widget.dispose();
|
|
widget.dispose();
|
|
anchor.dispose();
|
|
anchor.dispose();
|
|
});
|
|
});
|
|
|
|
+
|
|
|
|
+ it('should hide completion items widget if mouse down misses it', () => {
|
|
|
|
+ let anchor = createEditorWidget();
|
|
|
|
+ let model = new CompleterModel();
|
|
|
|
+ let options: Completer.IOptions = {
|
|
|
|
+ editor: anchor.editor,
|
|
|
|
+ model
|
|
|
|
+ };
|
|
|
|
+ let listener = (sender: any, selected: string) => {
|
|
|
|
+ // no op
|
|
|
|
+ };
|
|
|
|
+ model.setCompletionItems!({
|
|
|
|
+ items: [{ label: 'foo' }, { label: 'bar' }]
|
|
|
|
+ });
|
|
|
|
+ Widget.attach(anchor, document.body);
|
|
|
|
+
|
|
|
|
+ let widget = new Completer(options);
|
|
|
|
+
|
|
|
|
+ widget.selected.connect(listener);
|
|
|
|
+ Widget.attach(widget, document.body);
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+ expect(widget.isHidden).to.equal(false);
|
|
|
|
+ simulate(anchor.node, 'mousedown');
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+ expect(widget.isHidden).to.equal(true);
|
|
|
|
+ widget.dispose();
|
|
|
|
+ anchor.dispose();
|
|
|
|
+ });
|
|
});
|
|
});
|
|
|
|
|
|
describe('scroll', () => {
|
|
describe('scroll', () => {
|
|
@@ -653,6 +1102,84 @@ describe('completer/widget', () => {
|
|
anchor.dispose();
|
|
anchor.dispose();
|
|
});
|
|
});
|
|
|
|
|
|
|
|
+ it('should emit label if there is only one completion item and insert Text is not set', () => {
|
|
|
|
+ let anchor = createEditorWidget();
|
|
|
|
+ let model = new CompleterModel();
|
|
|
|
+ let coords = { left: 0, right: 0, top: 100, bottom: 120 };
|
|
|
|
+ let request: Completer.ITextState = {
|
|
|
|
+ column: 0,
|
|
|
|
+ lineHeight: 0,
|
|
|
|
+ charWidth: 0,
|
|
|
|
+ line: 0,
|
|
|
|
+ coords: coords as CodeEditor.ICoordinate,
|
|
|
|
+ text: 'f'
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ let value = '';
|
|
|
|
+ let options: Completer.IOptions = {
|
|
|
|
+ editor: anchor.editor,
|
|
|
|
+ model
|
|
|
|
+ };
|
|
|
|
+ let listener = (sender: any, selected: string) => {
|
|
|
|
+ value = selected;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ Widget.attach(anchor, document.body);
|
|
|
|
+ model.original = request;
|
|
|
|
+ model.setCompletionItems!({ items: [{ label: 'foo' }] });
|
|
|
|
+
|
|
|
|
+ let widget = new Completer(options);
|
|
|
|
+ widget.selected.connect(listener);
|
|
|
|
+ Widget.attach(widget, document.body);
|
|
|
|
+
|
|
|
|
+ expect(value).to.equal('');
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+ expect(value).to.equal('foo');
|
|
|
|
+
|
|
|
|
+ widget.dispose();
|
|
|
|
+ anchor.dispose();
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ it('should emit insert text if there is only one completion item and insert Text is set', () => {
|
|
|
|
+ let anchor = createEditorWidget();
|
|
|
|
+ let model = new CompleterModel();
|
|
|
|
+ let coords = { left: 0, right: 0, top: 100, bottom: 120 };
|
|
|
|
+ let request: Completer.ITextState = {
|
|
|
|
+ column: 0,
|
|
|
|
+ lineHeight: 0,
|
|
|
|
+ charWidth: 0,
|
|
|
|
+ line: 0,
|
|
|
|
+ coords: coords as CodeEditor.ICoordinate,
|
|
|
|
+ text: 'f'
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ let value = '';
|
|
|
|
+ let options: Completer.IOptions = {
|
|
|
|
+ editor: anchor.editor,
|
|
|
|
+ model
|
|
|
|
+ };
|
|
|
|
+ let listener = (sender: any, selected: string) => {
|
|
|
|
+ value = selected;
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ Widget.attach(anchor, document.body);
|
|
|
|
+ model.original = request;
|
|
|
|
+ model.setCompletionItems!({
|
|
|
|
+ items: [{ label: 'foo', insertText: 'bar' }]
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ let widget = new Completer(options);
|
|
|
|
+ widget.selected.connect(listener);
|
|
|
|
+ Widget.attach(widget, document.body);
|
|
|
|
+
|
|
|
|
+ expect(value).to.equal('');
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+ expect(value).to.equal('bar');
|
|
|
|
+
|
|
|
|
+ widget.dispose();
|
|
|
|
+ anchor.dispose();
|
|
|
|
+ });
|
|
|
|
+
|
|
it('should do nothing if a model does not exist', () => {
|
|
it('should do nothing if a model does not exist', () => {
|
|
let widget = new LogWidget({ editor: null });
|
|
let widget = new LogWidget({ editor: null });
|
|
MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
@@ -690,6 +1217,40 @@ describe('completer/widget', () => {
|
|
widget.dispose();
|
|
widget.dispose();
|
|
anchor.dispose();
|
|
anchor.dispose();
|
|
});
|
|
});
|
|
|
|
+
|
|
|
|
+ it('should un-hide widget if multiple completion items are available', () => {
|
|
|
|
+ let anchor = createEditorWidget();
|
|
|
|
+ let model = new CompleterModel();
|
|
|
|
+ let coords = { left: 0, right: 0, top: 100, bottom: 120 };
|
|
|
|
+ let request: Completer.ITextState = {
|
|
|
|
+ column: 0,
|
|
|
|
+ lineHeight: 0,
|
|
|
|
+ charWidth: 0,
|
|
|
|
+ line: 0,
|
|
|
|
+ coords: coords as CodeEditor.ICoordinate,
|
|
|
|
+ text: 'f'
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ let options: Completer.IOptions = {
|
|
|
|
+ editor: anchor.editor,
|
|
|
|
+ model
|
|
|
|
+ };
|
|
|
|
+
|
|
|
|
+ Widget.attach(anchor, document.body);
|
|
|
|
+ model.original = request;
|
|
|
|
+ model.setCompletionItems!({
|
|
|
|
+ items: [{ label: 'foo' }, { label: 'bar' }, { label: 'baz' }]
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ let widget = new Completer(options);
|
|
|
|
+ widget.hide();
|
|
|
|
+ expect(widget.isHidden).to.equal(true);
|
|
|
|
+ Widget.attach(widget, document.body);
|
|
|
|
+ MessageLoop.sendMessage(widget, Widget.Msg.UpdateRequest);
|
|
|
|
+ expect(widget.isVisible).to.equal(true);
|
|
|
|
+ widget.dispose();
|
|
|
|
+ anchor.dispose();
|
|
|
|
+ });
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|