Merge branch 'master' into api/docs

This commit is contained in:
Garen Torikian
2013-04-14 09:43:06 -07:00
28 changed files with 216 additions and 93 deletions

3
.nakignore Normal file
View File

@@ -0,0 +1,3 @@
tags
node_modules
vendor/packages

View File

@@ -265,11 +265,13 @@
}
}
- (void)applicationWillTerminate:(NSNotification *)notification {
- (NSApplicationTerminateReply)applicationShouldTerminate:
(NSApplication *)sender {
for (NSWindow *window in [self windows]) {
[window performClose:self];
}
CefShutdown();
return NSTerminateCancel;
}
# pragma mark CefAppProtocol

View File

@@ -1,6 +1,7 @@
#include <sstream>
#include <iostream>
#include <assert.h>
#include "include/cef_app.h"
#include "include/cef_path_util.h"
#include "include/cef_process_util.h"
#include "include/cef_task.h"
@@ -14,7 +15,9 @@
#define REQUIRE_IO_THREAD() assert(CefCurrentlyOn(TID_IO));
#define REQUIRE_FILE_THREAD() assert(CefCurrentlyOn(TID_FILE));
AtomCefClient::AtomCefClient(){
static int numberOfOpenBrowsers = 0;
AtomCefClient::AtomCefClient() {
}
AtomCefClient::AtomCefClient(bool handlePasteboardCommands, bool ignoreTitleChanges) {
@@ -167,17 +170,22 @@ bool AtomCefClient::OnKeyEvent(CefRefPtr<CefBrowser> browser,
void AtomCefClient::OnBeforeClose(CefRefPtr<CefBrowser> browser) {
// REQUIRE_UI_THREAD(); // When uncommented this fails when app is terminated
m_Browser = NULL;
numberOfOpenBrowsers--;
if (numberOfOpenBrowsers == 0) {
CefQuitMessageLoop();
}
}
void AtomCefClient::OnAfterCreated(CefRefPtr<CefBrowser> browser) {
REQUIRE_UI_THREAD();
AutoLock lock_scope(this);
if (!m_Browser.get()) {
if (!m_Browser.get()) {
m_Browser = browser;
}
GetBrowser()->GetHost()->SetFocus(true);
numberOfOpenBrowsers++;
}
void AtomCefClient::OnLoadError(CefRefPtr<CefBrowser> browser,

View File

@@ -86,6 +86,7 @@ class AtomCefClient : public CefClient,
// CefLifeSpanHandler methods
virtual void OnAfterCreated(CefRefPtr<CefBrowser> browser) OVERRIDE;
virtual void OnBeforeClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
virtual bool DoClose(CefRefPtr<CefBrowser> browser) OVERRIDE;
// CefLoadHandler methods
@@ -100,11 +101,13 @@ class AtomCefClient : public CefClient,
bool Save(const std::string& path, const std::string& data);
void RestartRendererProcess(CefRefPtr<CefBrowser> browser);
bool IsClosed() { return m_IsClosed; };
protected:
CefRefPtr<CefBrowser> m_Browser;
bool m_HandlePasteboardCommands = false;
bool m_IgnoreTitleChanges = false;
bool m_IsClosed = false;
void FocusNextWindow();
void FocusPreviousWindow();

View File

@@ -156,7 +156,7 @@ void AtomCefClient::Exit(int status) {
}
void AtomCefClient::Log(const char *message) {
std::cout << message << "\n";
NSLog(@"%s", message);
}
void AtomCefClient::GetVersion(int replyId, CefRefPtr<CefBrowser> browser) {
@@ -169,3 +169,10 @@ void AtomCefClient::GetVersion(int replyId, CefRefPtr<CefBrowser> browser) {
replyArguments->SetList(0, CreateReplyDescriptor(replyId, 0));
browser->SendProcessMessage(PID_RENDERER, replyMessage);
}
bool AtomCefClient::DoClose(CefRefPtr<CefBrowser> browser) {
m_IsClosed = true;
NSWindow *window = [browser->GetHost()->GetWindowHandle() window];
[window performClose:window];
return false;
}

View File

@@ -32,6 +32,7 @@ int AtomMain(int argc, char* argv[]) {
[mainNib instantiateWithOwner:application topLevelObjects:nil];
CefRunMessageLoop();
CefShutdown();
}
return 0;

View File

@@ -177,6 +177,10 @@
[self addBrowserToView:self.webView url:[urlString UTF8String] cefHandler:_cefClient];
}
- (BOOL)isBrowserUsable {
return _cefClient && !_cefClient->IsClosed() && _cefClient->GetBrowser();
}
- (void)toggleDevTools {
if (_devToolsView) {
[self hideDevTools];
@@ -188,22 +192,21 @@
- (void)showDevTools {
if (_devToolsView) return;
if (![self isBrowserUsable]) return;
if (_cefClient && _cefClient->GetBrowser()) {
NSRect webViewFrame = _webView.frame;
NSRect devToolsViewFrame = _webView.frame;
devToolsViewFrame.size.height = NSHeight(webViewFrame) / 3;
webViewFrame.size.height = NSHeight(webViewFrame) - NSHeight(devToolsViewFrame);
[_webView setFrame:webViewFrame];
_devToolsView = [[NSView alloc] initWithFrame:devToolsViewFrame];
NSRect webViewFrame = _webView.frame;
NSRect devToolsViewFrame = _webView.frame;
devToolsViewFrame.size.height = NSHeight(webViewFrame) / 3;
webViewFrame.size.height = NSHeight(webViewFrame) - NSHeight(devToolsViewFrame);
[_webView setFrame:webViewFrame];
_devToolsView = [[NSView alloc] initWithFrame:devToolsViewFrame];
[_splitView addSubview:_devToolsView];
[_splitView adjustSubviews];
[_splitView addSubview:_devToolsView];
[_splitView adjustSubviews];
_cefDevToolsClient = new AtomCefClient(true, true);
std::string devtools_url = _cefClient->GetBrowser()->GetHost()->GetDevToolsURL(true);
[self addBrowserToView:_devToolsView url:devtools_url.c_str() cefHandler:_cefDevToolsClient];
}
_cefDevToolsClient = new AtomCefClient(true, true);
std::string devtools_url = _cefClient->GetBrowser()->GetHost()->GetDevToolsURL(true);
[self addBrowserToView:_devToolsView url:devtools_url.c_str() cefHandler:_cefDevToolsClient];
}
- (void)hideDevTools {
@@ -212,17 +215,19 @@
[_devToolsView release];
_devToolsView = nil;
_cefDevToolsClient = NULL;
_cefClient->GetBrowser()->GetHost()->SetFocus(true);
if ([self isBrowserUsable]) {
_cefClient->GetBrowser()->GetHost()->SetFocus(true);
}
}
- (void)openPath:(NSString*)path {
if (_cefClient && _cefClient->GetBrowser()) {
CefRefPtr<CefProcessMessage> openMessage = CefProcessMessage::Create("openPath");
CefRefPtr<CefListValue> openArguments = openMessage->GetArgumentList();
openArguments->SetSize(1);
openArguments->SetString(0, [path UTF8String]);
_cefClient->GetBrowser()->SendProcessMessage(PID_RENDERER, openMessage);
}
if (![self isBrowserUsable]) return;
CefRefPtr<CefProcessMessage> openMessage = CefProcessMessage::Create("openPath");
CefRefPtr<CefListValue> openArguments = openMessage->GetArgumentList();
openArguments->SetSize(1);
openArguments->SetString(0, [path UTF8String]);
_cefClient->GetBrowser()->SendProcessMessage(PID_RENDERER, openMessage);
}
- (void)setPidToKillOnClose:(NSNumber *)pid {
@@ -239,14 +244,15 @@
}
- (void)windowDidBecomeMain:(NSNotification *)notification {
if (_cefClient && _cefClient->GetBrowser()) {
if ([self isBrowserUsable]) {
_cefClient->GetBrowser()->GetHost()->SetFocus(true);
}
}
- (BOOL)windowShouldClose:(NSNotification *)notification {
if (_cefClient && _cefClient->GetBrowser()) {
if ([self isBrowserUsable]) {
_cefClient->GetBrowser()->GetHost()->CloseBrowser(false);
return NO;
}
if (_pidToKillOnClose) kill([_pidToKillOnClose intValue], SIGQUIT);

View File

@@ -7,7 +7,7 @@
"ctags": "0.3.0",
"oniguruma": "0.8.0",
"mkdirp": "0.3.5",
"git-utils": "0.13.0",
"git-utils": "0.14.0",
"underscore": "1.4.4",
"d3": "3.0.8",
"coffee-cache": "0.1.0",
@@ -15,7 +15,7 @@
"async": "0.2.6",
"nak": "0.2.12",
"spellchecker": "0.3.0",
"pathwatcher": "0.1.5",
"pathwatcher": "0.3.0",
"plist": "git://github.com/nathansobo/node-plist.git",
"space-pen": "git://github.com/nathansobo/space-pen.git"
},

View File

@@ -1,5 +1,8 @@
#!/bin/sh
# exit on subprocess errors
set -o errexit
exit_unless_xcode_exists() {
if [ ! -d /Applications/Xcode.app ]; then
echo "ERROR: Atom requires Xcode"

View File

@@ -4,4 +4,5 @@ set -ex
cd "$(dirname "$0")/../.."
export PATH="/usr/local/Cellar/node/0.8.21/bin:/usr/local/bin:${PATH}"
export VERSION=$VERSION
rake clean
rake setup-codesigning create-xcode-project

View File

@@ -8,8 +8,8 @@ RESOUCES_PATH="$BUILT_PRODUCTS_DIR/$UNLOCALIZED_RESOURCES_FOLDER_PATH"
# Copy non-coffee files into bundle
rsync --archive --recursive \
--exclude="src/**/*.coffee" --exclude="src/**/*.cson" \
--exclude="src/**/*.less" --exclude="static/*.less" \
--exclude="src/**.coffee" --exclude="src/**.cson" \
--exclude="src/**.less" --exclude="static/**.less" \
--exclude="node_modules/less" \
node_modules src static vendor spec benchmark themes dot-atom atom.sh \
"${COMPILED_SOURCES_DIR}/" "$RESOUCES_PATH"

View File

@@ -2563,3 +2563,15 @@ describe "Editor", ->
editor.trigger('editor:scroll-to-cursor')
expect(editor.getFirstVisibleScreenRow()).toBe 0
expect(editor.getLastVisibleScreenRow()).toBe 2
describe "when the editor is removed", ->
it "fires a editor:will-be-removed event", ->
window.rootView = new RootView
rootView.open('sample.js')
rootView.attachToDom()
editor = rootView.getActiveView()
willBeRemovedHandler = jasmine.createSpy('fileChange')
editor.on 'editor:will-be-removed', willBeRemovedHandler
editor.getPane().destroyActiveItem()
expect(willBeRemovedHandler).toHaveBeenCalled()

View File

@@ -284,6 +284,22 @@ describe "Project", ->
expect(paths.length).toBe 0
expect(matches.length).toBe 0
it "includes files and folders that begin with a '.'", ->
projectPath = '/tmp/atom-tests/folder-with-dot-file'
filePath = fsUtils.join(projectPath, '.text')
fsUtils.write(filePath, 'match this')
project.setPath(projectPath)
paths = []
matches = []
waitsForPromise ->
project.scan /match this/, ({path, match, range}) ->
paths.push(path)
matches.push(match)
runs ->
expect(paths.length).toBe 1
expect(paths[0]).toBe filePath
expect(matches.length).toBe 1
describe "serialization", ->
it "restores the project path", ->

View File

@@ -169,14 +169,24 @@ describe "SelectList", ->
expect(selectList.detach).toHaveBeenCalled()
describe "the core:move-to-top event", ->
it "scrolls to the top and selects the first element", ->
it "scrolls to the top, selects the first element, and does not bubble the event", ->
selectList.attachToDom()
moveToTopHandler = jasmine.createSpy("moveToTopHandler")
selectList.parent().on 'core:move-to-top', moveToTopHandler
selectList.trigger 'core:move-down'
expect(list.find('li:eq(1)')).toHaveClass 'selected'
selectList.trigger 'core:move-to-top'
expect(list.find('li:first')).toHaveClass 'selected'
expect(moveToTopHandler).not.toHaveBeenCalled()
describe "the core:move-to-bottom event", ->
it "scrolls to the bottom and selects the last element", ->
it "scrolls to the bottom, selects the last element, and does not bubble the event", ->
selectList.attachToDom()
moveToBottomHandler = jasmine.createSpy("moveToBottomHandler")
selectList.parent().on 'core:move-to-bottom', moveToBottomHandler
expect(list.find('li:first')).toHaveClass 'selected'
selectList.trigger 'core:move-to-bottom'
expect(list.find('li:last')).toHaveClass 'selected'
expect(moveToBottomHandler).not.toHaveBeenCalled()

View File

@@ -1057,11 +1057,11 @@ class Editor extends View
remove: (selector, keepData) ->
return super if keepData or @removed
@trigger 'editor:will-be-removed'
super
rootView?.focus()
beforeRemove: ->
@trigger 'editor:will-be-removed'
@removed = true
@activeEditSession?.destroy()
$(window).off(".editor-#{@id}")

View File

@@ -166,7 +166,7 @@ class Pane extends View
uri = item.getUri()
atom.confirm(
"'#{item.getTitle?() ? item.getUri()}' has changes, do you want to save them?"
"Your changes will be lost if close this item without saving."
"Your changes will be lost if you close this item without saving."
"Save", => @saveItem(item, nextAction)
"Cancel", cancelAction
"Don't Save", nextAction

View File

@@ -290,7 +290,7 @@ class Project
readLine(line) if state is 'readingLines'
command = require.resolve('nak')
args = ['--ackmate', regex.source, @getPath()]
args = ['--hidden', '--ackmate', regex.source, @getPath()]
args.unshift("--addVCSIgnores") if config.get('core.excludeVcsIgnoredPaths')
new BufferedProcess({command, args, stdout, exit})
deferred

View File

@@ -32,9 +32,11 @@ class SelectList extends View
@on 'core:move-to-top', =>
@selectItem(@list.find('li:first'))
@list.scrollToTop()
false
@on 'core:move-to-bottom', =>
@selectItem(@list.find('li:last'))
@list.scrollToBottom()
false
@on 'core:confirm', => @confirmSelection()
@on 'core:cancel', => @cancel()

View File

@@ -164,13 +164,11 @@ window.reload = ->
timesReloaded = process.global.timesReloaded ? 0
++timesReloaded
restartValue = if window.location.search.indexOf('spec-bootstrap') == -1 then 10 else 3
if timesReloaded > restartValue
if timesReloaded > 3
atom.restartRendererProcess()
else
$native.reload()
process.global.timesReloaded = timesReloaded
$native.reload()
window.onerror = ->
atom.showDevTools()

View File

@@ -1,3 +1,4 @@
$ = require 'jquery'
{$$} = require 'space-pen'
Range = require 'range'
SelectList = require 'select-list'
@@ -22,7 +23,8 @@ class AutocompleteView extends SelectList
itemForElement: (match) ->
$$ ->
@li match.word
@li =>
@span match.word
handleEvents: ->
@editor.on 'editor:path-changed', => @setCurrentBuffer(@editor.getBuffer())
@@ -156,7 +158,15 @@ class AutocompleteView extends SelectList
{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()
super
@setPosition()

View File

@@ -416,3 +416,12 @@ describe "AutocompleteView", ->
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()

View File

@@ -8,3 +8,15 @@
overflow-y: scroll;
max-height: 200px;
}
.select-list.autocomplete ol li {
padding-top: 5px;
padding-bottom: 5px;
padding-left: 0px;
padding-right: 0px;
}
.autocomplete span {
padding-left: 5px;
padding-right: 5px;
}

View File

@@ -15,6 +15,7 @@ class LoadPathsTask
args.unshift('--addVCSIgnores') if config.get('core.excludeVcsIgnoredPaths')
args.unshift('--ignore', ignoredNames.join(',')) if ignoredNames.length > 0
args.unshift('--follow')
args.unshift('--hidden')
paths = []
exit = (code) =>

View File

@@ -1,4 +1,5 @@
_ = require 'underscore'
Subscriber = require 'subscriber'
module.exports =
class GitDiffView
@@ -9,58 +10,67 @@ class GitDiffView
@gutter = @editor.gutter
@diffs = {}
@editor.on 'editor:path-changed', => @subscribeToBuffer()
@editor.on 'editor:display-updated', => @renderDiffs()
git.on 'statuses-changed', =>
@subscribe @editor, 'editor:path-changed', @subscribeToBuffer
@subscribe @editor, 'editor:display-updated', @renderDiffs
@subscribe git, 'statuses-changed', =>
@diffs = {}
@scheduleDiffs()
git.on 'status-changed', (path) =>
@scheduleUpdate()
@subscribe git, 'status-changed', (path) =>
delete @diffs[path]
@scheduleDiffs() if path is @editor.getPath()
@scheduleUpdate() if path is @editor.getPath()
@subscribeToBuffer()
subscribeToBuffer: ->
beforeRemove: ->
@unsubscribe()
@unsubscribeFromBuffer()
unsubscribeFromBuffer: ->
if @buffer?
@removeDiffs()
delete @diffs[@buffer.getPath()] if @buffer.destroyed
@buffer.off '.git-diff'
@buffer.off 'contents-modified', @updateDiffs
@buffer = null
if @buffer = @editor.getBuffer()
@scheduleDiffs() unless @diffs[@buffer.getPath()]?
@buffer.on 'contents-modified.git-diff', =>
@generateDiffs()
@renderDiffs()
subscribeToBuffer: =>
@unsubscribeFromBuffer()
scheduleDiffs: ->
_.nextTick =>
@generateDiffs()
@renderDiffs()
if @buffer = @editor.getBuffer()
@scheduleUpdate() unless @diffs[@buffer.getPath()]?
@buffer.on 'contents-modified', @updateDiffs
scheduleUpdate: ->
_.nextTick(@updateDiffs)
updateDiffs: =>
@generateDiffs()
@renderDiffs()
generateDiffs: ->
if path = @buffer.getPath()
@diffs[path] = git?.getLineDiffs(path, @buffer.getText())
removeDiffs: ->
removeDiffs: =>
if @gutter.hasGitLineDiffs
@gutter.find('.line-number').removeClass('git-line-added git-line-modified git-line-removed')
@gutter.hasGitLineDiffs = false
renderDiffs: ->
renderDiffs: =>
return unless @gutter.isVisible()
@removeDiffs()
hunks = @diffs[@editor.getPath()] ? []
linesHighlighted = 0
for hunk in hunks
if hunk.oldLines is 0 and hunk.newLines > 0
for row in [hunk.newStart...hunk.newStart + hunk.newLines]
for {oldStart, newStart, oldLines, newLines} in hunks
if oldLines is 0 and newLines > 0
for row in [newStart...newStart + newLines]
linesHighlighted += @gutter.find(".line-number[lineNumber=#{row - 1}]").addClass('git-line-added').length
else if hunk.newLines is 0 and hunk.oldLines > 0
linesHighlighted += @gutter.find(".line-number[lineNumber=#{hunk.newStart - 1}]").addClass('git-line-removed').length
else if newLines is 0 and oldLines > 0
linesHighlighted += @gutter.find(".line-number[lineNumber=#{newStart - 1}]").addClass('git-line-removed').length
else
for row in [hunk.newStart...hunk.newStart + hunk.newLines]
for row in [newStart...newStart + newLines]
linesHighlighted += @gutter.find(".line-number[lineNumber=#{row - 1}]").addClass('git-line-modified').length
@gutter.hasGitLineDiffs = linesHighlighted > 0
_.extend GitDiffView.prototype, Subscriber

View File

@@ -11,27 +11,30 @@ class SpellCheckView extends View
views: []
initialize: (@editor) ->
@subscribe @editor, 'editor:path-changed', => @subscribeToBuffer()
@subscribe @editor, 'editor:grammar-changed', => @subscribeToBuffer()
@observeConfig 'editor.fontSize', => @subscribeToBuffer()
@observeConfig 'spell-check.grammars', => @subscribeToBuffer()
@subscribe @editor, 'editor:path-changed', @subscribeToBuffer
@subscribe @editor, 'editor:grammar-changed', @subscribeToBuffer
@observeConfig 'editor.fontSize', @subscribeToBuffer
@observeConfig 'spell-check.grammars', @subscribeToBuffer
@subscribeToBuffer()
beforeRemove: ->
@unsubscribeFromBuffer()
unsubscribeFromBuffer: ->
@destroyViews()
@task?.abort()
if @buffer?
@buffer.off '.spell-check'
@buffer.off 'contents-modified', @updateMisspellings
@buffer = null
subscribeToBuffer: ->
subscribeToBuffer: =>
@unsubscribeFromBuffer()
if @spellCheckCurrentGrammar()
@buffer = @editor.getBuffer()
@buffer.on 'contents-modified.spell-check', => @updateMisspellings()
@buffer.on 'contents-modified', @updateMisspellings
@updateMisspellings()
spellCheckCurrentGrammar: ->
@@ -49,11 +52,7 @@ class SpellCheckView extends View
@views.push(view)
@append(view)
updateMisspellings: ->
unless @editor.activeEditSession?
@unsubscribeFromBuffer()
return
updateMisspellings: =>
@task?.abort()
callback = (misspellings) =>

View File

@@ -35,23 +35,31 @@ class StatusBarView extends View
if git?
@subscribe git, 'status-changed', (path, status) =>
@updateStatusBar() if path is @getActiveItemPath()
@subscribe git, 'statuses-changed', =>
@updateStatusBar()
@subscribe git, 'statuses-changed', @updateStatusBar
@subscribeToBuffer()
beforeRemove: ->
@unsubscribeFromBuffer()
getActiveItemPath: ->
@pane.activeItem?.getPath?()
unsubscribeFromBuffer: ->
if @buffer?
@buffer.off 'modified-status-changed', @updateBufferHasModifiedText
@buffer.off 'saved', @updateStatusBar
@buffer = null
subscribeToBuffer: ->
@buffer?.off '.status-bar'
@unsubscribeFromBuffer()
if @buffer = @pane.activeItem.getBuffer?()
@buffer.on 'modified-status-changed.status-bar', (isModified) => @updateBufferHasModifiedText(isModified)
@buffer.on 'saved.status-bar', => @updateStatusBar()
@buffer.on 'modified-status-changed', @updateBufferHasModifiedText
@buffer.on 'saved', @updateStatusBar
@updateStatusBar()
updateStatusBar: ->
updateStatusBar: =>
@updateGrammarText()
@updateBranchText()
@updateBufferHasModifiedText(@buffer?.isModified())
@@ -65,7 +73,7 @@ class StatusBarView extends View
else
@grammarName.text(grammar.name).show()
updateBufferHasModifiedText: (isModified)->
updateBufferHasModifiedText: (isModified) =>
if isModified
@bufferModified.text('*') unless @isModified
@isModified = true

View File

@@ -1,16 +1,17 @@
ctags = require 'ctags'
fsUtils = require 'fs-utils'
path = require 'path'
module.exports =
getTagsFile: (path) ->
tagsFile = fsUtils.join(path, "tags")
getTagsFile: (tagsFilePath) ->
tagsFile = path.join(tagsFilePath, "tags")
return tagsFile if fsUtils.isFile(tagsFile)
tagsFile = fsUtils.join(path, "TAGS")
tagsFile = path.join(tagsFilePath, "TAGS")
return tagsFile if fsUtils.isFile(tagsFile)
loadTags: (path) ->
tagsFile = @getTagsFile(path)
loadTags: (tagsFilePath) ->
tagsFile = @getTagsFile(tagsFilePath)
if tagsFile
callTaskMethod("tagsLoaded", ctags.getTags(tagsFile))
else

View File

@@ -1,5 +1,6 @@
.select-list.popover-list {
width: 200px;
min-width: 200px;
border: 2px solid #222;
-webkit-box-shadow: 0 0 3px 3px rgba(0, 0, 0, .5);
margin-left: 0px;
@@ -14,4 +15,4 @@
.select-list.popover-list ol li {
padding: 5px;
}
}