mirror of
https://github.com/pulsar-edit/pulsar.git
synced 2024-11-10 10:17:11 +03:00
Pull out autocomplete package into a separate repo
This commit is contained in:
parent
dc775f93ff
commit
52e8e3aaf3
@ -66,6 +66,7 @@
|
||||
"xml-tmbundle": "1.0.0",
|
||||
"yaml-tmbundle": "1.0.0",
|
||||
"archive-view": "0.1.0",
|
||||
"autocomplete": "0.1.0",
|
||||
"autoflow": "0.1.0",
|
||||
"bookmarks": "0.1.0",
|
||||
"bracket-matcher": "0.1.0",
|
||||
|
@ -1,8 +0,0 @@
|
||||
'.editor':
|
||||
'ctrl-space': 'autocomplete:attach'
|
||||
|
||||
'.autocomplete .editor':
|
||||
'ctrl-space': 'core:cancel'
|
||||
|
||||
'.autocomplete .mini.editor input':
|
||||
'enter': 'core:confirm'
|
@ -1,190 +0,0 @@
|
||||
$ = require 'jquery'
|
||||
{$$} = require 'space-pen'
|
||||
_ = require 'underscore'
|
||||
Range = require 'range'
|
||||
SelectList = require 'select-list'
|
||||
|
||||
module.exports =
|
||||
class AutocompleteView extends SelectList
|
||||
@viewClass: -> "autocomplete #{super} popover-list"
|
||||
|
||||
editor: null
|
||||
currentBuffer: null
|
||||
wordList: null
|
||||
wordRegex: /\w+/g
|
||||
originalSelectionBufferRange: null
|
||||
originalCursorPosition: null
|
||||
aboveCursor: false
|
||||
filterKey: 'word'
|
||||
|
||||
initialize: (@editor) ->
|
||||
super
|
||||
@handleEvents()
|
||||
@setCurrentBuffer(@editor.getBuffer())
|
||||
|
||||
itemForElement: (match) ->
|
||||
$$ ->
|
||||
@li =>
|
||||
@span match.word
|
||||
|
||||
handleEvents: ->
|
||||
@list.on 'mousewheel', (event) -> event.stopPropagation()
|
||||
|
||||
@editor.on 'editor:path-changed', => @setCurrentBuffer(@editor.getBuffer())
|
||||
@editor.command 'autocomplete:attach', => @attach()
|
||||
@editor.command 'autocomplete:next', => @selectNextItem()
|
||||
@editor.command 'autocomplete:previous', => @selectPreviousItem()
|
||||
|
||||
@miniEditor.preempt 'textInput', (e) =>
|
||||
text = e.originalEvent.data
|
||||
unless text.match(@wordRegex)
|
||||
@confirmSelection()
|
||||
@editor.insertText(text)
|
||||
false
|
||||
|
||||
setCurrentBuffer: (@currentBuffer) ->
|
||||
|
||||
selectItem: (item) ->
|
||||
super
|
||||
|
||||
match = @getSelectedElement()
|
||||
@replaceSelectedTextWithMatch(match) if match
|
||||
|
||||
selectNextItem: ->
|
||||
super
|
||||
|
||||
false
|
||||
|
||||
selectPreviousItem: ->
|
||||
super
|
||||
|
||||
false
|
||||
|
||||
getCompletionsForCursorScope: ->
|
||||
cursorScope = @editor.scopesForBufferPosition(@editor.getCursorBufferPosition())
|
||||
completions = syntax.propertiesForScope(cursorScope, 'editor.completions')
|
||||
completions = completions.map (properties) -> _.valueForKeyPath(properties, 'editor.completions')
|
||||
_.uniq(_.flatten(completions))
|
||||
|
||||
buildWordList: ->
|
||||
wordHash = {}
|
||||
matches = @currentBuffer.getText().match(@wordRegex)
|
||||
wordHash[word] ?= true for word in matches ? []
|
||||
wordHash[word] ?= true for word in @getCompletionsForCursorScope()
|
||||
|
||||
@wordList = Object.keys(wordHash).sort (word1, word2) ->
|
||||
word1 = word1.toLowerCase()
|
||||
word2 = word2.toLowerCase()
|
||||
if word1 > word2
|
||||
1
|
||||
else if word1 < word2
|
||||
-1
|
||||
else
|
||||
0
|
||||
|
||||
confirmed: (match) ->
|
||||
@editor.getSelection().clear()
|
||||
@cancel()
|
||||
return unless match
|
||||
@replaceSelectedTextWithMatch match
|
||||
position = @editor.getCursorBufferPosition()
|
||||
@editor.setCursorBufferPosition([position.row, position.column + match.suffix.length])
|
||||
|
||||
cancelled: ->
|
||||
super
|
||||
|
||||
@editor.abort()
|
||||
@editor.setSelectedBufferRange(@originalSelectionBufferRange)
|
||||
rootView.focus() if @miniEditor.isFocused
|
||||
|
||||
attach: ->
|
||||
@editor.transact()
|
||||
|
||||
@aboveCursor = false
|
||||
@originalSelectionBufferRange = @editor.getSelection().getBufferRange()
|
||||
@originalCursorPosition = @editor.getCursorScreenPosition()
|
||||
|
||||
@buildWordList()
|
||||
matches = @findMatchesForCurrentSelection()
|
||||
@setArray(matches)
|
||||
|
||||
if matches.length is 1
|
||||
@confirmSelection()
|
||||
else
|
||||
@editor.appendToLinesView(this)
|
||||
@setPosition()
|
||||
@miniEditor.focus()
|
||||
|
||||
detach: ->
|
||||
super
|
||||
|
||||
@editor.off(".autocomplete")
|
||||
@editor.focus()
|
||||
|
||||
setPosition: ->
|
||||
{ left, top } = @editor.pixelPositionForScreenPosition(@originalCursorPosition)
|
||||
height = @outerHeight()
|
||||
potentialTop = top + @editor.lineHeight
|
||||
potentialBottom = potentialTop - @editor.scrollTop() + height
|
||||
|
||||
if @aboveCursor or potentialBottom > @editor.outerHeight()
|
||||
@aboveCursor = true
|
||||
@css(left: left, top: top - height, bottom: 'inherit')
|
||||
else
|
||||
@css(left: left, top: potentialTop, bottom: 'inherit')
|
||||
|
||||
findMatchesForCurrentSelection: ->
|
||||
selection = @editor.getSelection()
|
||||
{prefix, suffix} = @prefixAndSuffixOfSelection(selection)
|
||||
|
||||
if (prefix.length + suffix.length) > 0
|
||||
regex = new RegExp("^#{prefix}.+#{suffix}$", "i")
|
||||
currentWord = prefix + @editor.getSelectedText() + suffix
|
||||
for word in @wordList when regex.test(word) and word != currentWord
|
||||
{prefix, suffix, word}
|
||||
else
|
||||
{word, prefix, suffix} for word in @wordList
|
||||
|
||||
replaceSelectedTextWithMatch: (match) ->
|
||||
selection = @editor.getSelection()
|
||||
startPosition = selection.getBufferRange().start
|
||||
buffer = @editor.getBuffer()
|
||||
|
||||
selection.deleteSelectedText()
|
||||
cursorPosition = @editor.getCursorBufferPosition()
|
||||
buffer.delete(Range.fromPointWithDelta(cursorPosition, 0, match.suffix.length))
|
||||
buffer.delete(Range.fromPointWithDelta(cursorPosition, 0, -match.prefix.length))
|
||||
@editor.insertText(match.word)
|
||||
|
||||
infixLength = match.word.length - match.prefix.length - match.suffix.length
|
||||
@editor.setSelectedBufferRange([startPosition, [startPosition.row, startPosition.column + infixLength]])
|
||||
|
||||
prefixAndSuffixOfSelection: (selection) ->
|
||||
selectionRange = selection.getBufferRange()
|
||||
lineRange = [[selectionRange.start.row, 0], [selectionRange.end.row, @editor.lineLengthForBufferRow(selectionRange.end.row)]]
|
||||
[prefix, suffix] = ["", ""]
|
||||
|
||||
@currentBuffer.scanInRange @wordRegex, lineRange, ({match, range, stop}) ->
|
||||
stop() if range.start.isGreaterThan(selectionRange.end)
|
||||
|
||||
if range.intersectsWith(selectionRange)
|
||||
prefixOffset = selectionRange.start.column - range.start.column
|
||||
suffixOffset = selectionRange.end.column - range.end.column
|
||||
|
||||
prefix = match[0][0...prefixOffset] if range.start.isLessThan(selectionRange.start)
|
||||
suffix = match[0][suffixOffset..] if range.end.isGreaterThan(selectionRange.end)
|
||||
|
||||
{prefix, suffix}
|
||||
|
||||
afterAttach: (onDom) ->
|
||||
if onDom
|
||||
widestCompletion = parseInt(@css('min-width')) or 0
|
||||
@list.find('span').each ->
|
||||
widestCompletion = Math.max(widestCompletion, $(this).outerWidth())
|
||||
@list.width(widestCompletion)
|
||||
@width(@list.outerWidth())
|
||||
|
||||
populateList: ->
|
||||
super
|
||||
|
||||
@setPosition()
|
@ -1,16 +0,0 @@
|
||||
AutocompleteView = require './autocomplete-view'
|
||||
|
||||
module.exports =
|
||||
autoCompleteViews: []
|
||||
editorSubscription: null
|
||||
|
||||
activate: ->
|
||||
@editorSubscription = rootView.eachEditor (editor) =>
|
||||
if editor.attached and not editor.mini
|
||||
@autoCompleteViews.push new AutocompleteView(editor)
|
||||
|
||||
deactivate: ->
|
||||
@editorSubscription?.off()
|
||||
@editorSubscription = null
|
||||
@autoCompleteViews.forEach (autoCompleteView) -> autoCompleteView.remove()
|
||||
@autoCompleteViews = []
|
@ -1,4 +0,0 @@
|
||||
'main': './lib/autocomplete'
|
||||
'description': 'Display possible completions from the current editor with `ctrl-space`.'
|
||||
'activationEvents':
|
||||
'autocomplete:attach': '.editor'
|
@ -1,456 +0,0 @@
|
||||
$ = require 'jquery'
|
||||
AutocompleteView = require 'autocomplete/lib/autocomplete-view'
|
||||
Autocomplete = require 'autocomplete/lib/autocomplete'
|
||||
Buffer = require 'text-buffer'
|
||||
Editor = require 'editor'
|
||||
RootView = require 'root-view'
|
||||
|
||||
describe "Autocomplete", ->
|
||||
beforeEach ->
|
||||
window.rootView = new RootView
|
||||
rootView.open('sample.js')
|
||||
rootView.simulateDomAttachment()
|
||||
|
||||
describe "@activate()", ->
|
||||
it "activates autocomplete on all existing and future editors (but not on autocomplete's own mini editor)", ->
|
||||
spyOn(AutocompleteView.prototype, 'initialize').andCallThrough()
|
||||
autocompletePackage = atom.activatePackage("autocomplete")
|
||||
expect(AutocompleteView.prototype.initialize).not.toHaveBeenCalled()
|
||||
|
||||
leftEditor = rootView.getActiveView()
|
||||
rightEditor = leftEditor.splitRight()
|
||||
|
||||
leftEditor.trigger 'autocomplete:attach'
|
||||
expect(leftEditor.find('.autocomplete')).toExist()
|
||||
expect(rightEditor.find('.autocomplete')).not.toExist()
|
||||
|
||||
expect(AutocompleteView.prototype.initialize).toHaveBeenCalled()
|
||||
|
||||
autoCompleteView = leftEditor.find('.autocomplete').view()
|
||||
autoCompleteView.trigger 'core:cancel'
|
||||
expect(leftEditor.find('.autocomplete')).not.toExist()
|
||||
|
||||
rightEditor.trigger 'autocomplete:attach'
|
||||
expect(rightEditor.find('.autocomplete')).toExist()
|
||||
|
||||
describe "@deactivate()", ->
|
||||
it "removes all autocomplete views and doesn't create new ones when new editors are opened", ->
|
||||
atom.activatePackage('autocomplete')
|
||||
rootView.getActiveView().trigger "autocomplete:attach"
|
||||
expect(rootView.getActiveView().find('.autocomplete')).toExist()
|
||||
atom.deactivatePackage('autocomplete')
|
||||
expect(rootView.getActiveView().find('.autocomplete')).not.toExist()
|
||||
rootView.getActiveView().splitRight()
|
||||
rootView.getActiveView().trigger "autocomplete:attach"
|
||||
expect(rootView.getActiveView().find('.autocomplete')).not.toExist()
|
||||
|
||||
describe "AutocompleteView", ->
|
||||
autocomplete = null
|
||||
editor = null
|
||||
miniEditor = null
|
||||
|
||||
beforeEach ->
|
||||
window.rootView = new RootView
|
||||
editor = new Editor(editSession: project.open('sample.js'))
|
||||
atom.activatePackage('autocomplete')
|
||||
autocomplete = new AutocompleteView(editor)
|
||||
miniEditor = autocomplete.miniEditor
|
||||
|
||||
describe 'autocomplete:attach event', ->
|
||||
it "shows autocomplete view and focuses its mini-editor", ->
|
||||
expect(editor.find('.autocomplete')).not.toExist()
|
||||
|
||||
editor.trigger "autocomplete:attach"
|
||||
expect(editor.find('.autocomplete')).toExist()
|
||||
expect(autocomplete.editor.isFocused).toBeFalsy()
|
||||
expect(autocomplete.miniEditor.isFocused).toBeTruthy()
|
||||
|
||||
describe "when no text is selected", ->
|
||||
it 'autocompletes word when there is only a prefix', ->
|
||||
editor.getBuffer().insert([10,0] ,"extra:s:extra")
|
||||
editor.setCursorBufferPosition([10,7])
|
||||
autocomplete.attach()
|
||||
|
||||
expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
|
||||
expect(editor.getCursorBufferPosition()).toEqual [10,11]
|
||||
expect(editor.getSelection().getBufferRange()).toEqual [[10,7], [10,11]]
|
||||
|
||||
expect(autocomplete.list.find('li').length).toBe 2
|
||||
expect(autocomplete.list.find('li:eq(0)')).toHaveText('shift')
|
||||
expect(autocomplete.list.find('li:eq(1)')).toHaveText('sort')
|
||||
|
||||
it 'autocompletes word when there is only a suffix', ->
|
||||
editor.getBuffer().insert([10,0] ,"extra:n:extra")
|
||||
editor.setCursorBufferPosition([10,6])
|
||||
autocomplete.attach()
|
||||
|
||||
expect(editor.lineForBufferRow(10)).toBe "extra:function:extra"
|
||||
expect(editor.getCursorBufferPosition()).toEqual [10,13]
|
||||
expect(editor.getSelection().getBufferRange()).toEqual [[10,6], [10,13]]
|
||||
|
||||
expect(autocomplete.list.find('li').length).toBe 2
|
||||
expect(autocomplete.list.find('li:eq(0)')).toHaveText('function')
|
||||
expect(autocomplete.list.find('li:eq(1)')).toHaveText('return')
|
||||
|
||||
it 'autocompletes word when there is a single prefix and suffix match', ->
|
||||
editor.getBuffer().insert([8,43] ,"q")
|
||||
editor.setCursorBufferPosition([8,44])
|
||||
autocomplete.attach()
|
||||
|
||||
expect(editor.lineForBufferRow(8)).toBe " return sort(left).concat(pivot).concat(quicksort(right));"
|
||||
expect(editor.getCursorBufferPosition()).toEqual [8,52]
|
||||
expect(editor.getSelection().getBufferRange().isEmpty()).toBeTruthy()
|
||||
|
||||
expect(autocomplete.list.find('li').length).toBe 0
|
||||
|
||||
it "shows all words there is no prefix or suffix", ->
|
||||
editor.setCursorBufferPosition([10, 0])
|
||||
autocomplete.attach()
|
||||
|
||||
expect(autocomplete.list.find('li:eq(0)')).toHaveText('0')
|
||||
expect(autocomplete.list.find('li:eq(1)')).toHaveText('1')
|
||||
expect(autocomplete.list.find('li').length).toBe 22
|
||||
|
||||
it "autocompletes word and replaces case of prefix with case of word", ->
|
||||
editor.getBuffer().insert([10,0] ,"extra:SO:extra")
|
||||
editor.setCursorBufferPosition([10,8])
|
||||
autocomplete.attach()
|
||||
|
||||
expect(editor.lineForBufferRow(10)).toBe "extra:sort:extra"
|
||||
expect(editor.getCursorBufferPosition()).toEqual [10,10]
|
||||
expect(editor.getSelection().isEmpty()).toBeTruthy()
|
||||
|
||||
describe "when text is selected", ->
|
||||
it 'autocompletes word when there is only a prefix', ->
|
||||
editor.getBuffer().insert([10,0] ,"extra:sort:extra")
|
||||
editor.setSelectedBufferRange [[10,7], [10,10]]
|
||||
autocomplete.attach()
|
||||
|
||||
expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
|
||||
expect(editor.getCursorBufferPosition()).toEqual [10,11]
|
||||
expect(editor.getSelection().getBufferRange().isEmpty()).toBeTruthy()
|
||||
|
||||
expect(autocomplete.list.find('li').length).toBe 0
|
||||
|
||||
it 'autocompletes word when there is only a suffix', ->
|
||||
editor.getBuffer().insert([10,0] ,"extra:current:extra")
|
||||
editor.setSelectedBufferRange [[10,6],[10,12]]
|
||||
autocomplete.attach()
|
||||
|
||||
expect(editor.lineForBufferRow(10)).toBe "extra:concat:extra"
|
||||
expect(editor.getCursorBufferPosition()).toEqual [10,11]
|
||||
expect(editor.getSelection().getBufferRange()).toEqual [[10,6],[10,11]]
|
||||
|
||||
expect(autocomplete.list.find('li').length).toBe 7
|
||||
expect(autocomplete.list.find('li:contains(current)')).not.toExist()
|
||||
|
||||
it 'autocompletes word when there is a prefix and suffix', ->
|
||||
editor.setSelectedBufferRange [[5,7],[5,12]]
|
||||
autocomplete.attach()
|
||||
|
||||
expect(editor.lineForBufferRow(5)).toBe " concat = items.shift();"
|
||||
expect(editor.getCursorBufferPosition()).toEqual [5,12]
|
||||
expect(editor.getSelection().getBufferRange().isEmpty()).toBeTruthy()
|
||||
|
||||
expect(autocomplete.list.find('li').length).toBe 0
|
||||
|
||||
it 'replaces selection with selected match, moves the cursor to the end of the match, and removes the autocomplete menu', ->
|
||||
editor.getBuffer().insert([10,0] ,"extra:sort:extra")
|
||||
editor.setSelectedBufferRange [[10,7], [10,9]]
|
||||
autocomplete.attach()
|
||||
|
||||
expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
|
||||
expect(editor.getCursorBufferPosition()).toEqual [10,11]
|
||||
expect(editor.getSelection().isEmpty()).toBeTruthy()
|
||||
expect(editor.find('.autocomplete')).not.toExist()
|
||||
|
||||
describe "when the editor is scrolled to the right", ->
|
||||
it "does not scroll it to the left", ->
|
||||
editor.width(300)
|
||||
editor.height(300)
|
||||
editor.attachToDom()
|
||||
editor.setCursorBufferPosition([6, 6])
|
||||
previousScrollLeft = editor.scrollLeft()
|
||||
autocomplete.attach()
|
||||
expect(editor.scrollLeft()).toBe previousScrollLeft
|
||||
|
||||
describe 'core:confirm event', ->
|
||||
describe "where there are matches", ->
|
||||
describe "where there is no selection", ->
|
||||
it "closes the menu and moves the cursor to the end", ->
|
||||
editor.getBuffer().insert([10,0] ,"extra:sh:extra")
|
||||
editor.setCursorBufferPosition([10,8])
|
||||
autocomplete.attach()
|
||||
|
||||
expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
|
||||
expect(editor.getCursorBufferPosition()).toEqual [10,11]
|
||||
expect(editor.getSelection().isEmpty()).toBeTruthy()
|
||||
expect(editor.find('.autocomplete')).not.toExist()
|
||||
|
||||
describe 'core:cancel event', ->
|
||||
describe "when there are no matches", ->
|
||||
it "closes the menu without changing the buffer", ->
|
||||
editor.getBuffer().insert([10,0] ,"xxx")
|
||||
editor.setCursorBufferPosition [10, 3]
|
||||
autocomplete.attach()
|
||||
expect(autocomplete.error).toHaveText "No matches found"
|
||||
|
||||
miniEditor.trigger "core:cancel"
|
||||
|
||||
expect(editor.lineForBufferRow(10)).toBe "xxx"
|
||||
expect(editor.getCursorBufferPosition()).toEqual [10,3]
|
||||
expect(editor.getSelection().isEmpty()).toBeTruthy()
|
||||
expect(editor.find('.autocomplete')).not.toExist()
|
||||
|
||||
it 'does not replace selection, removes autocomplete view and returns focus to editor', ->
|
||||
editor.getBuffer().insert([10,0] ,"extra:so:extra")
|
||||
editor.setSelectedBufferRange [[10,7], [10,8]]
|
||||
originalSelectionBufferRange = editor.getSelection().getBufferRange()
|
||||
|
||||
autocomplete.attach()
|
||||
editor.setCursorBufferPosition [0, 0] # even if selection changes before cancel, it should work
|
||||
miniEditor.trigger "core:cancel"
|
||||
|
||||
expect(editor.lineForBufferRow(10)).toBe "extra:so:extra"
|
||||
expect(editor.getSelection().getBufferRange()).toEqual originalSelectionBufferRange
|
||||
expect(editor.find('.autocomplete')).not.toExist()
|
||||
|
||||
it "does not clear out a previously confirmed selection when canceling with an empty list", ->
|
||||
editor.getBuffer().insert([10, 0], "ort\n")
|
||||
editor.setCursorBufferPosition([10, 0])
|
||||
|
||||
autocomplete.attach()
|
||||
miniEditor.trigger 'core:confirm'
|
||||
expect(editor.lineForBufferRow(10)).toBe 'quicksort'
|
||||
|
||||
editor.setCursorBufferPosition([11, 0])
|
||||
autocomplete.attach()
|
||||
miniEditor.trigger 'core:cancel'
|
||||
expect(editor.lineForBufferRow(10)).toBe 'quicksort'
|
||||
|
||||
it "restores the case of the prefix to the original value", ->
|
||||
editor.getBuffer().insert([10,0] ,"extra:S:extra")
|
||||
editor.setCursorBufferPosition([10,7])
|
||||
autocomplete.attach()
|
||||
|
||||
expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
|
||||
expect(editor.getCursorBufferPosition()).toEqual [10,11]
|
||||
autocomplete.trigger 'core:cancel'
|
||||
expect(editor.lineForBufferRow(10)).toBe "extra:S:extra"
|
||||
expect(editor.getCursorBufferPosition()).toEqual [10,7]
|
||||
|
||||
it "restores the original buffer contents even if there was an additional operation after autocomplete attached (regression)", ->
|
||||
editor.getBuffer().insert([10,0] ,"extra:s:extra")
|
||||
editor.setCursorBufferPosition([10,7])
|
||||
autocomplete.attach()
|
||||
|
||||
editor.getBuffer().append('hi')
|
||||
expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
|
||||
autocomplete.trigger 'core:cancel'
|
||||
expect(editor.lineForBufferRow(10)).toBe "extra:s:extra"
|
||||
|
||||
editor.redo()
|
||||
expect(editor.lineForBufferRow(10)).toBe "extra:s:extra"
|
||||
|
||||
describe 'move-up event', ->
|
||||
it "highlights the previous match and replaces the selection with it", ->
|
||||
editor.getBuffer().insert([10,0] ,"extra:t:extra")
|
||||
editor.setCursorBufferPosition([10,6])
|
||||
autocomplete.attach()
|
||||
|
||||
miniEditor.trigger "core:move-up"
|
||||
expect(editor.lineForBufferRow(10)).toBe "extra:sort: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')
|
||||
|
||||
miniEditor.trigger "core:move-up"
|
||||
expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
|
||||
expect(autocomplete.find('li:eq(0)')).not.toHaveClass('selected')
|
||||
expect(autocomplete.find('li:eq(7)')).not.toHaveClass('selected')
|
||||
expect(autocomplete.find('li:eq(6)')).toHaveClass('selected')
|
||||
|
||||
describe 'move-down event', ->
|
||||
it "highlights the next match and replaces the selection with it", ->
|
||||
editor.getBuffer().insert([10,0] ,"extra:s:extra")
|
||||
editor.setCursorBufferPosition([10,7])
|
||||
autocomplete.attach()
|
||||
|
||||
miniEditor.trigger "core:move-down"
|
||||
expect(editor.lineForBufferRow(10)).toBe "extra:sort:extra"
|
||||
expect(autocomplete.find('li:eq(0)')).not.toHaveClass('selected')
|
||||
expect(autocomplete.find('li:eq(1)')).toHaveClass('selected')
|
||||
|
||||
miniEditor.trigger "core:move-down"
|
||||
expect(editor.lineForBufferRow(10)).toBe "extra:shift:extra"
|
||||
expect(autocomplete.find('li:eq(0)')).toHaveClass('selected')
|
||||
expect(autocomplete.find('li:eq(1)')).not.toHaveClass('selected')
|
||||
|
||||
describe "when a match is clicked in the match list", ->
|
||||
it "selects and confirms the match", ->
|
||||
editor.getBuffer().insert([10,0] ,"t")
|
||||
editor.setCursorBufferPosition([10, 0])
|
||||
autocomplete.attach()
|
||||
|
||||
matchToSelect = autocomplete.list.find('li:eq(1)')
|
||||
matchToSelect.mousedown()
|
||||
expect(matchToSelect).toMatchSelector('.selected')
|
||||
matchToSelect.mouseup()
|
||||
|
||||
expect(autocomplete.parent()).not.toExist()
|
||||
expect(editor.lineForBufferRow(10)).toBe matchToSelect.text()
|
||||
|
||||
describe "when the mini-editor receives keyboard input", ->
|
||||
beforeEach ->
|
||||
editor.attachToDom()
|
||||
|
||||
describe "when text is removed from the mini-editor", ->
|
||||
it "reloads the match list based on the mini-editor's text", ->
|
||||
editor.getBuffer().insert([10,0] ,"t")
|
||||
editor.setCursorBufferPosition([10,0])
|
||||
autocomplete.attach()
|
||||
|
||||
expect(autocomplete.list.find('li').length).toBe 8
|
||||
miniEditor.textInput('c')
|
||||
window.advanceClock(autocomplete.inputThrottle)
|
||||
expect(autocomplete.list.find('li').length).toBe 3
|
||||
miniEditor.backspace()
|
||||
window.advanceClock(autocomplete.inputThrottle)
|
||||
expect(autocomplete.list.find('li').length).toBe 8
|
||||
|
||||
describe "when the text contains only word characters", ->
|
||||
it "narrows the list of completions with the fuzzy match algorithm", ->
|
||||
editor.getBuffer().insert([10,0] ,"t")
|
||||
editor.setCursorBufferPosition([10,0])
|
||||
autocomplete.attach()
|
||||
|
||||
expect(autocomplete.list.find('li').length).toBe 8
|
||||
miniEditor.textInput('i')
|
||||
window.advanceClock(autocomplete.inputThrottle)
|
||||
expect(autocomplete.list.find('li').length).toBe 4
|
||||
expect(autocomplete.list.find('li:eq(0)')).toHaveText 'pivot'
|
||||
expect(autocomplete.list.find('li:eq(0)')).toHaveClass 'selected'
|
||||
expect(autocomplete.list.find('li:eq(1)')).toHaveText 'right'
|
||||
expect(autocomplete.list.find('li:eq(2)')).toHaveText 'shift'
|
||||
expect(autocomplete.list.find('li:eq(3)')).toHaveText 'quicksort'
|
||||
expect(editor.lineForBufferRow(10)).toEqual 'pivot'
|
||||
|
||||
miniEditor.textInput('o')
|
||||
window.advanceClock(autocomplete.inputThrottle)
|
||||
expect(autocomplete.list.find('li').length).toBe 2
|
||||
expect(autocomplete.list.find('li:eq(0)')).toHaveText 'pivot'
|
||||
expect(autocomplete.list.find('li:eq(1)')).toHaveText 'quicksort'
|
||||
|
||||
describe "when a non-word character is typed in the mini-editor", ->
|
||||
it "immediately confirms the current completion choice and inserts that character into the buffer", ->
|
||||
editor.getBuffer().insert([10,0] ,"t")
|
||||
editor.setCursorBufferPosition([10,0])
|
||||
autocomplete.attach()
|
||||
|
||||
miniEditor.textInput('iv')
|
||||
window.advanceClock(autocomplete.inputThrottle)
|
||||
expect(autocomplete.list.find('li:eq(0)')).toHaveText 'pivot'
|
||||
|
||||
miniEditor.textInput(' ')
|
||||
window.advanceClock(autocomplete.inputThrottle)
|
||||
expect(autocomplete.parent()).not.toExist()
|
||||
expect(editor.lineForBufferRow(10)).toEqual 'pivot '
|
||||
|
||||
describe 'when the mini-editor loses focus before the selection is confirmed', ->
|
||||
it "cancels the autocomplete", ->
|
||||
editor.attachToDom()
|
||||
autocomplete.attach()
|
||||
spyOn(autocomplete, "cancel")
|
||||
|
||||
editor.focus()
|
||||
|
||||
expect(autocomplete.cancel).toHaveBeenCalled()
|
||||
|
||||
describe ".attach()", ->
|
||||
beforeEach ->
|
||||
editor.attachToDom()
|
||||
setEditorHeightInLines(editor, 13)
|
||||
editor.resetDisplay() # Ensures the editor only has 13 lines visible
|
||||
|
||||
describe "when the autocomplete view fits below the cursor", ->
|
||||
it "adds the autocomplete view to the editor below the cursor", ->
|
||||
editor.setCursorBufferPosition [1, 2]
|
||||
cursorPixelPosition = editor.pixelPositionForScreenPosition(editor.getCursorScreenPosition())
|
||||
autocomplete.attach()
|
||||
expect(editor.find('.autocomplete')).toExist()
|
||||
|
||||
expect(autocomplete.position().top).toBe cursorPixelPosition.top + editor.lineHeight
|
||||
expect(autocomplete.position().left).toBe cursorPixelPosition.left
|
||||
|
||||
describe "when the autocomplete view does not fit below the cursor", ->
|
||||
it "adds the autocomplete view to the editor above the cursor", ->
|
||||
editor.setCursorScreenPosition([11, 0])
|
||||
editor.insertText('t ')
|
||||
editor.setCursorScreenPosition([11, 0])
|
||||
cursorPixelPosition = editor.pixelPositionForScreenPosition(editor.getCursorScreenPosition())
|
||||
autocomplete.attach()
|
||||
|
||||
expect(autocomplete.parent()).toExist()
|
||||
autocompleteBottom = autocomplete.position().top + autocomplete.outerHeight()
|
||||
expect(autocompleteBottom).toBe cursorPixelPosition.top
|
||||
expect(autocomplete.position().left).toBe cursorPixelPosition.left
|
||||
|
||||
it "updates the position when the list is filtered and the height of the list decreases", ->
|
||||
editor.setCursorScreenPosition([11, 0])
|
||||
editor.insertText('s')
|
||||
editor.setCursorScreenPosition([11, 0])
|
||||
cursorPixelPosition = editor.pixelPositionForScreenPosition(editor.getCursorScreenPosition())
|
||||
autocomplete.attach()
|
||||
|
||||
expect(autocomplete.parent()).toExist()
|
||||
autocompleteBottom = autocomplete.position().top + autocomplete.outerHeight()
|
||||
expect(autocompleteBottom).toBe cursorPixelPosition.top
|
||||
expect(autocomplete.position().left).toBe cursorPixelPosition.left
|
||||
|
||||
miniEditor.textInput('sh')
|
||||
window.advanceClock(autocomplete.inputThrottle)
|
||||
|
||||
expect(autocomplete.parent()).toExist()
|
||||
autocompleteBottom = autocomplete.position().top + autocomplete.outerHeight()
|
||||
expect(autocompleteBottom).toBe cursorPixelPosition.top
|
||||
expect(autocomplete.position().left).toBe cursorPixelPosition.left
|
||||
|
||||
describe ".cancel()", ->
|
||||
it "clears the mini-editor and unbinds autocomplete event handlers for move-up and move-down", ->
|
||||
autocomplete.attach()
|
||||
miniEditor.setText('foo')
|
||||
|
||||
autocomplete.cancel()
|
||||
expect(miniEditor.getText()).toBe ''
|
||||
|
||||
editor.trigger 'core:move-down'
|
||||
expect(editor.getCursorBufferPosition().row).toBe 1
|
||||
|
||||
editor.trigger 'core:move-up'
|
||||
expect(editor.getCursorBufferPosition().row).toBe 0
|
||||
|
||||
it "sets the width of the view to be wide enough to contain the longest completion without scrolling", ->
|
||||
editor.attachToDom()
|
||||
editor.insertText('thisIsAReallyReallyReallyLongCompletion ')
|
||||
editor.moveCursorToBottom()
|
||||
editor.insertNewline()
|
||||
editor.insertText('t')
|
||||
autocomplete.attach()
|
||||
expect(autocomplete.list.prop('scrollWidth')).toBe autocomplete.list.width()
|
||||
|
||||
it "includes completions for the scope's completion preferences", ->
|
||||
atom.activatePackage('css-tmbundle', sync: true)
|
||||
cssEditor = new Editor(editSession: project.open('css.css'))
|
||||
autocomplete = new AutocompleteView(cssEditor)
|
||||
|
||||
cssEditor.attachToDom()
|
||||
cssEditor.moveCursorToEndOfLine()
|
||||
cssEditor.insertText(' out')
|
||||
cssEditor.moveCursorToEndOfLine()
|
||||
|
||||
autocomplete.attach()
|
||||
expect(autocomplete.list.find('li').length).toBe 4
|
||||
expect(autocomplete.list.find('li:eq(0)')).toHaveText 'outline'
|
||||
expect(autocomplete.list.find('li:eq(1)')).toHaveText 'outline-color'
|
||||
expect(autocomplete.list.find('li:eq(2)')).toHaveText 'outline-style'
|
||||
expect(autocomplete.list.find('li:eq(3)')).toHaveText 'outline-width'
|
@ -1,26 +0,0 @@
|
||||
.autocomplete {
|
||||
&.select-list {
|
||||
box-sizing: content-box;
|
||||
margin-left: 0;
|
||||
|
||||
ol li {
|
||||
padding: 5px 0 5px 0;
|
||||
}
|
||||
}
|
||||
|
||||
ol {
|
||||
box-sizing: content-box;
|
||||
position: relative;
|
||||
overflow-y: scroll;
|
||||
max-height: 200px;
|
||||
}
|
||||
|
||||
span {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
margin: 2px;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user