From 6ea9901ef218be339b79cb59472e65a7feae673a Mon Sep 17 00:00:00 2001 From: Corey Johnson & Nathan Sobo Date: Thu, 19 Apr 2012 14:59:02 -0700 Subject: [PATCH] Autocomplete uses miniEditor --- spec/app/autocomplete-spec.coffee | 94 +++++++++-------------------- src/app/autocomplete.coffee | 54 +++++++---------- src/app/keymaps/autocomplete.coffee | 2 +- 3 files changed, 51 insertions(+), 99 deletions(-) diff --git a/spec/app/autocomplete-spec.coffee b/spec/app/autocomplete-spec.coffee index 74e5a3a08..da668125b 100644 --- a/spec/app/autocomplete-spec.coffee +++ b/spec/app/autocomplete-spec.coffee @@ -6,33 +6,36 @@ Editor = require 'editor' describe "Autocomplete", -> autocomplete = null editor = null + miniEditor = null beforeEach -> editor = new Editor() editor.setBuffer new Buffer(require.resolve('fixtures/sample.js')) autocomplete = new Autocomplete(editor) + miniEditor = autocomplete.miniEditor afterEach -> autocomplete.remove() describe 'autocomplete:toggle event', -> - it "shows autocomplete view and adds 'autocomplete' class to editor", -> + it "shows autocomplete view and focuses its mini-editor", -> expect($(document).find('#autocomplete')).not.toExist() - expect(editor).not.toHaveClass('autocomplete') editor.trigger "autocomplete:toggle" expect($(document).find('#autocomplete')).toExist() - expect(editor).toHaveClass('autocomplete') + expect(autocomplete.editor.isFocused).toBeFalsy() + expect(autocomplete.miniEditor.isFocused).toBeTruthy() - editor.trigger "autocomplete:toggle" + miniEditor.trigger "autocomplete:toggle" expect($(document).find('#autocomplete')).not.toExist() - expect(editor).not.toHaveClass('autocomplete') + expect(autocomplete.editor.isFocused).toBeTruthy() + expect(autocomplete.miniEditor.isFocused).toBeFalsy() describe "when no text is selected", -> it 'autocompletes word when there is only a prefix', -> editor.buffer.insert([10,0] ,"extra:s:extra") editor.setCursorBufferPosition([10,7]) - editor.trigger "autocomplete:toggle" + autocomplete.attach() expect(editor.lineForBufferRow(10)).toBe "extra:sort:extra" expect(editor.getCursorBufferPosition()).toEqual [10,10] @@ -45,7 +48,7 @@ describe "Autocomplete", -> it 'autocompletes word when there is only a suffix', -> editor.buffer.insert([10,0] ,"extra:e:extra") editor.setCursorBufferPosition([10,6]) - editor.trigger "autocomplete:toggle" + autocomplete.attach() expect(editor.lineForBufferRow(10)).toBe "extra:while:extra" expect(editor.getCursorBufferPosition()).toEqual [10,10] @@ -57,7 +60,7 @@ describe "Autocomplete", -> it 'autocompletes word when there is a prefix and suffix', -> editor.buffer.insert([8,43] ,"q") editor.setCursorBufferPosition([8,44]) - editor.trigger "autocomplete:toggle" + autocomplete.attach() expect(editor.lineForBufferRow(8)).toBe " return sort(left).concat(pivot).concat(quicksort(right));" expect(editor.getCursorBufferPosition()).toEqual [8,48] @@ -70,7 +73,7 @@ describe "Autocomplete", -> it 'autocompletes word when there is only a prefix', -> editor.buffer.insert([10,0] ,"extra:sort:extra") editor.setSelectionBufferRange [[10,7], [10,10]] - editor.trigger "autocomplete:toggle" + autocomplete.attach() expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra" expect(editor.getCursorBufferPosition()).toEqual [10,11] @@ -82,7 +85,7 @@ describe "Autocomplete", -> it 'autocompletes word when there is only a suffix', -> editor.buffer.insert([10,0] ,"extra:current:extra") editor.setSelectionBufferRange [[10,6],[10,12]] - editor.trigger "autocomplete:toggle" + autocomplete.attach() expect(editor.lineForBufferRow(10)).toBe "extra:quicksort:extra" expect(editor.getCursorBufferPosition()).toEqual [10,14] @@ -93,7 +96,7 @@ describe "Autocomplete", -> it 'autocompletes word when there is a prefix and suffix', -> editor.setSelectionBufferRange [[5,7],[5,12]] - editor.trigger "autocomplete:toggle" + autocomplete.attach() expect(editor.lineForBufferRow(5)).toBe " concat = items.shift();" expect(editor.getCursorBufferPosition()).toEqual [5,11] @@ -106,9 +109,8 @@ describe "Autocomplete", -> it 'replaces selection with selected match, moves the cursor to the end of the match, and removes the autocomplete menu', -> editor.buffer.insert([10,0] ,"extra:sort:extra") editor.setSelectionBufferRange [[10,7], [10,9]] - editor.trigger "autocomplete:toggle" - - editor.trigger "autocomplete:confirm" + autocomplete.attach() + miniEditor.trigger "autocomplete:confirm" expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra" expect(editor.getCursorBufferPosition()).toEqual [10,11] @@ -121,8 +123,8 @@ describe "Autocomplete", -> editor.setSelectionBufferRange [[10,7], [10,8]] originalSelectionBufferRange = editor.getSelection().getBufferRange() - editor.trigger "autocomplete:toggle" - editor.trigger "autocomplete:cancel" + autocomplete.attach() + miniEditor.trigger "autocomplete:cancel" expect(editor.lineForBufferRow(10)).toBe "extra:so:extra" expect(editor.getSelection().getBufferRange()).toEqual originalSelectionBufferRange @@ -132,15 +134,15 @@ describe "Autocomplete", -> it 'replaces selection with previous match', -> editor.buffer.insert([10,0] ,"extra:t:extra") editor.setCursorBufferPosition([10,6]) - editor.trigger "autocomplete:toggle" + autocomplete.attach() - editor.trigger "move-up" + miniEditor.trigger "move-up" expect(editor.lineForBufferRow(10)).toBe "extra:concat:extra" expect(autocomplete.find('li:eq(0)')).not.toHaveClass('selected') expect(autocomplete.find('li:eq(1)')).not.toHaveClass('selected') expect(autocomplete.find('li:eq(7)')).toHaveClass('selected') - editor.trigger "move-up" + miniEditor.trigger "move-up" expect(editor.lineForBufferRow(10)).toBe "extra:right:extra" expect(autocomplete.find('li:eq(0)')).not.toHaveClass('selected') expect(autocomplete.find('li:eq(7)')).not.toHaveClass('selected') @@ -150,28 +152,25 @@ describe "Autocomplete", -> it 'replaces selection with next match', -> editor.buffer.insert([10,0] ,"extra:s:extra") editor.setCursorBufferPosition([10,7]) - editor.trigger "autocomplete:toggle" + autocomplete.attach() - editor.trigger "move-down" + miniEditor.trigger "move-down" expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra" expect(autocomplete.find('li:eq(0)')).not.toHaveClass('selected') expect(autocomplete.find('li:eq(1)')).toHaveClass('selected') - editor.trigger "move-down" + miniEditor.trigger "move-down" expect(editor.lineForBufferRow(10)).toBe "extra:sort:extra" expect(autocomplete.find('li:eq(0)')).toHaveClass('selected') expect(autocomplete.find('li:eq(1)')).not.toHaveClass('selected') - describe 'when the cursor is moved', -> + describe 'when the editor is focused', -> it "cancels the autocomplete", -> - editor.buffer.insert([10,0] ,"extra:s:extra") - editor.setCursorBufferPosition([10,7]) - editor.trigger "autocomplete:toggle" + autocomplete.attach() - spyOn(autocomplete, "cancel").andCallThrough() - editor.moveCursorRight() + spyOn(autocomplete, "cancel") + editor.focus() expect(autocomplete.cancel).toHaveBeenCalled() - expect(editor.lineForBufferRow(10)).toBe "extra:s:extra" describe 'when changes are made to the buffer', -> describe "when the autocomplete menu is detached", -> @@ -187,44 +186,9 @@ describe "Autocomplete", -> spyOn(autocomplete, 'buildWordList') editor.setCursorBufferPosition([10,7]) - editor.trigger "autocomplete:toggle" + autocomplete.attach() expect(autocomplete.buildWordList).not.toHaveBeenCalled() - describe "when the change was not caused by autocomplete", -> - describe "when the change produces a prefix that still has matches in the word list", -> - it "rebuilds the match list based on the new prefix and suffix", -> - editor.buffer.insert([10, 0] ,"t") - editor.setCursorBufferPosition [10, 0] - editor.trigger "autocomplete:toggle" - expect($(document).find('#autocomplete')).toExist() - - editor.insertText('c') - - expect($(document).find('#autocomplete')).toExist() - - expect(editor.lineForBufferRow(10)).toBe "current" - expect(editor.getCursorBufferPosition()).toEqual [10,6] - expect(editor.getSelection().getBufferRange()).toEqual [[10,1], [10,6]] - - expect(autocomplete.matchesList.find('li').length).toBe 2 - expect(autocomplete.matchesList.find('li:eq(0)')).toHaveText('current') - expect(autocomplete.matchesList.find('li:eq(1)')).toHaveText('concat') - - describe "when the change produces a prefix that has no matches in the word list", -> - it "accepts the selected match and appends the change after it", -> - editor.buffer.insert([10, 0] ,"c") - editor.setCursorBufferPosition [10, 1] - editor.trigger "autocomplete:toggle" - expect($(document).find('#autocomplete')).toExist() - - editor.insertText(' ') - - expect(editor.lineForBufferRow(10)).toBe "current " - expect(editor.getCursorBufferPosition()).toEqual [10,8] - expect(editor.getSelection().getBufferRange()).toEqual [[10,8], [10,8]] - - expect($(document).find('#autocomplete')).not.toExist() - describe "when editor's buffer is assigned a new buffer", -> it 'creates and uses a new word list based on new buffer', -> wordList = autocomplete.wordList diff --git a/src/app/autocomplete.coffee b/src/app/autocomplete.coffee index cd2b151c8..65e7d4f00 100644 --- a/src/app/autocomplete.coffee +++ b/src/app/autocomplete.coffee @@ -2,14 +2,17 @@ $ = require 'jquery' _ = require 'underscore' Range = require 'range' +Editor = require 'editor' module.exports = class Autocomplete extends View @content: -> @div id: 'autocomplete', => @ol outlet: 'matchesList' + @subview 'miniEditor', new Editor editor: null + miniEditor: null currentBuffer: null wordList: null wordRegex: /\w+/g @@ -27,16 +30,25 @@ class Autocomplete extends View handleEvents: -> @editor.on 'buffer-path-change', => @setCurrentBuffer(@editor.buffer) - @editor.on 'autocomplete:toggle', => @toggle() - @editor.on 'autocomplete:confirm', => @confirm() - @editor.on 'autocomplete:cancel', => @cancel() @editor.on 'before-remove', => @currentBuffer?.off '.autocomplete' + @editor.on 'autocomplete:toggle', => @attach() + @on 'autocomplete:toggle', => @detach() + @on 'autocomplete:confirm', => @confirm() + @on 'autocomplete:cancel', => @cancel() + + @miniEditor.preempt 'move-up', => + @selectPreviousMatch() + false + + @miniEditor.preempt 'move-down', => + @selectNextMatch() + false + setCurrentBuffer: (buffer) -> @currentBuffer?.off '.autocomplete' @currentBuffer = buffer @buildWordList() - @currentBuffer.on 'change.autocomplete', (e) => @bufferChanged(e) confirm: -> @@ -53,35 +65,23 @@ class Autocomplete extends View @editor.getSelection().insertText @originalSelectedText @editor.setSelectionBufferRange(@originalSelectionBufferRange) - toggle: -> - if @parent()[0] then @detach() else @attach() - attach: -> - @editor.preempt 'move-up.autocomplete', => - @selectPreviousMatch() - false + @editor.on 'focus.autocomplete', => @cancel() - @editor.preempt 'move-down.autocomplete', => - @selectNextMatch() - false - - @editor.on 'cursor-move.autocomplete', (e, data) => - @cancel() unless @isAutocompleting or data.bufferChange - - @editor.addClass('autocomplete') @originalSelectedText = @editor.getSelectedText() @originalSelectionBufferRange = @editor.getSelection().getBufferRange() @buildMatchList() + @selectMatchAtIndex(0) if @matches.length > 0 cursorScreenPosition = @editor.getCursorScreenPosition() {left, top} = @editor.pixelOffsetForScreenPosition(cursorScreenPosition) @css {left: left, top: top + @editor.lineHeight} $(document.body).append(this) - @focus() + @miniEditor.focus() detach: -> @editor.off(".autocomplete") - @editor.removeClass('autocomplete') + @editor.focus() super selectPreviousMatch: -> @@ -103,17 +103,6 @@ class Autocomplete extends View @matches[@currentMatchIndex] bufferChanged: (e) -> - if @parent()[0] and not @isAutocompleting - selectedMatch = @selectedMatch() - @buildMatchList() - if @matches.length == 0 - @detach() - @currentBuffer.undo() - @completeUsingMatch(selectedMatch) - @editor.getSelection().clearSelection() - @editor.insertText(e.newText) - return - @buildWordList() unless @isAutocompleting buildMatchList: -> @@ -124,6 +113,7 @@ class Autocomplete extends View return currentWord = prefix + @editor.getSelectedText() + suffix + @matches = (match for match in @wordMatches(prefix, suffix) when match.word != currentWord) @matchesList.empty() @@ -132,8 +122,6 @@ class Autocomplete extends View else @matchesList.append($$ -> @li "No matches found") - @selectMatchAtIndex(0) if @matches.length > 0 - buildWordList: () -> @wordList = _.unique(@currentBuffer.getText().match(@wordRegex)) diff --git a/src/app/keymaps/autocomplete.coffee b/src/app/keymaps/autocomplete.coffee index c22cb4ae6..ac8038a0e 100644 --- a/src/app/keymaps/autocomplete.coffee +++ b/src/app/keymaps/autocomplete.coffee @@ -1,6 +1,6 @@ window.keymap.bindKeys '.editor', 'escape': 'autocomplete:toggle' -window.keymap.bindKeys '.editor.autocomplete', +window.keymap.bindKeys '#autocomplete', 'enter': 'autocomplete:confirm' 'escape': 'autocomplete:cancel'