mirror of
https://github.com/atom/atom.git
synced 2026-02-15 17:15:24 -05:00
Merge master
This commit is contained in:
@@ -75,6 +75,7 @@ class Project
|
||||
fs.absolute filePath
|
||||
|
||||
relativize: (fullPath) ->
|
||||
return fullPath unless fullPath.lastIndexOf(@getPath()) is 0
|
||||
fullPath.replace(@getPath(), "").replace(/^\//, '')
|
||||
|
||||
getSoftTabs: -> @softTabs
|
||||
|
||||
@@ -45,7 +45,9 @@ class SelectList extends View
|
||||
|
||||
schedulePopulateList: ->
|
||||
clearTimeout(@scheduleTimeout)
|
||||
@scheduleTimeout = setTimeout((=> @populateList()), @inputThrottle)
|
||||
populateCallback = =>
|
||||
@populateList() if @isOnDom()
|
||||
@scheduleTimeout = setTimeout(populateCallback, @inputThrottle)
|
||||
|
||||
setArray: (@array) ->
|
||||
@populateList()
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
{View} = require 'space-pen'
|
||||
$ = require 'jquery'
|
||||
|
||||
module.exports =
|
||||
class SortableList extends View
|
||||
@viewClass: -> 'sortable-list'
|
||||
|
||||
initialize: ->
|
||||
@on 'dragstart', '.sortable', @onDragStart
|
||||
@on 'dragend', '.sortable', @onDragEnd
|
||||
@on 'dragover', '.sortable', @onDragOver
|
||||
@on 'dragenter', '.sortable', @onDragEnter
|
||||
@on 'dragleave', '.sortable', @onDragLeave
|
||||
@on 'drop', '.sortable', @onDrop
|
||||
|
||||
onDragStart: (event) =>
|
||||
unless @shouldAllowDrag(event)
|
||||
event.preventDefault()
|
||||
return
|
||||
|
||||
el = @getSortableElement(event)
|
||||
el.addClass 'is-dragging'
|
||||
event.originalEvent.dataTransfer.setData 'sortable-index', el.index()
|
||||
|
||||
onDragEnd: (event) =>
|
||||
@getSortableElement(event).removeClass 'is-dragging'
|
||||
|
||||
onDragEnter: (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
onDragOver: (event) =>
|
||||
event.preventDefault()
|
||||
@getSortableElement(event).addClass 'is-drop-target'
|
||||
|
||||
onDragLeave: (event) =>
|
||||
@getSortableElement(event).removeClass 'is-drop-target'
|
||||
|
||||
onDrop: (event) =>
|
||||
return false if !@shouldAllowDrop(event)
|
||||
event.stopPropagation()
|
||||
@find('.is-drop-target').removeClass 'is-drop-target'
|
||||
|
||||
shouldAllowDrag: (event) ->
|
||||
true
|
||||
|
||||
shouldAllowDrop: (event) ->
|
||||
true
|
||||
|
||||
getDroppedElement: (event) ->
|
||||
index = event.originalEvent.dataTransfer.getData('sortable-index')
|
||||
@find(".sortable:eq(#{index})")
|
||||
|
||||
getSortableElement: (event) ->
|
||||
$(event.target).closest('.sortable')
|
||||
@@ -288,6 +288,9 @@ describe "AutocompleteView", ->
|
||||
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")
|
||||
|
||||
@@ -39,7 +39,7 @@ class FuzzyFinderView extends SelectList
|
||||
$$ ->
|
||||
@li =>
|
||||
if git?
|
||||
status = git.statuses[project.resolve(path)]
|
||||
status = git.statuses[path]
|
||||
if git.isStatusNew(status)
|
||||
@div class: 'status new'
|
||||
else if git.isStatusModified(status)
|
||||
@@ -60,7 +60,7 @@ class FuzzyFinderView extends SelectList
|
||||
typeClass = 'text-name'
|
||||
|
||||
@span fs.base(path), class: "file label #{typeClass}"
|
||||
if folder = fs.directory(project.relativize(path))
|
||||
if folder = project.relativize(fs.directory(path))
|
||||
@span " - #{folder}/", class: 'directory'
|
||||
|
||||
openPath: (path) ->
|
||||
@@ -76,7 +76,7 @@ class FuzzyFinderView extends SelectList
|
||||
|
||||
confirmed : (path) ->
|
||||
return unless path.length
|
||||
if fs.isFile(project.resolve(path))
|
||||
if fs.isFile(path)
|
||||
@cancel()
|
||||
@openPath(path)
|
||||
else
|
||||
@@ -133,11 +133,10 @@ class FuzzyFinderView extends SelectList
|
||||
@miniEditor.setText(currentWord)
|
||||
|
||||
populateGitStatusPaths: ->
|
||||
projectRelativePaths = []
|
||||
for path, status of git.statuses
|
||||
continue unless fs.isFile(path)
|
||||
projectRelativePaths.push(project.relativize(path))
|
||||
@setArray(projectRelativePaths)
|
||||
paths = []
|
||||
paths.push(path) for path, status of git.statuses when fs.isFile(path)
|
||||
|
||||
@setArray(paths)
|
||||
|
||||
populateProjectPaths: (options = {}) ->
|
||||
if @projectPaths?
|
||||
@@ -147,7 +146,6 @@ class FuzzyFinderView extends SelectList
|
||||
path.indexOf(options.filter) >= 0
|
||||
else
|
||||
@projectPaths
|
||||
|
||||
@setArray(listedItems)
|
||||
options.done(listedItems) if options.done?
|
||||
else
|
||||
@@ -165,8 +163,6 @@ class FuzzyFinderView extends SelectList
|
||||
populateOpenBufferPaths: ->
|
||||
editSessions = project.getEditSessions().filter (editSession) ->
|
||||
editSession.getPath()?
|
||||
editSessions = _.uniq editSessions, (editSession) ->
|
||||
editSession.getPath()
|
||||
|
||||
editSessions = _.sortBy editSessions, (editSession) =>
|
||||
if editSession is rootView.getActivePaneItem()
|
||||
@@ -174,17 +170,10 @@ class FuzzyFinderView extends SelectList
|
||||
else
|
||||
-(editSession.lastOpened or 1)
|
||||
|
||||
@paths = _.map editSessions, (editSession) ->
|
||||
project.relativize editSession.getPath()
|
||||
@paths = []
|
||||
@paths.push(editSession.getPath()) for editSession in editSessions
|
||||
|
||||
@setArray(@paths)
|
||||
|
||||
getOpenedPaths: ->
|
||||
paths = {}
|
||||
for editSession in project.getEditSessions()
|
||||
path = editSession.getPath()
|
||||
paths[path] = editSession.lastOpened if path?
|
||||
paths
|
||||
@setArray(_.uniq(@paths))
|
||||
|
||||
detach: ->
|
||||
super
|
||||
|
||||
@@ -31,7 +31,12 @@ module.exports =
|
||||
@projectPaths = null
|
||||
|
||||
serialize: ->
|
||||
@fuzzyFinderView?.getOpenedPaths()
|
||||
if @fuzzyFinderView?
|
||||
paths = {}
|
||||
for editSession in project.getEditSessions()
|
||||
path = editSession.getPath()
|
||||
paths[path] = editSession.lastOpened if path?
|
||||
paths
|
||||
|
||||
createView: ->
|
||||
unless @fuzzyFinderView
|
||||
|
||||
@@ -77,8 +77,8 @@ describe 'FuzzyFinder', ->
|
||||
expect(rootView.getActiveView()).toBe editor2
|
||||
rootView.trigger 'fuzzy-finder:toggle-file-finder'
|
||||
|
||||
finderView.confirmed('dir/a')
|
||||
expectedPath = project.resolve('dir/a')
|
||||
finderView.confirmed(expectedPath)
|
||||
|
||||
expect(finderView.hasParent()).toBeFalsy()
|
||||
expect(editor1.getPath()).not.toBe expectedPath
|
||||
@@ -144,6 +144,7 @@ describe 'FuzzyFinder', ->
|
||||
|
||||
atom.deactivatePackage('fuzzy-finder')
|
||||
states = _.map atom.getPackageState('fuzzy-finder'), (path, time) -> [ path, time ]
|
||||
expect(states.length).toBe 3
|
||||
states = _.sortBy states, (path, time) -> -time
|
||||
|
||||
paths = [ 'sample-with-tabs.coffee', 'sample.txt', 'sample.js' ]
|
||||
@@ -187,7 +188,7 @@ describe 'FuzzyFinder', ->
|
||||
describe "when the active pane has an item for the selected path", ->
|
||||
it "switches to the item for the selected path", ->
|
||||
expectedPath = project.resolve('sample.txt')
|
||||
finderView.confirmed('sample.txt')
|
||||
finderView.confirmed(expectedPath)
|
||||
|
||||
expect(finderView.hasParent()).toBeFalsy()
|
||||
expect(editor1.getPath()).not.toBe expectedPath
|
||||
@@ -203,7 +204,7 @@ describe 'FuzzyFinder', ->
|
||||
expect(rootView.getActiveView()).toBe editor1
|
||||
|
||||
expectedPath = project.resolve('sample.txt')
|
||||
finderView.confirmed('sample.txt')
|
||||
finderView.confirmed(expectedPath)
|
||||
|
||||
expect(finderView.hasParent()).toBeFalsy()
|
||||
expect(editor1.getPath()).toBe expectedPath
|
||||
@@ -372,7 +373,7 @@ describe 'FuzzyFinder', ->
|
||||
|
||||
runs ->
|
||||
expect(finderView).not.toBeVisible()
|
||||
expect(openedPath).toBe "#{project.getPath()}/sample.txt"
|
||||
expect(openedPath).toBe project.resolve("sample.txt")
|
||||
|
||||
it "displays an error when the word under the cursor doesn't match any files", ->
|
||||
editor.setText("moogoogaipan")
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
'.source.gfm':
|
||||
'editor':
|
||||
'increaseIndentPattern': '^\\s*([\\*\\+-])[ \\t]+'
|
||||
@@ -132,10 +132,3 @@ describe "GitHub Flavored Markdown grammar", ->
|
||||
{tokens} = grammar.tokenizeLine("> Quotation")
|
||||
expect(tokens[0]).toEqual value: ">", scopes: ["source.gfm", "support.quote.gfm"]
|
||||
expect(tokens[1]).toEqual value: " Quotation", scopes: ["source.gfm", "comment.quote.gfm"]
|
||||
|
||||
describe "auto indent", ->
|
||||
it "indents newlines entered after list lines", ->
|
||||
config.set('editor.autoIndent', true)
|
||||
editSession = project.buildEditSession('gfm.md')
|
||||
editSession.insertNewlineBelow()
|
||||
expect(editSession.buffer.lineForRow(1)).toBe ' '
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
'main': './lib/strip-trailing-whitespace'
|
||||
@@ -1,15 +1,18 @@
|
||||
$ = require 'jquery'
|
||||
{View} = require 'space-pen'
|
||||
_ = require 'underscore'
|
||||
SortableList = require 'sortable-list'
|
||||
TabView = require './tab-view'
|
||||
|
||||
module.exports =
|
||||
class TabBarView extends SortableList
|
||||
class TabBarView extends View
|
||||
@content: ->
|
||||
@ul class: "tabs #{@viewClass()}"
|
||||
@ul class: "tabs sortable-list"
|
||||
|
||||
initialize: (@pane) ->
|
||||
super
|
||||
@on 'dragstart', '.sortable', @onDragStart
|
||||
@on 'dragend', '.sortable', @onDragEnd
|
||||
@on 'dragover', @onDragOver
|
||||
@on 'drop', @onDrop
|
||||
|
||||
@paneContainer = @pane.getContainer()
|
||||
@addTabForItem(item) for item in @pane.getItems()
|
||||
@@ -74,27 +77,66 @@ class TabBarView extends SortableList
|
||||
(@paneContainer.getPanes().length > 1) or (@pane.getItems().length > 1)
|
||||
|
||||
onDragStart: (event) =>
|
||||
super
|
||||
unless @shouldAllowDrag(event)
|
||||
event.preventDefault()
|
||||
return
|
||||
|
||||
el = $(event.target).closest('.sortable')
|
||||
el.addClass 'is-dragging'
|
||||
event.originalEvent.dataTransfer.setData 'sortable-index', el.index()
|
||||
|
||||
pane = $(event.target).closest('.pane')
|
||||
paneIndex = @paneContainer.indexOfPane(pane)
|
||||
event.originalEvent.dataTransfer.setData 'from-pane-index', paneIndex
|
||||
|
||||
onDragEnd: (event) =>
|
||||
@find(".is-dragging").removeClass 'is-dragging'
|
||||
|
||||
onDragOver: (event) =>
|
||||
event.preventDefault()
|
||||
currentDropTargetIndex = @find(".is-drop-target").index()
|
||||
newDropTargetIndex = @getDropTargetIndex(event)
|
||||
|
||||
if newDropTargetIndex != currentDropTargetIndex
|
||||
@children().removeClass 'is-drop-target drop-target-is-after'
|
||||
sortableObjects = @find(".sortable")
|
||||
if newDropTargetIndex < sortableObjects.length
|
||||
sortableObjects.eq(newDropTargetIndex).addClass 'is-drop-target'
|
||||
else
|
||||
sortableObjects.eq(newDropTargetIndex - 1).addClass 'drop-target-is-after'
|
||||
|
||||
|
||||
onDrop: (event) =>
|
||||
super
|
||||
event.stopPropagation()
|
||||
@children('.is-drop-target').removeClass 'is-drop-target'
|
||||
@children('.drop-target-is-after').removeClass 'drop-target-is-after'
|
||||
|
||||
dataTransfer = event.originalEvent.dataTransfer
|
||||
fromIndex = parseInt(dataTransfer.getData('sortable-index'))
|
||||
fromPaneIndex = parseInt(dataTransfer.getData('from-pane-index'))
|
||||
fromPane = @paneContainer.paneAtIndex(fromPaneIndex)
|
||||
toIndex = @getSortableElement(event).index()
|
||||
toIndex = @getDropTargetIndex(event)
|
||||
toPane = $(event.target).closest('.pane').view()
|
||||
draggedTab = fromPane.find(".tabs .sortable:eq(#{fromIndex})").view()
|
||||
item = draggedTab.item
|
||||
|
||||
if toPane is fromPane
|
||||
toIndex++ if fromIndex > toIndex
|
||||
toIndex-- if fromIndex < toIndex
|
||||
toPane.moveItem(item, toIndex)
|
||||
else
|
||||
fromPane.moveItemToPane(item, toPane, toIndex)
|
||||
fromPane.moveItemToPane(item, toPane, toIndex--)
|
||||
toPane.showItem(item)
|
||||
toPane.focus()
|
||||
|
||||
getDropTargetIndex: (event) ->
|
||||
el = $(event.target).closest('.sortable')
|
||||
el = $(event.target).find('.sortable').last() if el.length == 0
|
||||
|
||||
elementCenter = el.offset().left + el.width() / 2
|
||||
|
||||
if event.originalEvent.pageX < elementCenter
|
||||
el.index()
|
||||
else if el.next().length > 0
|
||||
el.next().index()
|
||||
else
|
||||
el.index() + 1
|
||||
|
||||
@@ -235,6 +235,20 @@ describe "TabBarView", ->
|
||||
expect(pane.activeItem).toBe item1
|
||||
expect(pane.focus).toHaveBeenCalled()
|
||||
|
||||
describe "when it is dropped on the tab bar", ->
|
||||
it "moves the tab and its item to the end", ->
|
||||
expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["Item 1", "sample.js", "Item 2"]
|
||||
expect(pane.getItems()).toEqual [item1, editSession1, item2]
|
||||
expect(pane.activeItem).toBe item2
|
||||
spyOn(pane, 'focus')
|
||||
|
||||
[dragStartEvent, dropEvent] = buildDragEvents(tabBar.tabAtIndex(0), tabBar)
|
||||
tabBar.onDragStart(dragStartEvent)
|
||||
tabBar.onDrop(dropEvent)
|
||||
|
||||
expect(tabBar.getTabs().map (tab) -> tab.text()).toEqual ["sample.js", "Item 2", "Item 1"]
|
||||
expect(pane.getItems()).toEqual [editSession1, item2, item1]
|
||||
|
||||
describe "when a tab is dragged to a different pane", ->
|
||||
[pane2, tabBar2, item2b] = []
|
||||
|
||||
|
||||
@@ -83,11 +83,10 @@
|
||||
|
||||
}
|
||||
|
||||
.tab.is-drop-target:after {
|
||||
.tab.is-drop-target:before, .tab.drop-target-is-after:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: -2px;
|
||||
content: "";
|
||||
z-index: 999;
|
||||
display: inline-block;
|
||||
width: 2px;
|
||||
@@ -96,15 +95,30 @@
|
||||
background: #0098ff;
|
||||
}
|
||||
|
||||
.tab.is-drop-target:before {
|
||||
.tab.is-drop-target:after, .tab.drop-target-is-after:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
z-index: 9999;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background: #0098ff;
|
||||
right: -4px;
|
||||
top: 30px;
|
||||
border-radius: 4px;
|
||||
z-index: 9999;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.tab.is-drop-target:before {
|
||||
left: -2px;
|
||||
}
|
||||
|
||||
.tab.is-drop-target:after {
|
||||
left: -4px;
|
||||
}
|
||||
|
||||
.tab.drop-target-is-after:before {
|
||||
right: -4px;
|
||||
}
|
||||
|
||||
.tab.drop-target-is-after:after {
|
||||
right: -2px;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
module.exports =
|
||||
activate: ->
|
||||
rootView.eachBuffer (buffer) => @stripTrailingWhitespaceBeforeSave(buffer)
|
||||
rootView.eachBuffer (buffer) => @whitespaceBeforeSave(buffer)
|
||||
|
||||
stripTrailingWhitespaceBeforeSave: (buffer) ->
|
||||
configDefaults:
|
||||
ensureSingleTrailingNewline: true
|
||||
|
||||
whitespaceBeforeSave: (buffer) ->
|
||||
buffer.on 'will-be-saved', ->
|
||||
buffer.transact ->
|
||||
buffer.scan /[ \t]+$/g, (match, range, { replace }) ->
|
||||
replace('')
|
||||
if config.get('stripTrailingWhitespace.singleTrailingNewline')
|
||||
buffer.scan /[ \t]+$/g, (match, range, { replace }) -> replace('')
|
||||
|
||||
if config.get('whitespace.ensureSingleTrailingNewline')
|
||||
if buffer.getLastLine() is ''
|
||||
row = buffer.getLastRow() - 1
|
||||
while row and buffer.lineForRow(row) is ''
|
||||
1
src/packages/whitespace/package.cson
Normal file
1
src/packages/whitespace/package.cson
Normal file
@@ -0,0 +1 @@
|
||||
'main': './lib/whitespace'
|
||||
@@ -1,7 +1,7 @@
|
||||
RootView = require 'root-view'
|
||||
fs = require 'fs-utils'
|
||||
|
||||
describe "StripTrailingWhitespace", ->
|
||||
describe "Whitespace", ->
|
||||
[editor, path] = []
|
||||
|
||||
beforeEach ->
|
||||
@@ -10,17 +10,20 @@ describe "StripTrailingWhitespace", ->
|
||||
window.rootView = new RootView
|
||||
rootView.open(path)
|
||||
|
||||
atom.activatePackage('strip-trailing-whitespace')
|
||||
atom.activatePackage('whitespace')
|
||||
|
||||
rootView.focus()
|
||||
editor = rootView.getActiveView()
|
||||
|
||||
afterEach ->
|
||||
fs.remove(path) if fs.exists(path)
|
||||
rootView.remove()
|
||||
|
||||
it "strips trailing whitespace before an editor saves a buffer", ->
|
||||
spyOn(fs, 'write')
|
||||
|
||||
config.set("whitespace.ensureSingleTrailingNewline", false)
|
||||
config.update()
|
||||
|
||||
# works for buffers that are already open when extension is initialized
|
||||
editor.insertText("foo \nbar\t \n\nbaz")
|
||||
editor.getBuffer().save()
|
||||
@@ -34,15 +37,14 @@ describe "StripTrailingWhitespace", ->
|
||||
editor.getBuffer().save()
|
||||
expect(editor.getText()).toBe 'Some text.\n'
|
||||
|
||||
describe "stripTrailingWhitespace.singleTrailingNewline config", ->
|
||||
describe "whitespace.ensureSingleTrailingNewline config", ->
|
||||
[originalConfigValue] = []
|
||||
beforeEach ->
|
||||
originalConfigValue = config.get("stripTrailingWhitespace.singleTrailingNewline")
|
||||
config.set("stripTrailingWhitespace.singleTrailingNewline", true)
|
||||
config.update()
|
||||
originalConfigValue = config.get("whitespace.ensureSingleTrailingNewline")
|
||||
expect(originalConfigValue).toBe true
|
||||
|
||||
afterEach ->
|
||||
config.set("stripTrailingWhitespace.singleTrailingNewline", originalConfigValue)
|
||||
config.set("whitespace.ensureSingleTrailingNewline", originalConfigValue)
|
||||
config.update()
|
||||
|
||||
it "adds a trailing newline when there is no trailing newline", ->
|
||||
@@ -69,3 +71,12 @@ describe "StripTrailingWhitespace", ->
|
||||
editor.insertText "\n"
|
||||
editor.getBuffer().save()
|
||||
expect(editor.getText()).toBe "\n"
|
||||
|
||||
it "does not add trailing newline if ensureSingleTrailingNewline is false", ->
|
||||
config.set("whitespace.ensureSingleTrailingNewline", false)
|
||||
config.update()
|
||||
|
||||
editor.insertText "no trailing newline"
|
||||
editor.getBuffer().save()
|
||||
expect(editor.getText()).toBe "no trailing newline"
|
||||
|
||||
Reference in New Issue
Block a user