mirror of
https://github.com/atom/atom.git
synced 2026-01-24 14:28:14 -05:00
Merge remote-tracking branch 'origin/master' into cson-snippets
This commit is contained in:
@@ -139,7 +139,7 @@
|
||||
|
||||
- (void)open:(NSString *)path pidToKillWhenWindowCloses:(NSNumber *)pid {
|
||||
for (NSWindow *window in [self windows]) {
|
||||
if ([window isVisible] && ![window isExcludedFromWindowsMenu]) {
|
||||
if (![window isExcludedFromWindowsMenu]) {
|
||||
AtomWindowController *controller = [window windowController];
|
||||
if ([path isEqualToString:controller.pathToOpen]) {
|
||||
[window makeKeyAndOrderFront:nil];
|
||||
|
||||
@@ -9,6 +9,7 @@ var $git = {};
|
||||
native function checkoutHead(path);
|
||||
native function getDiffStats(path);
|
||||
native function isSubmodule(path);
|
||||
native function refreshIndex();
|
||||
|
||||
function GitRepository(path) {
|
||||
var repo = getRepository(path);
|
||||
@@ -24,5 +25,6 @@ var $git = {};
|
||||
GitRepository.prototype.checkoutHead = checkoutHead;
|
||||
GitRepository.prototype.getDiffStats = getDiffStats;
|
||||
GitRepository.prototype.isSubmodule = isSubmodule;
|
||||
GitRepository.prototype.refreshIndex = refreshIndex;
|
||||
this.GitRepository = GitRepository;
|
||||
})();
|
||||
|
||||
@@ -69,12 +69,6 @@ public:
|
||||
return CefV8Value::CreateInt(0);
|
||||
}
|
||||
|
||||
git_index* index;
|
||||
if (git_repository_index(&index, repo) == GIT_OK) {
|
||||
git_index_read(index);
|
||||
git_index_free(index);
|
||||
}
|
||||
|
||||
unsigned int status = 0;
|
||||
if (git_status_file(&status, repo, path) == GIT_OK) {
|
||||
return CefV8Value::CreateInt(status);
|
||||
@@ -100,12 +94,7 @@ public:
|
||||
|
||||
int result = git_checkout_head(repo, &options);
|
||||
free(copiedPath);
|
||||
if (result == GIT_OK) {
|
||||
return CefV8Value::CreateBool(true);
|
||||
}
|
||||
else {
|
||||
return CefV8Value::CreateBool(false);
|
||||
}
|
||||
return CefV8Value::CreateBool(result == GIT_OK);
|
||||
}
|
||||
|
||||
CefRefPtr<CefV8Value> GetDiffStats(const char *path) {
|
||||
@@ -192,7 +181,6 @@ public:
|
||||
BOOL isSubmodule = false;
|
||||
git_index* index;
|
||||
if (git_repository_index(&index, repo) == GIT_OK) {
|
||||
git_index_read(index);
|
||||
const git_index_entry *entry = git_index_get_bypath(index, path, 0);
|
||||
isSubmodule = entry != NULL && (entry->mode & S_IFMT) == GIT_FILEMODE_COMMIT;
|
||||
git_index_free(index);
|
||||
@@ -201,6 +189,14 @@ public:
|
||||
return CefV8Value::CreateBool(isSubmodule);
|
||||
}
|
||||
|
||||
void RefreshIndex() {
|
||||
git_index* index;
|
||||
if (exists && git_repository_index(&index, repo) == GIT_OK) {
|
||||
git_index_read(index);
|
||||
git_index_free(index);
|
||||
}
|
||||
}
|
||||
|
||||
IMPLEMENT_REFCOUNTING(GitRepository);
|
||||
};
|
||||
|
||||
@@ -264,6 +260,12 @@ bool Git::Execute(const CefString& name,
|
||||
return true;
|
||||
}
|
||||
|
||||
if (name == "refreshIndex") {
|
||||
GitRepository *userData = (GitRepository *)object->GetUserData().get();
|
||||
userData->RefreshIndex();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -2042,3 +2042,38 @@ describe "Editor", ->
|
||||
event.shiftKey = true
|
||||
editor.underlayer.trigger event
|
||||
expect(editor.getSelection().getScreenRange()).toEqual [[0,0], [12,2]]
|
||||
|
||||
describe ".destroyEditSessionIndex(index)", ->
|
||||
it "prompts to save dirty buffers before closing", ->
|
||||
editor.setText("I'm dirty")
|
||||
rootView.open('sample.txt')
|
||||
expect(editor.getEditSessions().length).toBe 2
|
||||
spyOn(atom, "confirm")
|
||||
editor.destroyEditSessionIndex(0)
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
expect(editor.getEditSessions().length).toBe 2
|
||||
expect(editor.getEditSessions()[0].buffer.isModified()).toBeTruthy()
|
||||
|
||||
describe ".destroyInactiveEditSessions()", ->
|
||||
it "destroys every edit session except the active one", ->
|
||||
rootView.open('sample.txt')
|
||||
cssSession = rootView.open('css.css')
|
||||
rootView.open('coffee.coffee')
|
||||
rootView.open('hello.rb')
|
||||
expect(editor.getEditSessions().length).toBe 5
|
||||
editor.setActiveEditSessionIndex(2)
|
||||
editor.destroyInactiveEditSessions()
|
||||
expect(editor.getActiveEditSessionIndex()).toBe 0
|
||||
expect(editor.getEditSessions().length).toBe 1
|
||||
expect(editor.getEditSessions()[0]).toBe cssSession
|
||||
|
||||
it "prompts to save dirty buffers before destroying", ->
|
||||
editor.setText("I'm dirty")
|
||||
dirtySession = editor.activeEditSession
|
||||
rootView.open('sample.txt')
|
||||
expect(editor.getEditSessions().length).toBe 2
|
||||
spyOn(atom, "confirm")
|
||||
editor.destroyInactiveEditSessions()
|
||||
expect(atom.confirm).toHaveBeenCalled()
|
||||
expect(editor.getEditSessions().length).toBe 2
|
||||
expect(editor.getEditSessions()[0].buffer.isModified()).toBeTruthy()
|
||||
|
||||
@@ -2,8 +2,11 @@ $ = require 'jquery'
|
||||
fs = require 'fs'
|
||||
|
||||
describe "Window", ->
|
||||
[rootView] = []
|
||||
|
||||
beforeEach ->
|
||||
window.attachRootView(require.resolve('fixtures'))
|
||||
rootView = window.rootView
|
||||
|
||||
afterEach ->
|
||||
window.shutdown()
|
||||
@@ -11,11 +14,16 @@ describe "Window", ->
|
||||
$(window).off 'beforeunload'
|
||||
|
||||
describe ".close()", ->
|
||||
it "is triggered by the 'close' event", ->
|
||||
it "is triggered by the 'core:close' event", ->
|
||||
spyOn window, 'close'
|
||||
$(window).trigger 'core:close'
|
||||
expect(window.close).toHaveBeenCalled()
|
||||
|
||||
it "is triggered by the 'window:close event'", ->
|
||||
spyOn window, 'close'
|
||||
$(window).trigger 'window:close'
|
||||
expect(window.close).toHaveBeenCalled()
|
||||
|
||||
describe ".reload()", ->
|
||||
it "returns false when no buffers are modified", ->
|
||||
spyOn($native, "reload")
|
||||
@@ -63,7 +71,7 @@ describe "Window", ->
|
||||
expect(atom.getRootViewStateForPath(window.rootView.project.getPath())).toBeUndefined()
|
||||
expectedState = JSON.parse(JSON.stringify(window.rootView.serialize())) # JSON.stringify removes keys with undefined values
|
||||
$(window).trigger 'beforeunload'
|
||||
expect(atom.getRootViewStateForPath(window.rootView.project.getPath())).toEqual expectedState
|
||||
expect(atom.getRootViewStateForPath(rootView.project.getPath())).toEqual expectedState
|
||||
|
||||
it "unsubscribes from all buffers", ->
|
||||
rootView.open('sample.js')
|
||||
@@ -74,3 +82,12 @@ describe "Window", ->
|
||||
$(window).trigger 'beforeunload'
|
||||
|
||||
expect(editor1.getBuffer().subscriptionCount()).toBe 0
|
||||
|
||||
describe ".shutdown()", ->
|
||||
it "only deactivates the RootView the first time it is called", ->
|
||||
deactivateSpy = spyOn(rootView, "deactivate").andCallThrough()
|
||||
window.shutdown()
|
||||
expect(rootView.deactivate).toHaveBeenCalled()
|
||||
deactivateSpy.reset()
|
||||
window.shutdown()
|
||||
expect(rootView.deactivate).not.toHaveBeenCalled()
|
||||
|
||||
@@ -148,7 +148,7 @@ class Editor extends View
|
||||
'core:select-down': @selectDown
|
||||
'core:select-to-top': @selectToTop
|
||||
'core:select-to-bottom': @selectToBottom
|
||||
'core:close': @close
|
||||
'core:close': @destroyActiveEditSession
|
||||
'editor:save': @save
|
||||
'editor:newline-below': @insertNewlineBelow
|
||||
'editor:toggle-soft-tabs': @toggleSoftTabs
|
||||
@@ -176,6 +176,7 @@ class Editor extends View
|
||||
'editor:toggle-line-comments': @toggleLineCommentsInSelection
|
||||
'editor:log-cursor-scope': @logCursorScope
|
||||
'editor:checkout-head-revision': @checkoutHead
|
||||
'editor:close-other-editors': @destroyInactiveEditSessions
|
||||
|
||||
documentation = {}
|
||||
for name, method of editorBindings
|
||||
@@ -446,17 +447,29 @@ class Editor extends View
|
||||
destroyActiveEditSession: ->
|
||||
@destroyEditSessionIndex(@getActiveEditSessionIndex())
|
||||
|
||||
destroyEditSessionIndex: (index) ->
|
||||
if @editSessions.length == 1
|
||||
@remove()
|
||||
else
|
||||
editSession = @editSessions[index]
|
||||
if index is @getActiveEditSessionIndex()
|
||||
destroyEditSessionIndex: (index, callback) ->
|
||||
return if @mini
|
||||
|
||||
editSession = @editSessions[index]
|
||||
destroySession = =>
|
||||
if index is @getActiveEditSessionIndex() and @editSessions.length > 1
|
||||
@loadPreviousEditSession()
|
||||
_.remove(@editSessions, editSession)
|
||||
editSession.destroy()
|
||||
@trigger 'editor:edit-session-removed', [editSession, index]
|
||||
@remove() if @editSessions.length is 0
|
||||
callback(index) if callback
|
||||
|
||||
if editSession.buffer.isModified()
|
||||
@promptToSaveDirtySession(editSession, destroySession)
|
||||
else
|
||||
destroySession(editSession)
|
||||
|
||||
destroyInactiveEditSessions: ->
|
||||
destroyIndex = (index) =>
|
||||
index++ if @activeEditSession is @editSessions[index]
|
||||
@destroyEditSessionIndex(index, destroyIndex) if @editSessions[index]
|
||||
destroyIndex(0)
|
||||
loadNextEditSession: ->
|
||||
nextIndex = (@getActiveEditSessionIndex() + 1) % @editSessions.length
|
||||
@setActiveEditSessionIndex(nextIndex)
|
||||
@@ -625,14 +638,14 @@ class Editor extends View
|
||||
@removeClass 'soft-wrap'
|
||||
$(window).off 'resize', @_setSoftWrapColumn
|
||||
|
||||
save: (onSuccess) ->
|
||||
save: (session=@activeEditSession, onSuccess) ->
|
||||
if @getPath()
|
||||
@activeEditSession.save()
|
||||
session.save()
|
||||
onSuccess?()
|
||||
else
|
||||
atom.showSaveDialog (path) =>
|
||||
if path
|
||||
@activeEditSession.saveAs(path)
|
||||
session.saveAs(path)
|
||||
onSuccess?()
|
||||
|
||||
autosave: ->
|
||||
@@ -670,19 +683,16 @@ class Editor extends View
|
||||
rootView: ->
|
||||
@parents('#root-view').view()
|
||||
|
||||
close: ->
|
||||
return if @mini
|
||||
if @getBuffer().isModified()
|
||||
filename = if @getPath() then fs.base(@getPath()) else "untitled buffer"
|
||||
atom.confirm(
|
||||
"'#{filename}' has changes, do you want to save them?"
|
||||
"Your changes will be lost if you don't save them"
|
||||
"Save", (=> @save(=> @destroyActiveEditSession())),
|
||||
"Cancel", null
|
||||
"Don't save", (=> @destroyActiveEditSession())
|
||||
)
|
||||
else
|
||||
@destroyActiveEditSession()
|
||||
promptToSaveDirtySession: (session, callback) ->
|
||||
path = session.getPath()
|
||||
filename = if path then fs.base(path) else "untitled buffer"
|
||||
atom.confirm(
|
||||
"'#{filename}' has changes, do you want to save them?"
|
||||
"Your changes will be lost if you don't save them"
|
||||
"Save", => @save(session, callback),
|
||||
"Cancel", null
|
||||
"Don't save", callback
|
||||
)
|
||||
|
||||
remove: (selector, keepData) ->
|
||||
return super if keepData
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
$ = require 'jquery'
|
||||
|
||||
module.exports =
|
||||
class Git
|
||||
|
||||
@@ -15,6 +17,9 @@ class Git
|
||||
|
||||
constructor: (path) ->
|
||||
@repo = new GitRepository(path)
|
||||
$(window).on 'focus', => @refreshIndex()
|
||||
|
||||
refreshIndex: -> @repo.refreshIndex()
|
||||
|
||||
getPath: -> @repo.getPath()
|
||||
|
||||
@@ -31,19 +36,25 @@ class Git
|
||||
isPathIgnored: (path) ->
|
||||
@repo.isIgnored(@relativize(path))
|
||||
|
||||
isPathModified: (path) ->
|
||||
isStatusModified: (status) ->
|
||||
modifiedFlags = @statusFlags.working_dir_modified |
|
||||
@statusFlags.working_dir_delete |
|
||||
@statusFlags.working_dir_typechange |
|
||||
@statusFlags.index_modified |
|
||||
@statusFlags.index_deleted |
|
||||
@statusFlags.index_typechange
|
||||
(@getPathStatus(path) & modifiedFlags) > 0
|
||||
(status & modifiedFlags) > 0
|
||||
|
||||
isPathNew: (path) ->
|
||||
isPathModified: (path) ->
|
||||
@isStatusModified(@getPathStatus(path))
|
||||
|
||||
isStatusNew: (status) ->
|
||||
newFlags = @statusFlags.working_dir_new |
|
||||
@statusFlags.index_new
|
||||
(@getPathStatus(path) & newFlags) > 0
|
||||
(status & newFlags) > 0
|
||||
|
||||
isPathNew: (path) ->
|
||||
@isStatusNew(@getPathStatus(path))
|
||||
|
||||
relativize: (path) ->
|
||||
workingDirectory = @getWorkingDirectory()
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
'pagedown': 'core:page-down'
|
||||
|
||||
'meta-S': 'window:save-all'
|
||||
'meta-W': 'window:close'
|
||||
'meta-+': 'window:increase-font-size'
|
||||
'meta--': 'window:decrease-font-size'
|
||||
'ctrl-w w': 'window:focus-next-pane'
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
'meta-enter': 'editor:newline-below'
|
||||
'tab': 'editor:indent'
|
||||
'meta-d': 'editor:delete-line'
|
||||
'alt-meta-w': 'editor:toggle-soft-wrap'
|
||||
'ctrl-[': 'editor:fold-current-row'
|
||||
'ctrl-]': 'editor:unfold-current-row'
|
||||
'ctrl-{': 'editor:fold-all'
|
||||
@@ -33,3 +32,4 @@
|
||||
'meta-alt-p': 'editor:log-cursor-scope'
|
||||
'meta-u': 'editor:upper-case'
|
||||
'meta-U': 'editor:lower-case'
|
||||
'alt-meta-w': 'editor:close-other-editors'
|
||||
|
||||
@@ -29,6 +29,7 @@ windowAdditions =
|
||||
@pasteboard = new Pasteboard
|
||||
|
||||
$(window).on 'core:close', => @close()
|
||||
$(window).command 'window:close', => @close()
|
||||
|
||||
# This method is intended only to be run when starting a normal application
|
||||
# Note: RootView assigns itself on window on initialization so that
|
||||
@@ -46,7 +47,8 @@ windowAdditions =
|
||||
false
|
||||
|
||||
shutdown: ->
|
||||
@rootView.deactivate()
|
||||
@rootView?.deactivate()
|
||||
@rootView = null
|
||||
$(window).unbind('focus')
|
||||
$(window).unbind('blur')
|
||||
$(window).off('before')
|
||||
|
||||
@@ -30,6 +30,20 @@ describe "EventPalette", ->
|
||||
else
|
||||
expect(eventLi).not.toExist()
|
||||
|
||||
it "displays all events registerd on the window", ->
|
||||
editorEvents = rootView.getActiveEditor().events()
|
||||
windowEvents = $(window).events()
|
||||
expect(_.isEmpty(windowEvents)).toBeFalsy()
|
||||
for eventName, description of windowEvents
|
||||
eventLi = palette.list.children("[data-event-name='#{eventName}']")
|
||||
description = editorEvents[eventName] unless description
|
||||
if description
|
||||
expect(eventLi).toExist()
|
||||
expect(eventLi.find('.event-name')).toHaveText(eventName)
|
||||
expect(eventLi.find('.event-description')).toHaveText(description)
|
||||
else
|
||||
expect(eventLi).not.toExist()
|
||||
|
||||
it "focuses the mini-editor and selects the first event", ->
|
||||
expect(palette.miniEditor.isFocused).toBeTruthy()
|
||||
expect(palette.find('.event:first')).toHaveClass 'selected'
|
||||
|
||||
@@ -28,7 +28,7 @@ class EventPalette extends SelectList
|
||||
@keyBindings = _.losslessInvert(keymap.bindingsForElement(@previouslyFocusedElement))
|
||||
|
||||
events = []
|
||||
for eventName, eventDescription of @previouslyFocusedElement.events()
|
||||
for eventName, eventDescription of _.extend($(window).events(), @previouslyFocusedElement.events())
|
||||
events.push({eventName, eventDescription}) if eventDescription
|
||||
|
||||
events = _.sortBy events, (e) -> e.eventDescription
|
||||
|
||||
@@ -76,7 +76,10 @@ class StatusBar extends View
|
||||
|
||||
@gitStatusIcon.addClass('git-status octicons')
|
||||
git = @buffer.getRepo()
|
||||
if git?.isPathModified(path)
|
||||
return unless git
|
||||
|
||||
status = git.getPathStatus(path)
|
||||
if git.isStatusModified(status)
|
||||
@gitStatusIcon.addClass('modified-status-icon')
|
||||
stats = git.getDiffStats(path)
|
||||
if stats.added and stats.deleted
|
||||
@@ -87,7 +90,7 @@ class StatusBar extends View
|
||||
@gitStatusIcon.text("-#{stats.deleted}")
|
||||
else
|
||||
@gitStatusIcon.text('')
|
||||
else if git?.isPathNew(path)
|
||||
else if git.isStatusNew(status)
|
||||
@gitStatusIcon.addClass('new-status-icon')
|
||||
@gitStatusIcon.text("+#{@buffer.getLineCount()}")
|
||||
|
||||
|
||||
@@ -23,17 +23,15 @@ class DirectoryView extends View
|
||||
@disclosureArrow.on 'click', => @toggleExpansion()
|
||||
|
||||
repo = @project.repo
|
||||
iconClass = 'directory-icon'
|
||||
if repo?
|
||||
path = @directory.getPath()
|
||||
@directoryName.addClass('ignored') if repo.isPathIgnored(path)
|
||||
if path is repo.getWorkingDirectory()
|
||||
@directoryName.addClass('repository-icon')
|
||||
else if repo.isSubmodule(path)
|
||||
@directoryName.addClass('submodule-icon')
|
||||
if parent
|
||||
@directoryName.addClass('ignored') if repo.isPathIgnored(path)
|
||||
iconClass = 'submodule-icon' if repo.isSubmodule(path)
|
||||
else
|
||||
@directoryName.addClass('directory-icon')
|
||||
else
|
||||
@directoryName.addClass('directory-icon')
|
||||
iconClass = 'repository-icon' if path is repo.getWorkingDirectory()
|
||||
@directoryName.addClass(iconClass)
|
||||
|
||||
getPath: ->
|
||||
@directory.path
|
||||
|
||||
@@ -36,10 +36,12 @@ class FileView extends View
|
||||
path = @getPath()
|
||||
if repo.isPathIgnored(path)
|
||||
@addClass('ignored')
|
||||
else if repo.isPathModified(path)
|
||||
@addClass('modified')
|
||||
else if repo.isPathNew(path)
|
||||
@addClass('new')
|
||||
else
|
||||
status = repo.getPathStatus(path)
|
||||
if repo.isStatusModified(status)
|
||||
@addClass('modified')
|
||||
else if repo.isStatusNew(status)
|
||||
@addClass('new')
|
||||
|
||||
getPath: ->
|
||||
@file.path
|
||||
|
||||
Reference in New Issue
Block a user