Merge remote-tracking branch 'origin/master' into vim-core-changes

Conflicts:
	src/app/pane.coffee
This commit is contained in:
Mutwin Kraus
2013-04-09 18:52:36 +02:00
34 changed files with 571 additions and 85 deletions

View File

@@ -2406,3 +2406,13 @@ describe "EditSession", ->
editSession.joinLine()
expect(editSession.lineForBufferRow(9)).toBe ' }; return sort(Array.apply(this, arguments)); };'
expect(editSession.getSelectedBufferRange()).toEqual [[9, 3], [9, 49]]
describe ".shouldPromptToSave()", ->
it "returns false when an edit session's buffer is in use by more than one session", ->
expect(editSession.shouldPromptToSave()).toBeFalsy()
buffer.setText('changed')
expect(editSession.shouldPromptToSave()).toBeTruthy()
editSession2 = project.buildEditSession('sample.js', autoIndent: false)
expect(editSession.shouldPromptToSave()).toBeFalsy()
editSession2.destroy()
expect(editSession.shouldPromptToSave()).toBeTruthy()

View File

@@ -1,3 +1,4 @@
RootView = require 'root-view'
EditSession = require 'edit-session'
Buffer = require 'text-buffer'
Editor = require 'editor'
@@ -2036,6 +2037,25 @@ describe "Editor", ->
runs ->
expect(editor.getText()).toBe(originalPathText)
describe ".pixelPositionForBufferPosition(position)", ->
describe "when the editor is detached", ->
it "returns top and left values of 0", ->
expect(editor.isOnDom()).toBeFalsy()
expect(editor.pixelPositionForBufferPosition([2,7])).toEqual top: 0, left: 0
describe "when the editor is invisible", ->
it "returns top and left values of 0", ->
editor.attachToDom()
editor.hide()
expect(editor.isVisible()).toBeFalsy()
expect(editor.pixelPositionForBufferPosition([2,7])).toEqual top: 0, left: 0
describe "when the editor is attached and visible", ->
it "returns the top and left pixel positions", ->
editor.attachToDom()
expect(editor.pixelPositionForBufferPosition([2,7])).toEqual top: 40, left: 70
describe "when clicking in the gutter", ->
beforeEach ->
editor.attachToDom()
@@ -2488,3 +2508,47 @@ describe "Editor", ->
editor.trigger(keydownEvent('escape'))
expect(testEventHandler).toHaveBeenCalled()
describe "when the editor is attached but invisible", ->
describe "when the editor's text is changed", ->
it "redraws the editor when it is next shown", ->
window.rootView = new RootView
rootView.open('sample.js')
rootView.attachToDom()
editor = rootView.getActiveView()
view = $$ -> @div id: 'view', tabindex: -1, 'View'
editor.getPane().showItem(view)
expect(editor.isVisible()).toBeFalsy()
editor.setText('hidden changes')
editor.setCursorBufferPosition([0,4])
displayUpdatedHandler = jasmine.createSpy("displayUpdatedHandler")
editor.on 'editor:display-updated', displayUpdatedHandler
editor.getPane().showItem(editor.getModel())
expect(editor.isVisible()).toBeTruthy()
waitsFor ->
displayUpdatedHandler.callCount is 1
runs ->
expect(editor.renderedLines.find('.line').text()).toBe 'hidden changes'
it "redraws the editor when it is next reattached", ->
editor.attachToDom()
editor.hide()
editor.setText('hidden changes')
editor.setCursorBufferPosition([0,4])
editor.detach()
displayUpdatedHandler = jasmine.createSpy("displayUpdatedHandler")
editor.on 'editor:display-updated', displayUpdatedHandler
editor.show()
editor.attachToDom()
waitsFor ->
displayUpdatedHandler.callCount is 1
runs ->
expect(editor.renderedLines.find('.line').text()).toBe 'hidden changes'

View File

@@ -0,0 +1,78 @@
ImageView = require 'image-view'
ImageEditSession = require 'image-edit-session'
describe "ImageView", ->
[view, path] = []
beforeEach ->
path = project.resolve('binary-file.png')
view = new ImageView()
view.attachToDom()
view.parent().height(100)
it "displays the image for a path", ->
view.setModel(new ImageEditSession(path))
expect(view.image.attr('src')).toBe path
it "centers the image in the editor", ->
imageLoaded = false
view.image.load =>
imageLoaded = true
view.setModel(new ImageEditSession(path))
waitsFor ->
imageLoaded
runs ->
expect(view.image.width()).toBe 10
expect(view.image.height()).toBe 10
expect(view.image.css('left')).toBe "#{(view.width() - view.image.outerWidth()) / 2}px"
expect(view.image.css('top')).toBe "#{(view.height() - view.image.outerHeight()) / 2}px"
describe "image-view:zoom-in", ->
it "increases the image size by 10%", ->
imageLoaded = false
view.image.load =>
imageLoaded = true
view.setModel(new ImageEditSession(path))
waitsFor ->
imageLoaded
runs ->
view.trigger 'image-view:zoom-in'
expect(view.image.width()).toBe 11
expect(view.image.height()).toBe 11
describe "image-view:zoom-out", ->
it "decreases the image size by 10%", ->
imageLoaded = false
view.image.load =>
imageLoaded = true
view.setModel(new ImageEditSession(path))
waitsFor ->
imageLoaded
runs ->
view.trigger 'image-view:zoom-out'
expect(view.image.width()).toBe 9
expect(view.image.height()).toBe 9
describe "image-view:reset-zoom", ->
it "restores the image to the original size", ->
imageLoaded = false
view.image.load =>
imageLoaded = true
view.setModel(new ImageEditSession(path))
waitsFor ->
imageLoaded
runs ->
view.trigger 'image-view:zoom-in'
expect(view.image.width()).not.toBe 10
expect(view.image.height()).not.toBe 10
view.trigger 'image-view:reset-zoom'
expect(view.image.width()).toBe 10
expect(view.image.height()).toBe 10

View File

@@ -110,7 +110,8 @@ describe "PaneContainer", ->
expect(pane1.activeItem).toEqual item3
describe "when there is no active pane", ->
it "attaches a new pane with the reconstructed last pane item", ->
it "attaches a new pane with the reconstructed last pane item and focuses it", ->
container.attachToDom()
pane1.remove()
pane2.remove()
item3 = pane3.activeItem
@@ -120,6 +121,7 @@ describe "PaneContainer", ->
container.reopenItem()
expect(container.getActivePane().activeItem).toEqual item3
expect(container.getActivePane().activeView).toMatchSelector ':focus'
it "does not reopen an item that is already open", ->
item3 = pane3.activeItem

View File

@@ -257,6 +257,13 @@ describe "Pane", ->
expect(pane2.getItems()).toEqual [view3, editSession1]
expect(editSession1.destroyed).toBeFalsy()
describe "when the item is a jQuery object", ->
it "preserves data by detaching instead of removing", ->
view1.data('preservative', 1234)
pane.moveItemToPane(view1, pane2, 1)
pane2.showItemAtIndex(1)
expect(pane2.activeView.data('preservative')).toBe 1234
describe "core:close", ->
it "destroys the active item and does not bubble the event", ->
containerCloseHandler = jasmine.createSpy("containerCloseHandler")

View File

@@ -354,3 +354,31 @@ describe "RootView", ->
rootView.open(require.resolve('fixtures/sample.txt'))
expect(count).toBe 1
expect(callbackBuffer).toBe rootView.getActiveView().getBuffer()
describe ".eachPane(callback)", ->
beforeEach ->
rootView.attachToDom()
it "invokes the callback for all existing panes", ->
count = 0
callbackPane = null
callback = (pane) ->
callbackPane = pane
count++
rootView.eachPane(callback)
expect(count).toBe 1
expect(callbackPane).toBe rootView.getActivePane()
it "invokes the callback for new panes", ->
count = 0
callbackPane = null
callback = (pane) ->
callbackPane = pane
count++
rootView.eachPane(callback)
count = 0
callbackPane = null
rootView.getActiveView().splitRight()
expect(count).toBe 1
expect(callbackPane).toBe rootView.getActivePane()

View File

@@ -198,3 +198,31 @@ describe "Window", ->
expect(deserialize({ deserializer: 'Foo', version: 3, name: 'Bar' })).toBeUndefined()
expect(deserialize({ deserializer: 'Foo', version: 1, name: 'Bar' })).toBeUndefined()
expect(deserialize({ deserializer: 'Foo', name: 'Bar' })).toBeUndefined()
describe "drag and drop", ->
buildDragEvent = (type, files) ->
dataTransfer =
files: files
data: {}
setData: (key, value) -> @data[key] = value
getData: (key) -> @data[key]
event = $.Event(type)
event.originalEvent = { dataTransfer }
event.preventDefault = ->
event.stopPropagation = ->
event
describe "when a file is dragged to window", ->
it "opens it", ->
spyOn(atom, "open")
event = buildDragEvent("drop", [ {path: "/fake1"}, {path: "/fake2"} ])
window.onDrop(event)
expect(atom.open.callCount).toBe 2
describe "when a non-file is dragged to window", ->
it "does nothing", ->
spyOn(atom, "open")
event = buildDragEvent("drop", [])
window.onDrop(event)
expect(atom.open).not.toHaveBeenCalled()

View File

@@ -156,6 +156,7 @@ class EditSession
saveAs: (path) -> @buffer.saveAs(path)
getFileExtension: -> @buffer.getExtension()
getPath: -> @buffer.getPath()
getBuffer: -> @buffer
getUri: -> @getPath()
isBufferRowBlank: (bufferRow) -> @buffer.isRowBlank(bufferRow)
nextNonBlankBufferRow: (bufferRow) -> @buffer.nextNonBlankRow(bufferRow)
@@ -167,7 +168,7 @@ class EditSession
scanInBufferRange: (args...) -> @buffer.scanInRange(args...)
backwardsScanInBufferRange: (args...) -> @buffer.backwardsScanInRange(args...)
isModified: -> @buffer.isModified()
hasEditors: -> @buffer.hasEditors()
shouldPromptToSave: -> @isModified() and not @buffer.hasMultipleEditors()
screenPositionForBufferPosition: (bufferPosition, options) -> @displayBuffer.screenPositionForBufferPosition(bufferPosition, options)
bufferPositionForScreenPosition: (screenPosition, options) -> @displayBuffer.bufferPositionForScreenPosition(screenPosition, options)

View File

@@ -442,6 +442,13 @@ class Editor extends View
@subscribe $(window), "resize.editor-#{@id}", => @requestDisplayUpdate()
@focus() if @isFocused
if pane = @getPane()
@active = @is(pane.activeView)
@subscribe pane, 'pane:active-item-changed', (event, item) =>
wasActive = @active
@active = @is(pane.activeView)
@redraw() if @active and not wasActive
@resetDisplay()
@trigger 'editor:attached', [this]
@@ -771,6 +778,7 @@ class Editor extends View
requestDisplayUpdate: ->
return if @pendingDisplayUpdate
return unless @isVisible()
@pendingDisplayUpdate = true
_.nextTick =>
@updateDisplay()
@@ -779,6 +787,10 @@ class Editor extends View
updateDisplay: (options={}) ->
return unless @attached and @activeEditSession
return if @activeEditSession.destroyed
unless @isVisible()
@redrawOnReattach = true
return
@updateRenderedLines()
@highlightCursorLine()
@updateCursorViews()
@@ -916,9 +928,8 @@ class Editor extends View
if intactRanges.length == 0
@renderedLines.empty()
else
else if currentLine = renderedLines.firstChild
domPosition = 0
currentLine = renderedLines.firstChild
for intactRange in intactRanges
while intactRange.domStart > domPosition
currentLine = killLine(currentLine)
@@ -1079,7 +1090,7 @@ class Editor extends View
@pixelPositionForScreenPosition(@screenPositionForBufferPosition(position))
pixelPositionForScreenPosition: (position) ->
return { top: 0, left: 0 } unless @isOnDom()
return { top: 0, left: 0 } unless @isOnDom() and @isVisible()
{row, column} = Point.fromObject(position)
actualRow = Math.floor(row)

View File

@@ -0,0 +1,42 @@
fsUtils = require 'fs-utils'
_ = require 'underscore'
module.exports=
class ImageEditSession
registerDeserializer(this)
@canOpen: (path) ->
_.indexOf([
'.gif'
'.jpeg'
'.jpg'
'.png'
], fsUtils.extension(path), true) >= 0
@deserialize: (state) ->
if fsUtils.exists(state.path)
project.buildEditSession(state.path)
else
console.warn "Could not build edit session for path '#{state.path}' because that file no longer exists"
constructor: (@path) ->
serialize: ->
deserializer: 'ImageEditSession'
path: @path
getViewClass: ->
require 'image-view'
getTitle: ->
if path = @getPath()
fsUtils.base(path)
else
'untitled'
getUri: -> @path
getPath: -> @path
isEqual: (other) ->
other instanceof ImageEditSession and @getUri() is other.getUri()

80
src/app/image-view.coffee Normal file
View File

@@ -0,0 +1,80 @@
ScrollView = require 'scroll-view'
_ = require 'underscore'
$ = require 'jquery'
module.exports =
class ImageView extends ScrollView
@content: ->
@div class: 'image-view', tabindex: -1, =>
@img outlet: 'image'
initialize: (imageEditSession) ->
super
requireStylesheet 'image-view'
@image.load =>
@originalHeight = @image.height()
@originalWidth = @image.width()
@loaded = true
@centerImage()
@setPath(imageEditSession?.getPath())
@subscribe $(window), 'resize', _.debounce((=> @centerImage()), 300)
@command 'image-view:zoom-in', => @zoomIn()
@command 'image-view:zoom-out', => @zoomOut()
@command 'image-view:reset-zoom', => @resetZoom()
afterAttach: (onDom) ->
return unless onDom
if pane = @getPane()
@active = @is(pane.activeView)
@subscribe pane, 'pane:active-item-changed', (event, item) =>
wasActive = @active
@active = @is(pane.activeView)
@centerImage() if @active and not wasActive
centerImage: ->
return unless @loaded and @isVisible()
@image.css
'top': Math.max((@height() - @image.outerHeight()) / 2, 0)
'left': Math.max((@width() - @image.outerWidth()) / 2, 0)
@image.show()
setPath: (path) ->
if path?
if @image.attr('src') isnt path
@loaded = false
@image.hide().attr('src', path)
else
@image.hide()
setModel: (imageEditSession) ->
@setPath(imageEditSession?.getPath())
getPane: ->
@parent('.item-views').parent('.pane').view()
adjustSize: (factor) ->
return unless @loaded and @isVisible()
newWidth = @image.width() * factor
newHeight = @image.height() * factor
@image.width(newWidth)
@image.height(newHeight)
@centerImage()
zoomOut: ->
@adjustSize(0.9)
zoomIn: ->
@adjustSize(1.1)
resetZoom: ->
return unless @loaded and @isVisible()
@image.width(@originalWidth)
@image.height(@originalHeight)
@centerImage()

View File

@@ -35,3 +35,9 @@
'.editor !important, .editor.mini !important':
'escape': 'editor:consolidate-selections'
'.image-view':
'meta-+': 'image-view:zoom-in'
'meta-=': 'image-view:zoom-in'
'meta--': 'image-view:zoom-out'
'meta-0': 'image-view:reset-zoom'

View File

@@ -55,7 +55,9 @@ class PaneContainer extends View
activePane.showItem(deserialize(lastItemState))
true
else
@append(new Pane(deserialize(lastItemState)))
newPane = new Pane(deserialize(lastItemState))
@append(newPane)
newPane.focus()
itemDestroyed: (item) ->
state = item.serialize?()

View File

@@ -58,7 +58,7 @@ class Pane extends View
return if @attached
@attached = true
@trigger 'pane:attached'
@trigger 'pane:attached', [this]
makeActive: ->
for pane in @getContainer().getPanes() when pane isnt this
@@ -148,7 +148,7 @@ class Pane extends View
@autosaveItem(item)
if promptToSave && item.isModified?()
if item.shouldPromptToSave?()
@promptToSaveItem(item, reallyDestroyItem)
else
reallyDestroyItem()
@@ -214,7 +214,9 @@ class Pane extends View
@trigger 'pane:item-moved', [item, newIndex]
moveItemToPane: (item, pane, index) ->
@isMovingItem = true
@removeItem(item)
@isMovingItem = false
pane.addItem(item, index)
itemForUri: (uri) ->
@@ -235,8 +237,12 @@ class Pane extends View
delete @viewsByClassName[viewClass.name]
if @items.length > 0
viewToRemove?.remove()
if @isMovingItem and item is viewToRemove
viewToRemove?.detach()
else
viewToRemove?.remove()
else
viewToRemove?.detach() if @isMovingItem and item is viewToRemove
@remove()
viewForItem: (item) ->

View File

@@ -4,6 +4,7 @@ $ = require 'jquery'
Range = require 'range'
Buffer = require 'text-buffer'
EditSession = require 'edit-session'
ImageEditSession = require 'image-edit-session'
EventEmitter = require 'event-emitter'
Directory = require 'directory'
BufferedProcess = require 'buffered-process'
@@ -85,7 +86,10 @@ class Project
setSoftWrap: (@softWrap) ->
buildEditSession: (filePath, editSessionOptions={}) ->
@buildEditSessionForBuffer(@bufferForPath(filePath), editSessionOptions)
if ImageEditSession.canOpen(filePath)
new ImageEditSession(filePath)
else
@buildEditSessionForBuffer(@bufferForPath(filePath), editSessionOptions)
buildEditSessionForBuffer: (buffer, editSessionOptions) ->
options = _.extend(@defaultEditSessionOptions(), editSessionOptions)

View File

@@ -162,6 +162,10 @@ class RootView extends View
indexOfPane: (pane) ->
@panes.indexOfPane(pane)
eachPane: (callback) ->
callback(pane) for pane in @getPanes()
@on 'pane:attached', (e, pane) -> callback(pane)
eachEditor: (callback) ->
callback(editor) for editor in @getEditors()
@on 'editor:attached', (e, editor) -> callback(editor)

View File

@@ -142,6 +142,7 @@ class SelectList extends View
cancelled: ->
@miniEditor.setText('')
@miniEditor.updateDisplay()
cancel: ->
@list.empty()

View File

@@ -70,7 +70,7 @@ class Buffer
path: @getPath()
text: @getText() if @isModified()
hasEditors: -> @refcount > 1
hasMultipleEditors: -> @refcount > 1
subscribeToFile: ->
@file.on "contents-changed", =>

View File

@@ -25,12 +25,6 @@ window.setUpEnvironment = ->
$(document).on 'keydown', keymap.handleKeyEvent
keymap.bindDefaultKeys()
ignoreEvents = (e) ->
e.preventDefault()
e.stopPropagation()
$(document).on 'dragover', ignoreEvents
$(document).on 'drop', ignoreEvents
requireStylesheet 'reset'
requireStylesheet 'atom'
requireStylesheet 'overlay'
@@ -50,6 +44,7 @@ window.startup = ->
console.warn "Failed to install `atom` binary"
handleWindowEvents()
handleDragDrop()
config.load()
keymap.loadBundledKeymaps()
atom.loadThemes()
@@ -93,6 +88,18 @@ window.handleWindowEvents = ->
$(window).command 'window:close', => confirmClose()
$(window).command 'window:reload', => reload()
window.handleDragDrop = ->
$(document).on 'dragover', (e) ->
e.preventDefault()
e.stopPropagation()
$(document).on 'drop', onDrop
window.onDrop = (e) ->
e.preventDefault()
e.stopPropagation()
for file in e.originalEvent.dataTransfer.files
atom.open(file.path)
window.deserializeWindowState = ->
RootView = require 'root-view'
Project = require 'project'

View File

@@ -39,13 +39,13 @@ class CommandPanelView extends View
@command 'core:move-up', => @navigateBackwardInHistory()
@command 'core:move-down', => @navigateForwardInHistory()
rootView.command 'command-panel:toggle', => @toggle()
rootView.command 'command-panel:toggle-preview', => @togglePreview()
rootView.command 'command-panel:find-in-file', => @attach('/')
rootView.command 'command-panel:find-in-project', => @attach('Xx/')
rootView.command 'command-panel:repeat-relative-address', => @repeatRelativeAddress()
rootView.command 'command-panel:repeat-relative-address-in-reverse', => @repeatRelativeAddress(reverse: true)
rootView.command 'command-panel:set-selection-as-regex-address', => @setSelectionAsLastRelativeAddress()
@subscribeToCommand rootView, 'command-panel:toggle', => @toggle()
@subscribeToCommand rootView, 'command-panel:toggle-preview', => @togglePreview()
@subscribeToCommand rootView, 'command-panel:find-in-file', => @attach('/')
@subscribeToCommand rootView, 'command-panel:find-in-project', => @attach('Xx/')
@subscribeToCommand rootView, 'command-panel:repeat-relative-address', => @repeatRelativeAddress()
@subscribeToCommand rootView, 'command-panel:repeat-relative-address-in-reverse', => @repeatRelativeAddress(reverse: true)
@subscribeToCommand rootView, 'command-panel:set-selection-as-regex-address', => @setSelectionAsLastRelativeAddress()
@on 'click', '.expand', @onExpandAll
@on 'click', '.collapse', @onCollapseAll
@@ -65,8 +65,6 @@ class CommandPanelView extends View
destroy: ->
@previewList.destroy()
rootView.off "command-panel:toggle-preview command-panel:find-in-file command-panel:find-in-project \
command-panel:repeat-relative-address command-panel:repeat-relative-address-in-reverse command-panel:set-selection-as-regex-address"
@remove()
toggle: ->

View File

@@ -14,10 +14,20 @@ class MarkdownPreviewView extends ScrollView
initialize: (@buffer) ->
super
@fetchRenderedMarkdown()
@on 'core:move-up', => @scrollUp()
@on 'core:move-down', => @scrollDown()
afterAttach: (onDom) ->
@subscribe @buffer, 'saved', =>
@fetchRenderedMarkdown()
pane = @getPane()
pane.showItem(this) if pane? and pane isnt rootView.getActivePane()
getPane: ->
@parent('.item-views').parent('.pane').view()
serialize: ->
deserializer: 'MarkdownPreviewView'
path: @buffer.getPath()

View File

@@ -1,10 +1,9 @@
EditSession = require 'edit-session'
MarkdownPreviewView = require 'markdown-preview/lib/markdown-preview-view'
MarkdownPreviewView = require './markdown-preview-view'
module.exports =
activate: ->
rootView.command 'markdown-preview:show', '.editor', => @show()
rootView.on 'core:save', ".pane", => @show() if @previewExists()
show: ->
activePane = rootView.getActivePane()
@@ -16,17 +15,19 @@ module.exports =
console.warn("Can not render markdown for '#{editSession.getUri() ? 'untitled'}'")
return
if nextPane = activePane.getNextPane()
if preview = nextPane.itemForUri("markdown-preview:#{editSession.getPath()}")
nextPane.showItem(preview)
preview.fetchRenderedMarkdown()
else
nextPane.showItem(new MarkdownPreviewView(editSession.buffer))
{previewPane, previewItem} = @getExistingPreview(editSession)
if previewItem?
previewPane.showItem(previewItem)
previewItem.fetchRenderedMarkdown()
else if nextPane = activePane.getNextPane()
nextPane.showItem(new MarkdownPreviewView(editSession.buffer))
else
activePane.splitRight(new MarkdownPreviewView(editSession.buffer))
activePane.focus()
previewExists: ->
nextPane = rootView.getActivePane().getNextPane()
item = rootView.getActivePane().activeItem
nextPane?.itemForUri("markdown-preview:#{item.getPath?()}")
getExistingPreview: (editSession) ->
uri = "markdown-preview:#{editSession.getPath()}"
for previewPane in rootView.getPanes()
previewItem = previewPane.itemForUri(uri)
return {previewPane, previewItem} if previewItem?
{}

View File

@@ -62,7 +62,7 @@ describe "MarkdownPreview package", ->
pane.focus()
MarkdownPreviewView.prototype.fetchRenderedMarkdown.reset()
pane.trigger("core:save")
pane.activeItem.buffer.trigger 'saved'
expect(MarkdownPreviewView.prototype.fetchRenderedMarkdown).not.toHaveBeenCalled()
describe "when a preview item has already been created for the edit session's uri", ->
@@ -86,12 +86,30 @@ describe "MarkdownPreview package", ->
expect(pane1).toMatchSelector(':has(:focus)')
describe "when a buffer is saved", ->
it "updates the existing preview item", ->
rootView.getActiveView().trigger 'markdown-preview:show'
[pane1, pane2] = rootView.getPanes()
preview = pane2.activeItem
pane1.focus()
describe "when the preview is in the same pane", ->
it "updates the preview but does not make it active", ->
rootView.getActiveView().trigger 'markdown-preview:show'
[pane1, pane2] = rootView.getPanes()
pane2.moveItemToPane(pane2.activeItem, pane1, 1)
pane1.showItemAtIndex(1)
pane1.showItemAtIndex(0)
preview = pane1.itemAtIndex(1)
preview.fetchRenderedMarkdown.reset()
pane1.trigger("core:save")
expect(preview.fetchRenderedMarkdown).toHaveBeenCalled()
preview.fetchRenderedMarkdown.reset()
pane1.activeItem.buffer.trigger 'saved'
expect(preview.fetchRenderedMarkdown).toHaveBeenCalled()
expect(pane1.activeItem).not.toBe preview
describe "when the preview is not in the same pane", ->
it "updates the preview and makes it active", ->
rootView.getActiveView().trigger 'markdown-preview:show'
[pane1, pane2] = rootView.getPanes()
preview = pane2.activeItem
pane2.showItem($$ -> @div id: 'view', tabindex: -1, 'View')
expect(pane2.activeItem).not.toBe preview
pane1.focus()
preview.fetchRenderedMarkdown.reset()
pane1.activeItem.buffer.trigger 'saved'
expect(preview.fetchRenderedMarkdown).toHaveBeenCalled()
expect(pane2.activeItem).toBe preview

View File

@@ -5,12 +5,8 @@ $ = require 'jquery'
module.exports =
class StatusBarView extends View
@activate: ->
rootView.eachEditor (editor) =>
@appendToEditorPane(rootView, editor) if editor.attached
@appendToEditorPane: (rootView, editor) ->
if pane = editor.getPane()
pane.append(new StatusBarView(rootView, editor))
rootView.eachPane (pane) =>
pane.append(new StatusBarView(rootView, pane))
@content: ->
@div class: 'status-bar', =>
@@ -26,41 +22,46 @@ class StatusBarView extends View
@span class: 'cursor-position', outlet: 'cursorPosition'
@span class: 'grammar-name', outlet: 'grammarName'
initialize: (rootView, @editor) ->
initialize: (rootView, @pane) ->
@updatePathText()
@editor.on 'editor:path-changed', =>
@subscribe @pane, 'pane:active-item-changed', =>
@subscribeToBuffer()
@updatePathText()
@updateCursorPositionText()
@subscribe @editor, 'cursor:moved', => @updateCursorPositionText()
@subscribe @grammarName, 'click', => @editor.trigger 'grammar-selector:show'
@subscribe @editor, 'editor:grammar-changed', => @updateGrammarText()
@subscribe @pane, 'cursor:moved', => @updateCursorPositionText()
@subscribe @grammarName, 'click', => @pane.activeView.trigger 'grammar-selector:show'
@subscribe @pane, 'editor:grammar-changed', => @updateGrammarText()
if git?
@subscribe git, 'status-changed', (path, status) =>
@updateStatusBar() if path is @buffer?.getPath()
@updateStatusBar() if path is @getActiveItemPath()
@subscribe git, 'statuses-changed', =>
@updateStatusBar()
@subscribeToBuffer()
getActiveItemPath: ->
@pane.activeItem?.getPath?()
subscribeToBuffer: ->
@buffer?.off '.status-bar'
@buffer = @editor.getBuffer()
@buffer.on 'modified-status-changed.status-bar', (isModified) => @updateBufferHasModifiedText(isModified)
@buffer.on 'saved.status-bar', => @updateStatusBar()
if @buffer = @pane.activeItem.getBuffer?()
@buffer.on 'modified-status-changed.status-bar', (isModified) => @updateBufferHasModifiedText(isModified)
@buffer.on 'saved.status-bar', => @updateStatusBar()
@updateStatusBar()
updateStatusBar: ->
@updateGrammarText()
@updateBranchText()
@updateBufferHasModifiedText(@buffer.isModified())
@updateBufferHasModifiedText(@buffer?.isModified())
@updateStatusText()
@updateCursorPositionText()
updateGrammarText: ->
grammar = @editor.getGrammar()
if grammar is syntax.nullGrammar
@grammarName.text('').hide()
grammar = @pane.activeView.getGrammar?()
if not grammar? or grammar is syntax.nullGrammar
@grammarName.hide()
else
@grammarName.text(grammar.name).show()
@@ -73,7 +74,7 @@ class StatusBarView extends View
@isModified = false
updateBranchText: ->
path = @editor.getPath()
path = @getActiveItemPath()
@branchArea.hide()
return unless path
@@ -82,7 +83,7 @@ class StatusBarView extends View
@branchArea.show() if head
updateStatusText: ->
path = @editor.getPath()
path = @getActiveItemPath()
@gitStatusIcon.removeClass()
return unless path
@@ -113,17 +114,24 @@ class StatusBarView extends View
@gitStatusIcon.text('')
else if git.isStatusNew(status)
@gitStatusIcon.addClass('new-status-icon')
@gitStatusIcon.text("+#{@buffer.getLineCount()}")
if @buffer?
@gitStatusIcon.text("+#{@buffer.getLineCount()}")
else
@gitStatusIcon.text('')
else if git.isPathIgnored(path)
@gitStatusIcon.addClass('ignored-status-icon')
@gitStatusIcon.text('')
updatePathText: ->
if path = @editor.getPath()
@currentPath.text(project.relativize(path))
if path = @getActiveItemPath()
@currentPath.text(project.relativize(path)).show()
else if title = @pane.activeItem.getTitle?()
@currentPath.text(title).show()
else
@currentPath.text('untitled')
@currentPath.hide()
updateCursorPositionText: ->
{ row, column } = @editor.getCursorBufferPosition()
@cursorPosition.text("#{row + 1},#{column + 1}")
if position = @pane.activeView.getCursorBufferPosition?()
@cursorPosition.text("#{position.row + 1},#{position.column + 1}").show()
else
@cursorPosition.hide()

View File

@@ -3,6 +3,7 @@ _ = require 'underscore'
RootView = require 'root-view'
StatusBar = require 'status-bar/lib/status-bar-view'
fsUtils = require 'fs-utils'
{$$} = require 'space-pen'
describe "StatusBar", ->
[editor, statusBar, buffer] = []
@@ -102,7 +103,9 @@ describe "StatusBar", ->
describe "when the associated editor's cursor position changes", ->
it "updates the cursor position in the status bar", ->
rootView.attachToDom()
editor.setCursorScreenPosition([1, 2])
editor.updateDisplay()
expect(statusBar.cursorPosition.text()).toBe '2,3'
describe "git branch label", ->
@@ -192,7 +195,6 @@ describe "StatusBar", ->
editor.activeEditSession.languageMode.grammar = syntax.nullGrammar
editor.activeEditSession.trigger 'grammar-changed'
expect(statusBar.find('.grammar-name')).toBeHidden()
expect(statusBar.find('.grammar-name').text()).toBe ''
editor.reloadGrammar()
expect(statusBar.find('.grammar-name')).toBeVisible()
expect(statusBar.find('.grammar-name').text()).toBe 'JavaScript'
@@ -209,3 +211,26 @@ describe "StatusBar", ->
editor.on 'grammar-selector:show', eventHandler
statusBar.find('.grammar-name').click()
expect(eventHandler).toHaveBeenCalled()
describe "when the active item view does not implement getCursorBufferPosition()", ->
it "hides the cursor position view", ->
rootView.attachToDom()
view = $$ -> @div id: 'view', tabindex: -1, 'View'
editor.getPane().showItem(view)
expect(statusBar.cursorPosition).toBeHidden()
describe "when the active item implements getTitle() but not getPath()", ->
it "displays the title", ->
rootView.attachToDom()
view = $$ -> @div id: 'view', tabindex: -1, 'View'
view.getTitle = => 'View Title'
editor.getPane().showItem(view)
expect(statusBar.currentPath.text()).toBe 'View Title'
expect(statusBar.currentPath).toBeVisible()
describe "when the active item neither getTitle() nor getPath()", ->
it "hides the path view", ->
rootView.attachToDom()
view = $$ -> @div id: 'view', tabindex: -1, 'View'
editor.getPane().showItem(view)
expect(statusBar.currentPath).toBeHidden()

View File

@@ -77,11 +77,8 @@ class TabBarView extends View
(@paneContainer.getPanes().length > 1) or (@pane.getItems().length > 1)
onDragStart: (event) =>
unless @shouldAllowDrag(event)
event.preventDefault()
return
event.originalEvent.dataTransfer.setData 'atom-event', 'true'
if @shouldAllowDrag()
event.originalEvent.dataTransfer.setData 'atom-event', 'true'
el = $(event.target).closest('.sortable')
el.addClass 'is-dragging'
@@ -91,8 +88,15 @@ class TabBarView extends View
paneIndex = @paneContainer.indexOfPane(pane)
event.originalEvent.dataTransfer.setData 'from-pane-index', paneIndex
item = @pane.getItems()[el.index()]
if item.getPath?
event.originalEvent.dataTransfer.setData 'text/uri-list', 'file://' + item.getPath()
event.originalEvent.dataTransfer.setData 'text/plain', item.getPath()
onDragEnd: (event) =>
@find(".is-dragging").removeClass 'is-dragging'
@children('.is-drop-target').removeClass 'is-drop-target'
@children('.drop-target-is-after').removeClass 'drop-target-is-after'
onDragOver: (event) =>
unless event.originalEvent.dataTransfer.getData('atom-event') is 'true'
@@ -120,8 +124,6 @@ class TabBarView extends View
return
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'))

View File

@@ -279,8 +279,8 @@ describe "TabBarView", ->
expect(pane2.activeItem).toBe item1
expect(pane2.focus).toHaveBeenCalled()
describe 'when a non-tab is dragged to pane', ->
it 'has no effect', ->
describe "when a non-tab is dragged to pane", ->
it "has no effect", ->
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
@@ -294,3 +294,11 @@ describe "TabBarView", ->
expect(pane.activeItem).toBe item2
expect(pane.focus).not.toHaveBeenCalled()
describe "when a tab is dragged out of application", ->
it "should carry file's information", ->
[dragStartEvent, dropEvent] = buildDragEvents(tabBar.tabAtIndex(1), tabBar.tabAtIndex(1))
tabBar.onDragStart(dragStartEvent)
expect(dragStartEvent.originalEvent.dataTransfer.getData("text/plain")).toEqual editSession1.getPath()
expect(dragStartEvent.originalEvent.dataTransfer.getData("text/uri-list")).toEqual 'file://' + editSession1.getPath()

View File

@@ -4,5 +4,10 @@ module.exports =
@subscriptions ?= []
@subscriptions.push(cancel: -> eventEmitter.off eventName, callback)
subscribeToCommand: (view, eventName, callback) ->
view.command eventName, callback
@subscriptions ?= []
@subscriptions.push(cancel: -> view.off eventName, callback)
unsubscribe: ->
subscription.cancel() for subscription in @subscriptions ? []

14
static/image-view.less Normal file
View File

@@ -0,0 +1,14 @@
.image-view {
height:100%;
width: 100%;
margin: 0;
padding: 0;
overflow: auto;
img {
padding: 25px;
border: 2px solid;
background-image: url(images/transparent-background.gif);
position: relative;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 B

View File

@@ -0,0 +1,7 @@
.image-view {
background-color: #1d1f21;
img {
border-color: #c5c8c6;
}
}

View File

@@ -8,4 +8,5 @@
'command-panel'
'command-logger'
'blurred'
'image-view'
]

View File

@@ -0,0 +1,7 @@
.image-view {
background-color: #fff;
}
.image-view img {
border-color: #ccc;
}

View File

@@ -9,4 +9,5 @@
'command-panel'
'command-logger'
'blurred'
'image-view'
]