Merge pull request #306 from github/dev

Merge dev into master
This commit is contained in:
Kevin Sawicki
2013-02-28 09:54:44 -08:00
94 changed files with 1291 additions and 390 deletions

23
.gitmodules vendored
View File

@@ -46,6 +46,27 @@
[submodule "vendor/packages/python.tmbundle"]
path = vendor/packages/python.tmbundle
url = https://github.com/textmate/python.tmbundle
[submodule "vendor/packages/clojure.tmbundle"]
path = vendor/packages/clojure.tmbundle
url = https://github.com/mmcgrana/textmate-clojure
[submodule "prebuilt-cef"]
path = prebuilt-cef
url = https://github.com/github/prebuilt-cef
url = git@github.com:github/prebuilt-cef.git
[submodule "vendor/packages/yaml.tmbundle"]
path = vendor/packages/yaml.tmbundle
url = https://github.com/textmate/yaml.tmbundle.git
[submodule "vendor/packages/java.tmbundle"]
path = vendor/packages/java.tmbundle
url = https://github.com/textmate/java.tmbundle.git
[submodule "vendor/packages/php.tmbundle"]
path = vendor/packages/php.tmbundle
url = https://github.com/textmate/php.tmbundle.git
[submodule "vendor/packages/perl.tmbundle"]
path = vendor/packages/perl.tmbundle
url = https://github.com/textmate/perl.tmbundle.git
[submodule "vendor/packages/sass.tmbundle"]
path = vendor/packages/sass.tmbundle
url = https://github.com/alexsancho/SASS.tmbundle.git
[submodule "vendor/packages/less.tmbundle"]
path = vendor/packages/less.tmbundle
url = https://github.com/mathewbyrne/less.tmbundle.git

View File

@@ -17,4 +17,3 @@ Requirements
1. gh-setup atom
2. cd ~/github/atom && `rake install`

View File

@@ -1,9 +1,6 @@
ATOM_SRC_PATH = File.dirname(__FILE__)
DOT_ATOM_PATH = ENV['HOME'] + "/.atom"
BUILD_DIR = 'atom-build'
require 'erb'
desc "Build Atom via `xcodebuild`"
task :build => "create-xcode-project" do
command = "xcodebuild -target Atom -configuration Release SYMROOT=#{BUILD_DIR}"
@@ -17,7 +14,7 @@ end
desc "Create xcode project from gyp file"
task "create-xcode-project" => "update-cef" do
`rm -rf atom.xcodeproj`
`gyp --depth=. atom.gyp`
`gyp --depth=. -D CODE_SIGN="#{ENV['CODE_SIGN']}" atom.gyp`
end
desc "Update CEF to the latest version specified by the prebuilt-cef submodule"
@@ -32,50 +29,56 @@ task "bootstrap" do
`script/bootstrap`
end
desc "Creates symlink from `application_path() to /Applications/Atom and creates `atom` cli app"
desc "Copies Atom.app to /Applications and creates `atom` cli app"
task :install => [:clean, :build] do
path = application_path()
exit 1 if not path
# Install Atom.app
dest = "/Applications/#{File.basename(path)}"
`rm -rf #{dest}`
`cp -r #{path} #{File.expand_path(dest)}`
dest_path = "/Applications/#{File.basename(path)}"
`rm -rf #{dest_path}`
`cp -r #{path} #{File.expand_path(dest_path)}`
# Install cli atom
usr_bin_path = "/opt/github/bin"
cli_path = "#{usr_bin_path}/atom"
template = ERB.new CLI_SCRIPT
namespace = OpenStruct.new(:application_path => dest, :resource_path => ATOM_SRC_PATH)
File.open(cli_path, "w") do |f|
f.write template.result(namespace.instance_eval { binding })
f.chmod(0755)
# Install atom cli
if File.directory?("/opt/boxen")
cli_path = "/opt/boxen/bin/atom"
else
cli_path = "/opt/github/bin/atom"
end
Rake::Task["create-dot-atom"].invoke()
FileUtils.cp("#{ATOM_SRC_PATH}/atom.sh", cli_path)
FileUtils.chmod(0755, cli_path)
Rake::Task["clone-default-bundles"].invoke()
puts "\033[32mType `atom` to start Atom! In Atom press `cmd-,` to edit your `~/.atom` directory\033[0m"
puts "\033[32mAtom is installed at `#{dest_path}`. Atom cli is installed at `#{cli_path}`\033[0m"
end
desc "Creates .atom file if non exists"
task "create-dot-atom" do
dot_atom_template_path = ATOM_SRC_PATH + "/dot-atom"
desc "Package up the app for speakeasy"
task :package => ["setup-codesigning", "bump-patch-number", "build"] do
path = application_path()
exit 1 if not path
if File.exists?(DOT_ATOM_PATH)
user_config = "#{DOT_ATOM_PATH}/user.coffee"
old_user_config = "#{DOT_ATOM_PATH}/atom.coffee"
dest_path = '/tmp/atom-for-speakeasy/Atom.tar.bz2'
`mkdir -p $(dirname #{dest_path})`
`rm -rf #{dest_path}`
`tar --directory $(dirname #{path}) -jcf #{dest_path} $(basename #{path})`
`open $(dirname #{dest_path})`
end
if File.exists?(old_user_config)
`mv #{old_user_config} #{user_config}`
puts "\033[32mRenamed #{old_user_config} to #{user_config}\033[0m"
end
else
`mkdir "#{DOT_ATOM_PATH}"`
`cp -r "#{dot_atom_template_path}/" "#{DOT_ATOM_PATH}"/`
`cp -r "#{ATOM_SRC_PATH}/themes/" "#{DOT_ATOM_PATH}"/themes/`
end
task "setup-codesigning" do
ENV['CODE_SIGN'] = "Developer ID Application: GitHub"
end
desc "Bump patch number"
task "bump-patch-number" do
version_number = `/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString:" ./native/mac/info.plist`
major, minor, patch = version_number.match(/(\d+)\.(\d+)\.(\d+)/)[1..-1].map {|o| o.to_i}
`/usr/libexec/PlistBuddy -c "Set :CFBundleShortVersionString #{major}.#{minor}.#{patch + 1}" ./native/mac/info.plist`
`/usr/libexec/PlistBuddy -c "Set :CFBundleVersion #{major}.#{minor}.#{patch + 1}" ./native/mac/info.plist`
new_version_number = `/usr/libexec/PlistBuddy -c "Print :CFBundleShortVersionString:" ./native/mac/info.plist`
puts "Bumped from #{version_number.strip} to #{new_version_number.strip}"
end
desc "Clone default bundles into vendor/bundles directory"
@@ -96,7 +99,6 @@ desc "Run the specs"
task :test => ["update-cef", "clone-default-bundles", "build"] do
`pkill Atom`
if path = application_path()
`rm -rf path`
cmd = "#{path}/Contents/MacOS/Atom --test --resource-path=#{ATOM_SRC_PATH} 2> /dev/null"
system(cmd)
exit($?.exitstatus)
@@ -130,30 +132,3 @@ def application_path
return nil
end
CLI_SCRIPT = <<-EOF
#!/bin/sh
open <%= application_path %> -n --args --resource-path="<%= resource_path %>" --executed-from="$(pwd)" --pid=$$ $@
# Used to exit process when atom is used as $EDITOR
on_die() {
exit 0
}
trap 'on_die' SIGQUIT SIGTERM
# Don't exit process if we were told to wait.
while [ "$#" -gt "0" ]; do
case $1 in
-W|--wait)
WAIT=1
;;
esac
shift
done
if [ $WAIT ]; then
while true; do
sleep 1
done
fi
EOF

View File

@@ -80,6 +80,9 @@
'OTHER_LDFLAGS': ['-Wl,-headerpad_max_install_names'], # Necessary to avoid an "install_name_tool: changing install names or rpaths can't be redone" error.
},
'conditions': [
['CODE_SIGN' , {
'xcode_settings': {'CODE_SIGN_IDENTITY': "<(CODE_SIGN)"},
}],
['OS=="win" and win_use_allocator_shim==1', {
'dependencies': [
'<(DEPTH)/base/allocator/allocator.gyp:allocator',

24
atom.sh Normal file
View File

@@ -0,0 +1,24 @@
#!/bin/sh
open -a /Applications/Atom.app -n --args --executed-from="$(pwd)" --pid=$$ $@
# Used to exit process when atom is used as $EDITOR
on_die() {
exit 0
}
trap 'on_die' SIGQUIT SIGTERM
# Don't exit process if we were told to wait.
while [ "$#" -gt "0" ]; do
case $1 in
-W|--wait)
WAIT=1
;;
esac
shift
done
if [ $WAIT ]; then
while true; do
sleep 1
done
fi

View File

@@ -1,6 +1,6 @@
# Getting Started
Welcome to Atom. This documentation is intented to offer a basic introduction
Welcome to Atom. This documentation is intended to offer a basic introduction
of how to get productive with this editor. Then we'll delve into more details
about configuring, theming, and extending Atom.
@@ -36,6 +36,15 @@ bindings, but here's a list of a few useful commands.
## Usage Basics
### If You See A Rendering Bug
Things are pretty stable, but we think we have a couple rendering bugs lurking
that are hard to reproduce. If you see one, please hit `meta-p` and type
"save debug snapshot". Run that command to save a snapshot of the misbehaving
editor and send it to us, along with a screenshot and your best description of
how you produced the bug. Refreshing with `meta-r` should usually resolve the
issue so you can keep working.
### Working With Files
#### Finding Files

View File

@@ -4,6 +4,7 @@
class AtomCefClient;
@interface AtomApplication : NSApplication <CefAppProtocol, NSApplicationDelegate> {
IBOutlet NSMenuItem *_versionMenuItem;
NSWindowController *_backgroundWindowController;
NSDictionary *_arguments;
NSInvocation *_updateInvocation;
@@ -23,8 +24,6 @@ class AtomCefClient;
- (void)runSpecsThenExit:(BOOL)exitWhenDone;
- (NSDictionary *)arguments;
- (void)runBenchmarksThenExit:(BOOL)exitWhenDone;
- (NSString *)updateStatus;
- (void)installUpdate;
@property (nonatomic, retain) NSDictionary *arguments;

View File

@@ -50,20 +50,20 @@
{ "resource-path", required_argument, NULL, 'R' },
{ "benchmark", no_argument, NULL, 'B' },
{ "test", no_argument, NULL, 'T' },
{ "stable", no_argument, NULL, 'S' },
{ "dev", no_argument, NULL, 'D' },
{ "pid", required_argument, NULL, 'P' },
{ "wait", no_argument, NULL, 'W' },
{ NULL, 0, NULL, 0 }
};
while ((opt = getopt_long(cleanArgc, cleanArgv, "K:R:BYSP:Wh?", longopts, &longindex)) != -1) {
while ((opt = getopt_long(cleanArgc, cleanArgv, "K:R:BYDP:Wh?", longopts, &longindex)) != -1) {
NSString *key, *value;
switch (opt) {
case 'K':
case 'R':
case 'B':
case 'T':
case 'S':
case 'D':
case 'W':
case 'P':
key = [NSString stringWithUTF8String:longopts[longindex].name];
@@ -123,8 +123,10 @@
+ (CefSettings)createCefSettings {
CefSettings settings;
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
NSString *userAgent = [NSString stringWithFormat:@"GitHubAtom/%@", version];
CefString(&settings.cache_path) = [[self supportDirectory] UTF8String];
CefString(&settings.user_agent) = "";
CefString(&settings.user_agent) = [userAgent UTF8String];
CefString(&settings.log_file) = "";
CefString(&settings.javascript_flags) = "";
settings.remote_debugging_port = 9090;
@@ -159,8 +161,8 @@
[self open:path pidToKillWhenWindowCloses:nil];
}
- (void)openUnstable:(NSString *)path {
[[AtomWindowController alloc] initUnstableWithPath:path];
- (void)openDev:(NSString *)path {
[[AtomWindowController alloc] initDevWithPath:path];
}
- (IBAction)runSpecs:(id)sender {
@@ -213,18 +215,24 @@
}
- (void)applicationWillFinishLaunching:(NSNotification *)notification {
SUUpdater.sharedUpdater.delegate = self;
SUUpdater.sharedUpdater.automaticallyChecksForUpdates = YES;
SUUpdater.sharedUpdater.automaticallyDownloadsUpdates = YES;
[SUUpdater.sharedUpdater checkForUpdatesInBackground];
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
_versionMenuItem.title = [NSString stringWithFormat:@"Version %@", version];
_backgroundWindowController = [[AtomWindowController alloc] initInBackground];
if ([self.arguments objectForKey:@"benchmark"]) {
[self runBenchmarksThenExit:true];
}
else if ([self.arguments objectForKey:@"test"]) {
[self runSpecsThenExit:true];
}
else {
_backgroundWindowController = [[AtomWindowController alloc] initInBackground];
if (![self.arguments objectForKey:@"dev"]) {
SUUpdater.sharedUpdater.delegate = self;
SUUpdater.sharedUpdater.automaticallyChecksForUpdates = YES;
SUUpdater.sharedUpdater.automaticallyDownloadsUpdates = YES;
[SUUpdater.sharedUpdater checkForUpdatesInBackground];
}
}
}
- (void)applicationWillTerminate:(NSNotification *)notification {
@@ -257,35 +265,25 @@
}
}
- (NSString *)updateStatus {
return _updateStatus ? _updateStatus : @"idle";
}
- (void)installUpdate {
if (_updateInvocation) [_updateInvocation invoke];
}
#pragma mark SUUpdaterDelegate
- (void)updaterDidNotFindUpdate:(SUUpdater *)update {
_updateStatus = @"current";
}
- (void)updater:(SUUpdater *)updater didFindValidUpdate:(SUAppcastItem *)update {
_updateStatus = @"downloading";
}
- (void)updater:(SUUpdater *)updater willExtractUpdate:(SUAppcastItem *)update {
_updateStatus = @"installing";
}
- (void)updater:(SUUpdater *)updater willInstallUpdateOnQuit:(SUAppcastItem *)update immediateInstallationInvocation:(NSInvocation *)invocation {
_updateInvocation = invocation;
_updateStatus = @"ready";
_updateInvocation = [invocation retain];
_versionMenuItem.title = [NSString stringWithFormat:@"Update to %@", update.versionString];
_versionMenuItem.target = _updateInvocation;
_versionMenuItem.action = @selector(invoke);
}
- (void)updater:(SUUpdater *)updater didCancelInstallUpdateOnQuit:(SUAppcastItem *)update {
_updateStatus = @"current";
}
@end

View File

@@ -36,9 +36,9 @@ bool AtomCefClient::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
bool hasArguments = argumentList->GetSize() > 1;
hasArguments ? Open(argumentList->GetString(1)) : Open();
}
if (name == "openUnstable") {
if (name == "openDev") {
bool hasArguments = argumentList->GetSize() > 1;
hasArguments ? OpenUnstable(argumentList->GetString(1)) : OpenUnstable();
hasArguments ? OpenDev(argumentList->GetString(1)) : OpenDev();
}
else if (name == "newWindow") {
NewWindow();
@@ -84,11 +84,8 @@ bool AtomCefClient::OnProcessMessageReceived(CefRefPtr<CefBrowser> browser,
else if (name == "toggleFullScreen") {
ToggleFullScreen(browser);
}
else if (name == "update") {
Update();
}
else if (name == "getUpdateStatus") {
GetUpdateStatus(messageId, browser);
else if (name == "getVersion") {
GetVersion(messageId, browser);
}
else {
return false;

View File

@@ -109,9 +109,8 @@ class AtomCefClient : public CefClient,
void FocusPreviousWindow();
void Open(std::string path);
void Open();
void OpenUnstable(std::string path);
void OpenUnstable();
void OpenDev(std::string path);
void OpenDev();
void NewWindow();
void ToggleDevTools(CefRefPtr<CefBrowser> browser);
void ShowDevTools(CefRefPtr<CefBrowser> browser);
@@ -126,8 +125,7 @@ class AtomCefClient : public CefClient,
void Log(const char *message);
void Show(CefRefPtr<CefBrowser> browser);
void ToggleFullScreen(CefRefPtr<CefBrowser> browser);
void Update();
void GetUpdateStatus(int replyId, CefRefPtr<CefBrowser> browser);
void GetVersion(int replyId, CefRefPtr<CefBrowser> browser);
IMPLEMENT_REFCOUNTING(AtomCefClient);
IMPLEMENT_LOCKING(AtomCefClient);

View File

@@ -56,17 +56,17 @@ void AtomCefClient::Open() {
}
}
void AtomCefClient::OpenUnstable(std::string path) {
void AtomCefClient::OpenDev(std::string path) {
NSString *pathString = [NSString stringWithCString:path.c_str() encoding:NSUTF8StringEncoding];
[(AtomApplication *)[AtomApplication sharedApplication] openUnstable:pathString];
[(AtomApplication *)[AtomApplication sharedApplication] openDev:pathString];
}
void AtomCefClient::OpenUnstable() {
void AtomCefClient::OpenDev() {
NSOpenPanel *panel = [NSOpenPanel openPanel];
[panel setCanChooseDirectories:YES];
if ([panel runModal] == NSFileHandlingPanelOKButton) {
NSURL *url = [[panel URLs] lastObject];
OpenUnstable([[url path] UTF8String]);
OpenDev([[url path] UTF8String]);
}
}
@@ -159,17 +159,13 @@ void AtomCefClient::Log(const char *message) {
std::cout << message << "\n";
}
void AtomCefClient::Update() {
[(AtomApplication *)NSApp installUpdate];
}
void AtomCefClient::GetUpdateStatus(int replyId, CefRefPtr<CefBrowser> browser) {
void AtomCefClient::GetVersion(int replyId, CefRefPtr<CefBrowser> browser) {
CefRefPtr<CefProcessMessage> replyMessage = CefProcessMessage::Create("reply");
CefRefPtr<CefListValue> replyArguments = replyMessage->GetArgumentList();
NSString *version = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"];
replyArguments->SetSize(2);
replyArguments->SetString(1, [[(AtomApplication *)NSApp updateStatus] UTF8String]);
replyArguments->SetString(1, [version UTF8String]);
replyArguments->SetList(0, CreateReplyDescriptor(replyId, 0));
browser->SendProcessMessage(PID_RENDERER, replyMessage);
}
}

View File

@@ -25,7 +25,7 @@ class AtomCefClient;
@property (nonatomic, retain) NSString *pathToOpen;
- (id)initWithPath:(NSString *)path;
- (id)initUnstableWithPath:(NSString *)path;
- (id)initDevWithPath:(NSString *)path;
- (id)initInBackground;
- (id)initSpecsThenExit:(BOOL)exitWhenDone;
- (id)initBenchmarksThenExit:(BOOL)exitWhenDone;

View File

@@ -33,6 +33,17 @@
AtomApplication *atomApplication = (AtomApplication *)[AtomApplication sharedApplication];
_resourcePath = [atomApplication.arguments objectForKey:@"resource-path"];
if (!alwaysUseBundleResourcePath && !_resourcePath) {
NSString *defaultRepositoryPath = @"~/github/atom";
defaultRepositoryPath = [defaultRepositoryPath stringByStandardizingPath];
if ([defaultRepositoryPath characterAtIndex:0] == '/') {
BOOL isDir = false;
BOOL exists = [[NSFileManager defaultManager] fileExistsAtPath:defaultRepositoryPath isDirectory:&isDir];
if (isDir && exists)
_resourcePath = defaultRepositoryPath;
}
}
if (alwaysUseBundleResourcePath || !_resourcePath) {
_resourcePath = [[NSBundle mainBundle] resourcePath];
}
@@ -52,11 +63,11 @@
- (id)initWithPath:(NSString *)path {
_pathToOpen = [path retain];
AtomApplication *atomApplication = (AtomApplication *)[AtomApplication sharedApplication];
BOOL stable = [atomApplication.arguments objectForKey:@"stable"] != nil;
return [self initWithBootstrapScript:@"window-bootstrap" background:NO alwaysUseBundleResourcePath:stable];
BOOL useBundleResourcePath = [atomApplication.arguments objectForKey:@"dev"] == nil;
return [self initWithBootstrapScript:@"window-bootstrap" background:NO alwaysUseBundleResourcePath:useBundleResourcePath];
}
- (id)initUnstableWithPath:(NSString *)path {
- (id)initDevWithPath:(NSString *)path {
_pathToOpen = [path retain];
AtomApplication *atomApplication = (AtomApplication *)[AtomApplication sharedApplication];
return [self initWithBootstrapScript:@"window-bootstrap" background:NO alwaysUseBundleResourcePath:false];
@@ -64,9 +75,9 @@
- (id)initInBackground {
AtomApplication *atomApplication = (AtomApplication *)[AtomApplication sharedApplication];
BOOL stable = [atomApplication.arguments objectForKey:@"stable"] != nil;
BOOL useBundleResourcePath = [atomApplication.arguments objectForKey:@"dev"] == nil;
[self initWithBootstrapScript:@"window-bootstrap" background:YES alwaysUseBundleResourcePath:stable];
[self initWithBootstrapScript:@"window-bootstrap" background:YES alwaysUseBundleResourcePath:useBundleResourcePath];
[self.window setFrame:NSMakeRect(0, 0, 0, 0) display:NO];
[self.window setExcludedFromWindowsMenu:YES];
[self.window setCollectionBehavior:NSWindowCollectionBehaviorStationary];

View File

@@ -2,13 +2,13 @@
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
<data>
<int key="IBDocument.SystemTarget">1050</int>
<string key="IBDocument.SystemVersion">12C54</string>
<string key="IBDocument.InterfaceBuilderVersion">2840</string>
<string key="IBDocument.SystemVersion">12C3103</string>
<string key="IBDocument.InterfaceBuilderVersion">3084</string>
<string key="IBDocument.AppKitVersion">1187.34</string>
<string key="IBDocument.HIToolboxVersion">625.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="NS.object.0">2840</string>
<string key="NS.object.0">3084</string>
</object>
<object class="NSArray" key="IBDocument.IntegratedClassDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
@@ -105,6 +105,15 @@
<reference key="NSOnImage" ref="293900348"/>
<reference key="NSMixedImage" ref="169361956"/>
</object>
<object class="NSMenuItem" id="781500792">
<reference key="NSMenu" ref="110575045"/>
<bool key="NSIsDisabled">YES</bool>
<string key="NSTitle">Version Info</string>
<string key="NSKeyEquiv"/>
<int key="NSMnemonicLoc">2147483647</int>
<reference key="NSOnImage" ref="293900348"/>
<reference key="NSMixedImage" ref="169361956"/>
</object>
<object class="NSMenuItem" id="139213332">
<reference key="NSMenu" ref="110575045"/>
<bool key="NSIsDisabled">YES</bool>
@@ -213,6 +222,14 @@
</object>
<int key="connectionID">447</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">_versionMenuItem</string>
<reference key="source" ref="1050"/>
<reference key="destination" ref="781500792"/>
</object>
<int key="connectionID">465</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">terminate:</string>
@@ -323,6 +340,7 @@
<reference ref="574677965"/>
<reference ref="90109190"/>
<reference ref="884100265"/>
<reference ref="781500792"/>
</object>
<reference key="parent" ref="694149608"/>
</object>
@@ -411,6 +429,11 @@
<reference key="object" ref="884100265"/>
<reference key="parent" ref="110575045"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">464</int>
<reference key="object" ref="781500792"/>
<reference key="parent" ref="110575045"/>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
@@ -435,6 +458,7 @@
<string>458.IBPluginDependency</string>
<string>459.IBPluginDependency</string>
<string>460.IBPluginDependency</string>
<string>464.IBPluginDependency</string>
<string>56.IBPluginDependency</string>
<string>57.IBPluginDependency</string>
</object>
@@ -460,6 +484,7 @@
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
</object>
</object>
<object class="NSMutableDictionary" key="unlocalizedProperties">
@@ -474,7 +499,7 @@
<reference key="dict.values" ref="755588897"/>
</object>
<nil key="sourceID"/>
<int key="maxID">463</int>
<int key="maxID">465</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
@@ -514,6 +539,17 @@
</object>
</object>
</object>
<object class="NSMutableDictionary" key="outlets">
<string key="NS.key.0">_versionMenuItem</string>
<string key="NS.object.0">NSMenuItem</string>
</object>
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
<string key="NS.key.0">_versionMenuItem</string>
<object class="IBToOneOutletInfo" key="NS.object.0">
<string key="name">_versionMenuItem</string>
<string key="candidateClassName">NSMenuItem</string>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/AtomApplication.h</string>

Binary file not shown.

View File

@@ -27,7 +27,8 @@ namespace v8_extensions {
"exists", "read", "write", "absolute", "getAllFilePathsAsync", "traverseTree", "isDirectory",
"isFile", "remove", "writeToPasteboard", "readFromPasteboard", "quit", "watchPath", "unwatchPath",
"getWatchedPaths", "unwatchAllPaths", "makeDirectory", "move", "moveToTrash", "reload", "lastModified",
"md5ForPath", "exec", "getPlatform", "setWindowState", "getWindowState"
"md5ForPath", "exec", "getPlatform", "setWindowState", "getWindowState", "isMisspelled",
"getCorrectionsForMisspelling"
};
CefRefPtr<CefV8Value> nativeObject = CefV8Value::CreateObject(NULL);
@@ -447,41 +448,49 @@ namespace v8_extensions {
context->Exit();
stdout.fileHandleForReading.writeabilityHandler = nil;
stderr.fileHandleForReading.writeabilityHandler = nil;
stdout.fileHandleForReading.readabilityHandler = nil;
stderr.fileHandleForReading.readabilityHandler = nil;
};
task.terminationHandler = ^(NSTask *) {
NSString *output = [[NSString alloc] initWithData:[[stdout fileHandleForReading] readDataToEndOfFile] encoding:NSUTF8StringEncoding];
NSString *errorOutput = [[NSString alloc] initWithData:[[stderr fileHandleForReading] readDataToEndOfFile] encoding:NSUTF8StringEncoding];
dispatch_sync(dispatch_get_main_queue(), ^() {
taskTerminatedHandle(output, errorOutput);
});
[output release];
[errorOutput release];
@synchronized(task) {
NSData *outputData = [[stdout fileHandleForReading] readDataToEndOfFile];
NSString *output = [[NSString alloc] initWithData:outputData encoding:NSUTF8StringEncoding];
NSData *errorData = [[stderr fileHandleForReading] readDataToEndOfFile];
NSString *errorOutput = [[NSString alloc] initWithData:errorData encoding:NSUTF8StringEncoding];
dispatch_sync(dispatch_get_main_queue(), ^() {
taskTerminatedHandle(output, errorOutput);
});
[output release];
[errorOutput release];
}
};
CefRefPtr<CefV8Value> stdoutFunction = options->GetValue("stdout");
if (stdoutFunction->IsFunction()) {
stdout.fileHandleForReading.writeabilityHandler = ^(NSFileHandle *fileHandle) {
NSData *data = [fileHandle availableData];
NSString *contents = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
dispatch_sync(dispatch_get_main_queue(), ^() {
outputHandle(contents, stdoutFunction);
});
[contents release];
stdout.fileHandleForReading.readabilityHandler = ^(NSFileHandle *fileHandle) {
@synchronized(task) {
NSData *data = [fileHandle availableData];
NSString *contents = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
dispatch_sync(dispatch_get_main_queue(), ^() {
outputHandle(contents, stdoutFunction);
});
[contents release];
}
};
}
CefRefPtr<CefV8Value> stderrFunction = options->GetValue("stderr");
if (stderrFunction->IsFunction()) {
stderr.fileHandleForReading.writeabilityHandler = ^(NSFileHandle *fileHandle) {
NSData *data = [fileHandle availableData];
NSString *contents = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
dispatch_sync(dispatch_get_main_queue(), ^() {
outputHandle(contents, stderrFunction);
});
[contents release];
stderr.fileHandleForReading.readabilityHandler = ^(NSFileHandle *fileHandle) {
@synchronized(task) {
NSData *data = [fileHandle availableData];
NSString *contents = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
dispatch_sync(dispatch_get_main_queue(), ^() {
outputHandle(contents, stderrFunction);
});
[contents release];
}
};
}
@@ -513,6 +522,29 @@ namespace v8_extensions {
return true;
}
else if (name == "isMisspelled") {
NSString *word = stringFromCefV8Value(arguments[0]);
NSRange range = [[NSSpellChecker sharedSpellChecker] checkSpellingOfString:word startingAt:0];
retval = CefV8Value::CreateBool(range.length > 0);
return true;
}
else if (name == "getCorrectionsForMisspelling") {
NSString *misspelling = stringFromCefV8Value(arguments[0]);
NSSpellChecker *spellchecker = [NSSpellChecker sharedSpellChecker];
NSString *language = [spellchecker language];
NSRange range;
range.location = 0;
range.length = [misspelling length];
NSArray *guesses = [spellchecker guessesForWordRange:range inString:misspelling language:language inSpellDocumentWithTag:0];
CefRefPtr<CefV8Value> v8Guesses = CefV8Value::CreateArray([guesses count]);
for (int i = 0; i < [guesses count]; i++) {
v8Guesses->SetValue(i, CefV8Value::CreateString([[guesses objectAtIndex:i] UTF8String]));
}
retval = v8Guesses;
return true;
}
return false;
};

View File

@@ -1,27 +0,0 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAzLmZ/pTYsc9Wu1+nbf2ri7HjwkJG2LlwS18t+LkSUBNWH7/2
plHjd4govWGGZSWdAR9bS7MpNIKdgypWXXIRTZxOgl91p+vm//brYZ+1fObC+cpA
/+AflzKICs0XMUdyJNUO9RNaqLYoH664cidVkgJdgVStvBxFZE8v7HmNW8R9/w6J
e0epeSCW7FnzUBZJmGJ0gw+mIQvHdqSt/57T7/TIGrpKQAZ1bWuYIwP2IteqiEiS
kmLbXbzbnQnGVD9zKfHry3XHkX550qCBtV0C4Gxnxh6QU6pa9wlgHt1iJoRWWPVS
mDOAywkMcADgP0yM8qDusxYA7rXJd+AQO51t6QIDAQABAoIBAQCifboln5yy3L+Z
bx2kJpBvC12cRSCYSe9AU6ZYYQSQ/BgCkUnCzwI9UJwNXIfgZk9MocfDuMs+u2Er
x4HPH4duU17ZCc2H36x0g1ZmIO5a2Ynt20NmFEzepCPodsouued2Jol9qcPuXs5P
/0y0hlXb0vbGfSB8SvAk4tlF12CEcXArb5ofs0ekjNiSFpfXklGEGKsi5uMr/gQB
ZKfszw13yYiHWRP1tDBH62sH6xOHFGaI5COh4WmeN0LijqYVT8om+Xgd2dPO7imJ
3Ylm5e1Fn/koISkGegp/i1BqmddWFVtUvvd/TdSi5gBqZDjYS/0It9cJCcmD0zmn
tCnwII2RAoGBAPEx3uO7uEKDcMhQkXmtjsfwnVLLYWipkjUqldPz+hdnEVchf7AF
B7/efH0x9OQtKCKIOhOsZ58NMSD0lIEnH/dsDenRBwJt9nIgzHWQ5ZaALkWFEedf
TzdjcXj64Du7M5sPtmK+VysPQRR8fTc7hP4tEFghpBN6o7r4gXokiuPdAoGBANlK
pTLqB8tbxnhGxhasVkbOrGAmAAjXj4p5kYjq739DYzDzZNyRZ8XVRepOWEKReuIe
5qxr/iRI8VeZgQjHu0Uorva03jamEs2lUo6v6igcyvPthU8a5UypueQH13tBfoYY
KZ7vkpUCmlNIKlMWmt6APx1NxBBq29vrgoFC06d9AoGBAIoYZqh3K2mRvjpCvvgu
4egiIMFtxujY5ehg6IYBU7igf3ImsOgECUrWxOoddzxJiogy/SWUeYO8VtuqjXhh
AVF1YYX08xF2CjuQAeByRLFl1JhjNzwE+uMYobiF02r/pO1sEp34own5Yuaq3DqK
bnvlK016fWnVtvbvH5riX09VAoGAWmiYe6qsO4yf84NfACgMbcGoLwkgko0+CBu0
1gcKRyU+rCtZF/zX9JRcVrTqPChTvKmmyK7WOzquUAhqi8CuxoO66KMwogRPZokW
5I+FGdOBDiURhmxmY5Nq6NnqajpB7oE3MA32K1G97FDma1PIdDNgD00/+qS7fbFr
x2SKs/ECgYBDZnD16pXVCJ2JEzHkxZDA17N82Jltd4ju8d/kcNKBrsSZysFy00VT
eU5wC1uDVfWtw0TEXZXQqnzBGEbTZJ+nhWfrm5kimyUkmyhZOHJMMZNs9rpLfD2i
xsE/qqEKdhxZmV7bUWr+v2Grtajz4zHxyqB1YWU9qma+wKppA068BQ==
-----END RSA PRIVATE KEY-----

View File

@@ -1,15 +1,5 @@
#!/usr/bin/env ruby
#!/bin/sh
sha = `git rev-parse HEAD`.chop
key = File.expand_path('../atom.githubapp.com', __FILE__)
system 'chmod', '600', key
system 'ssh',
'-l', 'atom',
'-i', key,
'-o', 'StrictHostKeyChecking=no',
'-p', '6000',
'atom.githubapp.com',
"rm -rf ~/.atom && cd /Users/atom/code/atom && git fetch -q origin && git reset -q --hard #{sha} && rake test"
exit $?.exitstatus
set -ex
rm -rf ~/.atom
rake test

View File

@@ -38,4 +38,4 @@ for CSON_FILE in $CSON_FILES; do
done;
# Copy non-coffee files into bundle
rsync --archive --recursive --exclude="src/**/*.coffee" --exclude="src/**/*.cson" src static vendor spec benchmark themes "$RESOUCES_PATH"
rsync --archive --recursive --exclude="src/**/*.coffee" --exclude="src/**/*.cson" src static vendor spec benchmark themes dot-atom atom.sh "$RESOUCES_PATH"

View File

@@ -131,3 +131,13 @@ describe "the `atom` global", ->
expect(packageStates['package-with-module']).toEqual someNumber: 1
expect(packageStates['package-with-serialize-error']).toBeUndefined()
expect(console.error).toHaveBeenCalled()
describe ".getVersion(callback)", ->
it "calls the callback with the current version number", ->
versionHandler = jasmine.createSpy("versionHandler")
atom.getVersion(versionHandler)
waitsFor ->
versionHandler.callCount > 0
runs ->
expect(versionHandler.argsForCall[0][0]).toMatch /^\d+\.\d+\.\d+$/

View File

@@ -741,6 +741,7 @@ describe 'Buffer', ->
oldTailPosition: [4, 20]
newTailPosition: [4, 20]
bufferChanged: false
valid: true
}
observeHandler.reset()
@@ -751,6 +752,7 @@ describe 'Buffer', ->
oldHeadPosition: [6, 2]
newHeadPosition: [6, 5]
bufferChanged: true
valid: true
}
it "calls the given callback when the marker's tail position changes", ->
@@ -762,6 +764,7 @@ describe 'Buffer', ->
oldTailPosition: [4, 20]
newTailPosition: [6, 2]
bufferChanged: false
valid: true
}
observeHandler.reset()
@@ -773,6 +776,7 @@ describe 'Buffer', ->
oldTailPosition: [6, 2]
newTailPosition: [6, 5]
bufferChanged: true
valid: true
}
it "calls the callback when the selection's tail is cleared", ->
@@ -784,6 +788,7 @@ describe 'Buffer', ->
oldTailPosition: [4, 20]
newTailPosition: [4, 23]
bufferChanged: false
valid: true
}
it "only calls the callback once when both the marker's head and tail positions change due to the same operation", ->
@@ -795,6 +800,7 @@ describe 'Buffer', ->
oldHeadPosition: [4, 23]
newHeadPosition: [4, 26]
bufferChanged: true
valid: true
}
observeHandler.reset()
@@ -806,6 +812,31 @@ describe 'Buffer', ->
oldHeadPosition: [4, 26]
newHeadPosition: [1, 1]
bufferChanged: false
valid: true
}
it "calls the callback with the valid flag set to false when the marker is invalidated", ->
buffer.deleteRow(4)
expect(observeHandler.callCount).toBe 1
expect(observeHandler.argsForCall[0][0]).toEqual {
oldTailPosition: [4, 20]
newTailPosition: [4, 20]
oldHeadPosition: [4, 23]
newHeadPosition: [4, 23]
bufferChanged: true
valid: false
}
observeHandler.reset()
buffer.undo()
expect(observeHandler.callCount).toBe 1
expect(observeHandler.argsForCall[0][0]).toEqual {
oldTailPosition: [4, 20]
newTailPosition: [4, 20]
oldHeadPosition: [4, 23]
newHeadPosition: [4, 23]
bufferChanged: true
valid: true
}
it "allows the observation subscription to be cancelled", ->
@@ -830,19 +861,20 @@ describe 'Buffer', ->
buffer.undo()
expect(buffer.getMarkerRange(marker)).toBeUndefined()
# even "stayValid" markers get destroyed properly
marker2 = buffer.markRange([[4, 20], [4, 23]], stayValid: true)
# even "invalidationStrategy: never" markers get destroyed properly
marker2 = buffer.markRange([[4, 20], [4, 23]], invalidationStrategy: 'never')
buffer.delete([[4, 15], [4, 25]])
buffer.destroyMarker(marker2)
buffer.undo()
expect(buffer.getMarkerRange(marker2)).toBeUndefined()
describe "marker updates due to buffer changes", ->
[marker1, marker2] = []
[marker1, marker2, marker3] = []
beforeEach ->
marker1 = buffer.markRange([[4, 20], [4, 23]])
marker2 = buffer.markRange([[4, 20], [4, 23]], stayValid: true)
marker2 = buffer.markRange([[4, 20], [4, 23]], invalidationStrategy: 'never')
marker3 = buffer.markRange([[4, 20], [4, 23]], invalidationStrategy: 'between')
describe "when the buffer changes due to a new operation", ->
describe "when the change precedes the marker range", ->
@@ -874,20 +906,37 @@ describe 'Buffer', ->
buffer.insert([4, 20], '...')
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 26]]
describe "when the invalidation strategy is 'between'", ->
it "invalidates the marker", ->
buffer.insert([4, 20], '...')
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
describe "when the change is an insertion at the end of the marker range", ->
it "moves the end point", ->
buffer.insert([4, 23], '...')
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 26]]
describe "when the invalidation strategy is 'between'", ->
it "invalidates the marker", ->
buffer.insert([4, 23], '...')
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
describe "when the change surrounds the marker range", ->
describe "when the marker was created with stayValid: false (the default)", ->
describe "when the marker's invalidation strategy is 'contains' (the default)", ->
it "invalidates the marker", ->
buffer.delete([[4, 15], [4, 25]])
expect(buffer.getMarkerRange(marker1)).toBeUndefined()
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
describe "when the marker was created with stayValid: true", ->
describe "when the marker's invalidation strategy is 'between'", ->
it "invalidates the marker", ->
buffer.delete([[4, 15], [4, 25]])
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
buffer.undo()
expect(buffer.getMarkerRange(marker3)).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'never'", ->
it "does not invalidate the marker, but sets it to an empty range at the end of the change", ->
buffer.change([[4, 15], [4, 25]], "...")
expect(buffer.getMarkerRange(marker2)).toEqual [[4, 18], [4, 18]]
@@ -895,14 +944,21 @@ describe 'Buffer', ->
expect(buffer.getMarkerRange(marker2)).toEqual [[4, 20], [4, 23]]
describe "when the change straddles the start of the marker range", ->
describe "when the marker was created with stayValid: false (the default)", ->
describe "when the marker's invalidation strategy is 'contains' (the default)", ->
it "invalidates the marker", ->
buffer.delete([[4, 15], [4, 22]])
expect(buffer.getMarkerRange(marker1)).toBeUndefined()
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
describe "when the marker was created with stayValid: true", ->
describe "when the marker's invalidation strategy is 'between'", ->
it "invalidates the marker", ->
buffer.delete([[4, 15], [4, 22]])
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
buffer.undo()
expect(buffer.getMarkerRange(marker3)).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'never'", ->
it "moves the start of the marker range to the end of the change", ->
buffer.delete([[4, 15], [4, 22]])
expect(buffer.getMarkerRange(marker2)).toEqual [[4, 15], [4, 16]]
@@ -910,20 +966,49 @@ describe 'Buffer', ->
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
describe "when the change straddles the end of the marker range", ->
describe "when the marker was created with stayValid: false (the default)", ->
describe "when the marker's invalidation strategy is 'contains' (the default)", ->
it "invalidates the marker", ->
buffer.delete([[4, 22], [4, 25]])
expect(buffer.getMarkerRange(marker1)).toBeUndefined()
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
describe "when the marker was created with stayValid: true", ->
describe "when the marker's invalidation strategy is 'between'", ->
it "invalidates the marker", ->
buffer.delete([[4, 22], [4, 25]])
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
buffer.undo()
expect(buffer.getMarkerRange(marker3)).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'never'", ->
it "moves the end of the marker range to the start of the change", ->
buffer.delete([[4, 22], [4, 25]])
expect(buffer.getMarkerRange(marker2)).toEqual [[4, 20], [4, 22]]
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
describe "when the change is between the start and the end of the marker range", ->
describe "when the marker's invalidation strategy is 'contains' (the default)", ->
it "does not invalidate the marker", ->
buffer.insert([4, 21], 'x')
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 24]]
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'between'", ->
it "invalidates the marker", ->
buffer.insert([4, 21], 'x')
expect(buffer.getMarkerRange(marker3)).toBeUndefined()
buffer.undo()
expect(buffer.getMarkerRange(marker3)).toEqual [[4, 20], [4, 23]]
describe "when the marker's invalidation strategy is 'never'", ->
it "moves the end of the marker range to the start of the change", ->
buffer.insert([4, 21], 'x')
expect(buffer.getMarkerRange(marker2)).toEqual [[4, 20], [4, 24]]
buffer.undo()
expect(buffer.getMarkerRange(marker1)).toEqual [[4, 20], [4, 23]]
describe "when the buffer changes due to the undo or redo of a previous operation", ->
it "restores invalidated markers when undoing/redoing in the other direction", ->
buffer.change([[4, 21], [4, 24]], "foo")

View File

@@ -109,3 +109,27 @@ describe "Config", ->
config.set("foo.bar.baz", "i'm back")
expect(observeHandler).toHaveBeenCalledWith("i'm back")
describe "initializeConfigDirectory()", ->
beforeEach ->
config.configDirPath = '/tmp/dot-atom-dir'
expect(fs.exists(config.configDirPath)).toBeFalsy()
afterEach ->
fs.remove('/tmp/dot-atom-dir') if fs.exists('/tmp/dot-atom-dir')
describe "when the configDirPath doesn't exist", ->
it "copies the contents of dot-atom to ~/.atom", ->
config.initializeConfigDirectory()
expect(fs.exists(config.configDirPath)).toBeTruthy()
expect(fs.exists(fs.join(config.configDirPath, 'packages'))).toBeTruthy()
expect(fs.exists(fs.join(config.configDirPath, 'snippets'))).toBeTruthy()
expect(fs.exists(fs.join(config.configDirPath, 'themes'))).toBeTruthy()
expect(fs.isFile(fs.join(config.configDirPath, 'config.cson'))).toBeTruthy()
it "copies the bundles themes to ~/.atom", ->
config.initializeConfigDirectory()
expect(fs.isFile(fs.join(config.configDirPath, 'themes/atom-dark-ui/package.cson'))).toBeTruthy()
expect(fs.isFile(fs.join(config.configDirPath, 'themes/atom-light-ui/package.cson'))).toBeTruthy()
expect(fs.isFile(fs.join(config.configDirPath, 'themes/atom-dark-syntax.css'))).toBeTruthy()
expect(fs.isFile(fs.join(config.configDirPath, 'themes/atom-light-syntax.css'))).toBeTruthy()

View File

@@ -661,6 +661,7 @@ describe "DisplayBuffer", ->
newTailScreenPosition: [5, 4]
newTailBufferPosition: [8, 4]
bufferChanged: false
valid: true
}
observeHandler.reset()
@@ -676,6 +677,7 @@ describe "DisplayBuffer", ->
newTailScreenPosition: [5, 4]
newTailBufferPosition: [8, 4]
bufferChanged: true
valid: true
}
observeHandler.reset()
@@ -691,6 +693,7 @@ describe "DisplayBuffer", ->
newTailScreenPosition: [8, 4]
newTailBufferPosition: [8, 4]
bufferChanged: false
valid: true
}
observeHandler.reset()
@@ -706,6 +709,7 @@ describe "DisplayBuffer", ->
newTailScreenPosition: [5, 4]
newTailBufferPosition: [8, 4]
bufferChanged: false
valid: true
}
it "calls the callback whenever the marker tail's position changes in the buffer or on screen", ->
@@ -721,6 +725,7 @@ describe "DisplayBuffer", ->
newTailScreenPosition: [8, 20]
newTailBufferPosition: [11, 20]
bufferChanged: false
valid: true
}
observeHandler.reset()
@@ -736,6 +741,40 @@ describe "DisplayBuffer", ->
newTailScreenPosition: [8, 23]
newTailBufferPosition: [11, 23]
bufferChanged: true
valid: true
}
it "calls the callback whenever the marker is invalidated or revalidated", ->
buffer.deleteRow(8)
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual {
oldHeadScreenPosition: [5, 10]
oldHeadBufferPosition: [8, 10]
newHeadScreenPosition: [5, 10]
newHeadBufferPosition: [8, 10]
oldTailScreenPosition: [5, 4]
oldTailBufferPosition: [8, 4]
newTailScreenPosition: [5, 4]
newTailBufferPosition: [8, 4]
bufferChanged: true
valid: false
}
observeHandler.reset()
buffer.undo()
expect(observeHandler).toHaveBeenCalled()
expect(observeHandler.argsForCall[0][0]).toEqual {
oldHeadScreenPosition: [5, 10]
oldHeadBufferPosition: [8, 10]
newHeadScreenPosition: [5, 10]
newHeadBufferPosition: [8, 10]
oldTailScreenPosition: [5, 4]
oldTailBufferPosition: [8, 4]
newTailScreenPosition: [5, 4]
newTailBufferPosition: [8, 4]
bufferChanged: true
valid: true
}
it "does not call the callback for screen changes that don't change the position of the marker", ->

View File

@@ -308,10 +308,11 @@ describe "Editor", ->
spyOn(atom, "confirm")
contentsConflictedHandler = jasmine.createSpy("contentsConflictedHandler")
editSession.on 'contents-conflicted', contentsConflictedHandler
fs.write(path, "a file change")
waitsFor "file to trigger contents-changed event", (done) ->
editSession.one 'contents-conflicted', done
waitsFor ->
contentsConflictedHandler.callCount > 0
runs ->
expect(atom.confirm).toHaveBeenCalled()
@@ -2136,6 +2137,11 @@ describe "Editor", ->
expect(editor.getSelection().isEmpty()).toBeTruthy()
expect(editor.getCursorScreenPosition()).toEqual [5, 0]
it "keeps the gutter line and the editor line the same heights (regression)", ->
editor.getSelection().setBufferRange(new Range([4, 29], [7, 4]))
editor.trigger 'editor:fold-selection'
expect(editor.gutter.find('.line-number:eq(4)').height()).toBe editor.renderedLines.find('.line:eq(4)').height()
describe "when a fold placeholder line is clicked", ->
it "removes the associated fold and places the cursor at its beginning", ->
@@ -2859,3 +2865,17 @@ describe "Editor", ->
editor.trigger 'editor:undo-close-session'
expect(editor.getPath()).toBe fixturesProject.resolve('sample.js')
expect(editor.getActiveEditSessionIndex()).toBe 0
describe "editor:save-debug-snapshot", ->
it "saves the state of the rendered lines, the display buffer, and the buffer to a file of the user's choosing", ->
saveDialogCallback = null
spyOn(atom, 'showSaveDialog').andCallFake (callback) -> saveDialogCallback = callback
spyOn(fs, 'write')
editor.trigger 'editor:save-debug-snapshot'
expect(atom.showSaveDialog).toHaveBeenCalled()
saveDialogCallback('/tmp/state')
expect(fs.write).toHaveBeenCalled()
expect(fs.write.argsForCall[0][0]).toBe '/tmp/state'
expect(typeof fs.write.argsForCall[0][1]).toBe 'string'

View File

@@ -89,7 +89,6 @@ describe "RootView", ->
expect(rootView.getEditors().length).toBe 0
viewState = rootView.serialize()
console.log viewState
rootView.deactivate()
window.rootView = RootView.deserialize(viewState)

View File

@@ -116,3 +116,16 @@ describe "Window", ->
window.shutdown()
window.shutdown()
expect(atom.setRootViewStateForPath.callCount).toBe 1
describe ".installAtomCommand(commandPath)", ->
commandPath = '/tmp/installed-atom-command/atom'
afterEach ->
fs.remove(commandPath) if fs.exists(commandPath)
describe "when the command path doesn't exist", ->
it "copies atom.sh to the specified path", ->
expect(fs.exists(commandPath)).toBeFalsy()
window.installAtomCommand(commandPath)
expect(fs.exists(commandPath)).toBeTruthy()
expect(fs.read(commandPath).length).toBeGreaterThan 1

1
spec/fixtures/gfm.md vendored Normal file
View File

@@ -0,0 +1 @@
* First list item

View File

@@ -114,7 +114,7 @@ describe 'Child Processes', ->
afterEach ->
jasmine.getEnv().defaultTimeoutInterval = originalTimeout
it "does not block indefinitally on stdout or stderr callbacks (regression)", ->
it "does not block indefinitely on stdout or stderr callbacks (regression)", ->
output = []
waitsForPromise ->

View File

@@ -3,13 +3,13 @@ OnigRegExp = require 'onig-reg-exp'
describe "OnigRegExp", ->
describe ".search(string, index)", ->
it "returns an array of the match and all capture groups", ->
regex = new OnigRegExp("\\w(\\d+)")
regex = OnigRegExp.create("\\w(\\d+)")
result = regex.search("----a123----")
expect(result).toEqual ["a123", "123"]
expect(result.index).toBe 4
expect(result.indices).toEqual [4, 5]
it "returns null if it does not match", ->
regex = new OnigRegExp("\\w(\\d+)")
regex = OnigRegExp.create("\\w(\\d+)")
result = regex.search("--------")
expect(result).toBeNull()

View File

@@ -94,8 +94,8 @@ _.extend atom,
open: (args...) ->
@sendMessageToBrowserProcess('open', args)
openUnstable: (args...) ->
@sendMessageToBrowserProcess('openUnstable', args)
openDev: (args...) ->
@sendMessageToBrowserProcess('openDev', args)
newWindow: (args...) ->
@sendMessageToBrowserProcess('newWindow', args)
@@ -179,3 +179,13 @@ _.extend atom,
getUpdateStatus: (callback) ->
@sendMessageToBrowserProcess('getUpdateStatus', [], callback)
requireUserInitScript: ->
userInitScriptPath = fs.join(config.configDirPath, "user.coffee")
try
require userInitScriptPath if fs.isFile(userInitScriptPath)
catch error
console.error "Failed to load `#{userInitScriptPath}`", error.stack, error
getVersion: (callback) ->
@sendMessageToBrowserProcess('getVersion', null, callback)

View File

@@ -107,5 +107,4 @@ class BufferChangeOperation
if validMarker = @buffer.validMarkers[id]
validMarker.setRange(previousRange)
else if invalidMarker = @buffer.invalidMarkers[id]
@buffer.validMarkers[id] = invalidMarker
invalidMarker.revalidate()

View File

@@ -8,9 +8,10 @@ class BufferMarker
headPosition: null
tailPosition: null
suppressObserverNotification: false
stayValid: false
invalidationStrategy: null
constructor: ({@id, @buffer, range, @stayValid, noTail, reverse}) ->
constructor: ({@id, @buffer, range, @invalidationStrategy, noTail, reverse}) ->
@invalidationStrategy ?= 'contains'
@setRange(range, {noTail, reverse})
setRange: (range, options={}) ->
@@ -71,23 +72,30 @@ class BufferMarker
newTailPosition = @getTailPosition()
@notifyObservers({oldTailPosition, newTailPosition, bufferChanged: false})
tryToInvalidate: (oldRange) ->
containsStart = oldRange.containsPoint(@getStartPosition(), exclusive: true)
containsEnd = oldRange.containsPoint(@getEndPosition(), exclusive: true)
return unless containsEnd or containsStart
tryToInvalidate: (changedRange) ->
betweenStartAndEnd = @getRange().containsRange(changedRange, exclusive: false)
containsStart = changedRange.containsPoint(@getStartPosition(), exclusive: true)
containsEnd = changedRange.containsPoint(@getEndPosition(), exclusive: true)
if @stayValid
previousRange = @getRange()
if containsStart and containsEnd
@setRange([oldRange.end, oldRange.end])
else if containsStart
@setRange([oldRange.end, @getEndPosition()])
else
@setRange([@getStartPosition(), oldRange.start])
[@id, previousRange]
else
@invalidate()
[@id]
switch @invalidationStrategy
when 'between'
if betweenStartAndEnd or containsStart or containsEnd
@invalidate()
[@id]
when 'contains'
if containsStart or containsEnd
@invalidate()
[@id]
when 'never'
if containsStart or containsEnd
previousRange = @getRange()
if containsStart and containsEnd
@setRange([changedRange.end, changedRange.end])
else if containsStart
@setRange([changedRange.end, @getEndPosition()])
else
@setRange([@getStartPosition(), changedRange.start])
[@id, previousRange]
handleBufferChange: (bufferChange) ->
@consolidateObserverNotifications true, =>
@@ -113,23 +121,31 @@ class BufferMarker
[newRow, newColumn]
observe: (callback) ->
@on 'position-changed', callback
@on 'changed', callback
cancel: => @unobserve(callback)
unobserve: (callback) ->
@off 'position-changed', callback
@off 'changed', callback
containsPoint: (point) ->
@getRange().containsPoint(point)
notifyObservers: ({oldHeadPosition, newHeadPosition, oldTailPosition, newTailPosition, bufferChanged}) ->
notifyObservers: ({oldHeadPosition, newHeadPosition, oldTailPosition, newTailPosition, bufferChanged} = {}) ->
return if @suppressObserverNotification
return if _.isEqual(newHeadPosition, oldHeadPosition) and _.isEqual(newTailPosition, oldTailPosition)
if newHeadPosition? and newTailPosition?
return if _.isEqual(newHeadPosition, oldHeadPosition) and _.isEqual(newTailPosition, oldTailPosition)
else if newHeadPosition?
return if _.isEqual(newHeadPosition, oldHeadPosition)
else if newTailPosition?
return if _.isEqual(newTailPosition, oldTailPosition)
oldHeadPosition ?= @getHeadPosition()
newHeadPosition ?= @getHeadPosition()
oldTailPosition ?= @getTailPosition()
newTailPosition ?= @getTailPosition()
@trigger 'position-changed', {oldHeadPosition, newHeadPosition, oldTailPosition, newTailPosition, bufferChanged}
valid = @buffer.validMarkers[@id]?
@trigger 'changed', {oldHeadPosition, newHeadPosition, oldTailPosition, newTailPosition, bufferChanged, valid}
consolidateObserverNotifications: (bufferChanged, fn) ->
@suppressObserverNotification = true
@@ -141,8 +157,14 @@ class BufferMarker
@suppressObserverNotification = false
@notifyObservers({oldHeadPosition, newHeadPosition, oldTailPosition, newTailPosition, bufferChanged})
invalidate: (preserve) ->
invalidate: ->
delete @buffer.validMarkers[@id]
@buffer.invalidMarkers[@id] = this
@notifyObservers(bufferChanged: true)
revalidate: ->
delete @buffer.invalidMarkers[@id]
@buffer.validMarkers[@id] = this
@notifyObservers(bufferChanged: true)
_.extend BufferMarker.prototype, EventEmitter

View File

@@ -419,11 +419,6 @@ class Buffer
return match[0][0] != '\t'
undefined
logLines: (start=0, end=@getLastRow())->
for row in [start..end]
line = @lineForRow(row)
console.log row, line, line.length
getRepo: -> @project?.repo
checkoutHead: ->
@@ -442,4 +437,15 @@ class Buffer
fileExists: ->
@file.exists()
logLines: (start=0, end=@getLastRow())->
for row in [start..end]
line = @lineForRow(row)
console.log row, line, line.length
getDebugSnapshot: ->
lines = ['Buffer:']
for row in [0..@getLastRow()]
lines.push "#{row}: #{@lineForRow(row)}"
lines.join('\n')
_.extend(Buffer.prototype, EventEmitter)

View File

@@ -3,7 +3,6 @@ _ = require 'underscore'
EventEmitter = require 'event-emitter'
configDirPath = fs.absolute("~/.atom")
userInitScriptPath = fs.join(configDirPath, "user.coffee")
bundledPackagesDirPath = fs.join(resourcePath, "src/packages")
bundledThemesDirPath = fs.join(resourcePath, "themes")
vendoredPackagesDirPath = fs.join(resourcePath, "vendor/packages")
@@ -30,11 +29,29 @@ class Config
@configFilePath = fs.resolve(configDirPath, 'config', ['json', 'cson'])
@configFilePath ?= fs.join(configDirPath, 'config.cson')
initializeConfigDirectory: ->
return if fs.exists(@configDirPath)
fs.makeDirectory(@configDirPath)
templateConfigDirPath = fs.resolve(window.resourcePath, 'dot-atom')
onConfigDirFile = (path) =>
templatePath = fs.join(templateConfigDirPath, path)
configPath = fs.join(@configDirPath, path)
fs.write(configPath, fs.read(templatePath))
fs.traverseTree(templateConfigDirPath, onConfigDirFile, (path) -> true)
configThemeDirPath = fs.join(@configDirPath, 'themes')
onThemeDirFile = (path) ->
templatePath = fs.join(bundledThemesDirPath, path)
configPath = fs.join(configThemeDirPath, path)
fs.write(configPath, fs.read(templatePath))
fs.traverseTree(bundledThemesDirPath, onThemeDirFile, (path) -> true)
load: ->
@initializeConfigDirectory()
@loadUserConfig()
@requireUserInitScript()
loadUserConfig: ->
if fs.exists(@configFilePath)
@@ -81,10 +98,4 @@ class Config
save: ->
fs.writeObject(@configFilePath, @settings)
requireUserInitScript: ->
try
require userInitScriptPath if fs.exists(userInitScriptPath)
catch error
console.error "Failed to load `#{userInitScriptPath}`", error.stack, error
_.extend Config.prototype, EventEmitter

View File

@@ -7,6 +7,7 @@ class DisplayBufferMarker
bufferMarkerSubscription: null
headScreenPosition: null
tailScreenPosition: null
valid: true
constructor: ({@id, @displayBuffer}) ->
@buffer = @displayBuffer.buffer
@@ -57,11 +58,11 @@ class DisplayBufferMarker
observe: (callback) ->
@observeBufferMarkerIfNeeded()
@on 'position-changed', callback
@on 'changed', callback
cancel: => @unobserve(callback)
unobserve: (callback) ->
@off 'position-changed', callback
@off 'changed', callback
@unobserveBufferMarkerIfNeeded()
observeBufferMarkerIfNeeded: ->
@@ -69,13 +70,14 @@ class DisplayBufferMarker
@getHeadScreenPosition() # memoize current value
@getTailScreenPosition() # memoize current value
@bufferMarkerSubscription =
@buffer.observeMarker @id, ({oldHeadPosition, newHeadPosition, oldTailPosition, newTailPosition, bufferChanged}) =>
@buffer.observeMarker @id, ({oldHeadPosition, newHeadPosition, oldTailPosition, newTailPosition, bufferChanged, valid}) =>
@notifyObservers
oldHeadBufferPosition: oldHeadPosition
newHeadBufferPosition: newHeadPosition
oldTailBufferPosition: oldTailPosition
newTailBufferPosition: newTailPosition
bufferChanged: bufferChanged
valid: valid
@displayBuffer.markers[@id] = this
unobserveBufferMarkerIfNeeded: ->
@@ -83,28 +85,37 @@ class DisplayBufferMarker
@bufferMarkerSubscription.cancel()
delete @displayBuffer.markers[@id]
notifyObservers: ({oldHeadBufferPosition, oldTailBufferPosition, bufferChanged}) ->
notifyObservers: ({oldHeadBufferPosition, oldTailBufferPosition, bufferChanged, valid} = {}) ->
oldHeadScreenPosition = @getHeadScreenPosition()
@headScreenPosition = null
newHeadScreenPosition = @getHeadScreenPosition()
newHeadScreenPosition = oldHeadScreenPosition
oldTailScreenPosition = @getTailScreenPosition()
@tailScreenPosition = null
newTailScreenPosition = @getTailScreenPosition()
newTailScreenPosition = oldTailScreenPosition
valid ?= true
return if _.isEqual(newHeadScreenPosition, oldHeadScreenPosition) and _.isEqual(newTailScreenPosition, oldTailScreenPosition)
if valid
@headScreenPosition = null
newHeadScreenPosition = @getHeadScreenPosition()
@tailScreenPosition = null
newTailScreenPosition = @getTailScreenPosition()
validChanged = valid isnt @valid
headScreenPositionChanged = not _.isEqual(newHeadScreenPosition, oldHeadScreenPosition)
tailScreenPositionChanged = not _.isEqual(newTailScreenPosition, oldTailScreenPosition)
return unless validChanged or headScreenPositionChanged or tailScreenPositionChanged
oldHeadBufferPosition ?= @getHeadBufferPosition()
newHeadBufferPosition = @getHeadBufferPosition()
newHeadBufferPosition = @getHeadBufferPosition() ? oldHeadBufferPosition
oldTailBufferPosition ?= @getTailBufferPosition()
newTailBufferPosition = @getTailBufferPosition()
newTailBufferPosition = @getTailBufferPosition() ? oldTailBufferPosition
@valid = valid
@trigger 'position-changed', {
@trigger 'changed', {
oldHeadScreenPosition, newHeadScreenPosition,
oldTailScreenPosition, newTailScreenPosition,
oldHeadBufferPosition, newHeadBufferPosition,
oldTailBufferPosition, newTailBufferPosition,
bufferChanged
valid
}
_.extend DisplayBufferMarker.prototype, EventEmitter

View File

@@ -332,8 +332,9 @@ class DisplayBuffer
getMarkers: ->
_.values(@markers)
markScreenRange: (screenRange) ->
@markBufferRange(@bufferRangeForScreenRange(screenRange))
markScreenRange: (args...) ->
bufferRange = @bufferRangeForScreenRange(args.shift())
@markBufferRange(bufferRange, args...)
markBufferRange: (args...) ->
@buffer.markRange(args...)
@@ -419,4 +420,10 @@ class DisplayBuffer
logLines: (start, end) ->
@lineMap.logLines(start, end)
getDebugSnapshot: ->
lines = ["Display Buffer:"]
for screenLine, row in @lineMap.linesForScreenRows(0, @getLastRow())
lines.push "#{row}: #{screenLine.text}"
lines.join('\n')
_.extend DisplayBuffer.prototype, EventEmitter

View File

@@ -538,11 +538,11 @@ class EditSession
_.last(@cursors)
addCursorAtScreenPosition: (screenPosition) ->
marker = @markScreenPosition(screenPosition, stayValid: true)
marker = @markScreenPosition(screenPosition, invalidationStrategy: 'never')
@addSelection(marker).cursor
addCursorAtBufferPosition: (bufferPosition) ->
marker = @markBufferPosition(bufferPosition, stayValid: true)
marker = @markBufferPosition(bufferPosition, invalidationStrategy: 'never')
@addSelection(marker).cursor
addCursor: (marker) ->
@@ -565,7 +565,7 @@ class EditSession
selection
addSelectionForBufferRange: (bufferRange, options={}) ->
options = _.defaults({stayValid: true}, options)
options = _.defaults({invalidationStrategy: 'never'}, options)
marker = @markBufferRange(bufferRange, options)
@addSelection(marker)
@@ -820,5 +820,11 @@ class EditSession
@displayBuffer.tokenizedBuffer.resetScreenLines()
grammarChanged
getDebugSnapshot: ->
[
@displayBuffer.getDebugSnapshot()
@displayBuffer.tokenizedBuffer.getDebugSnapshot()
].join('\n\n')
_.extend(EditSession.prototype, EventEmitter)
_.extend(EditSession.prototype, Subscriber)

View File

@@ -19,7 +19,7 @@ class Editor extends View
autosave: false
autoIndent: true
autoIndentOnPaste: false
nonWordCharacters: "./\\()\"':,.;<>~!@#$%^&*|+=[]{}`~?"
nonWordCharacters: "./\\()\"':,.;<>~!@#$%^&*|+=[]{}`~?-"
@nextEditorId: 1
@@ -194,6 +194,7 @@ class Editor extends View
'editor:duplicate-line': @duplicateLine
'editor:undo-close-session': @undoDestroySession
'editor:toggle-indent-guide': => config.set('editor.showIndentGuide', !config.get('editor.showIndentGuide'))
'editor:save-debug-snapshot': @saveDebugSnapshot
documentation = {}
for name, method of editorBindings
@@ -1226,16 +1227,9 @@ class Editor extends View
lineElementForScreenRow: (screenRow) ->
@renderedLines.children(":eq(#{screenRow - @firstRenderedScreenRow})")
logScreenLines: (start, end) ->
@activeEditSession.logScreenLines(start, end)
toggleLineCommentsInSelection: ->
@activeEditSession.toggleLineCommentsInSelection()
logRenderedLines: ->
@renderedLines.find('.line').each (n) ->
console.log n, $(this).text()
pixelPositionForBufferPosition: (position) ->
@pixelPositionForScreenPosition(@screenPositionForBufferPosition(position))
@@ -1348,3 +1342,29 @@ class Editor extends View
copyPathToPasteboard: ->
path = @getPath()
pasteboard.write(path) if path?
saveDebugSnapshot: ->
atom.showSaveDialog (path) =>
fs.write(path, @getDebugSnapshot()) if path
getDebugSnapshot: ->
[
"Debug Snapshot: #{@getPath()}"
@getRenderedLinesDebugSnapshot()
@activeEditSession.getDebugSnapshot()
@getBuffer().getDebugSnapshot()
].join('\n\n')
getRenderedLinesDebugSnapshot: ->
lines = ['Rendered Lines:']
firstRenderedScreenRow = @firstRenderedScreenRow
@renderedLines.find('.line').each (n) ->
lines.push "#{firstRenderedScreenRow + n}: #{$(this).text()}"
lines.join('\n')
logScreenLines: (start, end) ->
@activeEditSession.logScreenLines(start, end)
logRenderedLines: ->
@renderedLines.find('.line').each (n) ->
console.log n, $(this).text()

View File

@@ -24,7 +24,7 @@ class Git
ignore: 1 << 14
constructor: (path, options={}) ->
@repo = new GitRepository(path)
@repo = GitRepository.open(path)
refreshIndexOnFocus = options.refreshIndexOnFocus ? true
if refreshIndexOnFocus
$ = require 'jquery'

View File

@@ -22,15 +22,26 @@ class GrammarView extends SelectList
itemForElement: (grammar) ->
if grammar is @currentGrammar
grammarClass = 'current-grammar'
grammarClass = 'active-item'
else
grammarClass = 'grammar'
grammarClass = 'inactive-item'
$$ ->
@li grammar.name, class: grammarClass
populate: ->
grammars = new Array(syntax.grammars...)
grammars.sort (grammarA, grammarB) ->
if grammarA.scopeName is 'text.plain'
-1
else if grammarB.scopeName is 'text.plain'
1
else if grammarA.name < grammarB.name
-1
else if grammarA.name > grammarB.name
1
else
0
grammars.unshift(@autoDetect)
@setArray(grammars)

View File

@@ -20,14 +20,14 @@ class Keymap
'meta-n': 'new-window'
'meta-,': 'open-user-configuration'
'meta-o': 'open'
'meta-O': 'open-unstable'
'meta-O': 'open-dev'
'meta-w': 'core:close'
'alt-meta-i': 'toggle-dev-tools'
$(document).command 'new-window', => atom.newWindow()
$(document).command 'open-user-configuration', => atom.open(config.configDirPath)
$(document).command 'open', => atom.open()
$(document).command 'open-unstable', => atom.openUnstable()
$(document).command 'open-dev', => atom.openDev()
loadBundledKeymaps: ->
@loadDirectory(require.resolve('keymaps'))

View File

@@ -30,13 +30,13 @@ class LanguageMode
buffer = @editSession.buffer
commentStartRegexString = _.escapeRegExp(commentStartString).replace(/(\s+)$/, '($1)?')
commentStartRegex = new OnigRegExp("^(\\s*)(#{commentStartRegexString})")
commentStartRegex = OnigRegExp.create("^(\\s*)(#{commentStartRegexString})")
shouldUncomment = commentStartRegex.test(buffer.lineForRow(start))
if commentEndString = syntax.getProperty(scopes, "editor.commentEnd")
if shouldUncomment
commentEndRegexString = _.escapeRegExp(commentEndString).replace(/^(\s+)/, '($1)?')
commentEndRegex = new OnigRegExp("(#{commentEndRegexString})(\\s*)$")
commentEndRegex = OnigRegExp.create("(#{commentEndRegexString})(\\s*)$")
startMatch = commentStartRegex.search(buffer.lineForRow(start))
endMatch = commentEndRegex.search(buffer.lineForRow(end))
if startMatch and endMatch
@@ -78,7 +78,7 @@ class LanguageMode
continue if @editSession.isBufferRowBlank(row)
indentation = @editSession.indentationForBufferRow(row)
if indentation <= startIndentLevel
includeRowInFold = indentation == startIndentLevel and @foldEndRegexForScopes(scopes).search(@editSession.lineForBufferRow(row))
includeRowInFold = indentation == startIndentLevel and @foldEndRegexForScopes(scopes)?.search(@editSession.lineForBufferRow(row))
foldEndRow = row if includeRowInFold
break
@@ -152,12 +152,12 @@ class LanguageMode
increaseIndentRegexForScopes: (scopes) ->
if increaseIndentPattern = syntax.getProperty(scopes, 'editor.increaseIndentPattern')
new OnigRegExp(increaseIndentPattern)
OnigRegExp.create(increaseIndentPattern)
decreaseIndentRegexForScopes: (scopes) ->
if decreaseIndentPattern = syntax.getProperty(scopes, 'editor.decreaseIndentPattern')
new OnigRegExp(decreaseIndentPattern)
OnigRegExp.create(decreaseIndentPattern)
foldEndRegexForScopes: (scopes) ->
if foldEndPattern = syntax.getProperty(scopes, 'editor.foldEndPattern')
new OnigRegExp(foldEndPattern)
OnigRegExp.create(foldEndPattern)

View File

@@ -194,7 +194,7 @@ class Project
ChildProcess.exec command , bufferLines: true, stdout: (data) ->
lines = data.split('\n')
lines.pop() # the last segment is a spurios '' because data always ends in \n due to bufferLines: true
lines.pop() # the last segment is a spurious '' because data always ends in \n due to bufferLines: true
for line in lines
readPath(line) if state is 'readingPath'
readLine(line) if state is 'readingLines'

View File

@@ -57,7 +57,11 @@ class Range
else
otherRange.intersectsWith(this)
containsPoint: (point, { exclusive } = {}) ->
containsRange: (otherRange, {exclusive} = {}) ->
{ start, end } = Range.fromObject(otherRange)
@containsPoint(start, {exclusive}) and @containsPoint(end, {exclusive})
containsPoint: (point, {exclusive} = {}) ->
point = Point.fromObject(point)
if exclusive
point.isGreaterThan(@start) and point.isLessThan(@end)

View File

@@ -28,7 +28,7 @@ class TextMateGrammar
constructor: ({ @name, @fileTypes, @scopeName, patterns, repository, @foldingStopMarker, firstLineMatch}) ->
@initialRule = new Rule(this, {@scopeName, patterns})
@repository = {}
@firstLineRegex = new OnigRegExp(firstLineMatch) if firstLineMatch
@firstLineRegex = OnigRegExp.create(firstLineMatch) if firstLineMatch
@fileTypes ?= []
for name, data of repository
@@ -111,7 +111,7 @@ class Rule
regex = pattern.regexSource
regexes.push regex if regex
regexScanner = new OnigScanner(regexes)
regexScanner = OnigScanner.create(regexes)
regexScanner.patterns = patterns
@scannersByBaseGrammarName[baseGrammar.name] = regexScanner unless anchored
regexScanner

View File

@@ -73,15 +73,27 @@ class TextMatePackage extends Package
scopedProperties
readObjectFromPath: (path, callback) ->
object = null
error = null
if fs.isObjectPath(path)
object = fs.readObject(path)
else
plist.parseString fs.read(path), (e, data) ->
error = e
object = data[0]
error = throw new Error("Failed to load object at path `#{path}`") unless object
callback(error, object)
getTextMatePreferenceObjects: ->
preferenceObjects = []
if fs.exists(@preferencesPath)
for preferencePath in fs.list(@preferencesPath)
plist.parseString fs.read(preferencePath), (e, data) =>
@readObjectFromPath preferencePath, (e, object) =>
if e
console.warn "Failed to parse preference at path '#{preferencePath}'", e.stack
else
preferenceObjects.push(data[0])
preferenceObjects.push(object)
preferenceObjects
propertiesFromTextMateSettings: (textMateSettings) ->

View File

@@ -226,4 +226,10 @@ class TokenizedBuffer
line = @lineForScreenRow(row).text
console.log row, line, line.length
getDebugSnapshot: ->
lines = ["Tokenized Buffer:"]
for screenLine, row in @linesForScreenRows(0, @getLastRow())
lines.push "#{row}: #{screenLine.text}"
lines.join('\n')
_.extend(TokenizedBuffer.prototype, EventEmitter)

View File

@@ -1,5 +1,6 @@
fs = require 'fs'
$ = require 'jquery'
ChildProcess = require 'child-process'
require 'jquery-extensions'
require 'underscore-extensions'
require 'space-pen-extensions'
@@ -39,6 +40,11 @@ window.setUpEnvironment = ->
# This method is only called when opening a real application window
window.startup = ->
if fs.isDirectory('/opt/boxen')
installAtomCommand('/opt/boxen/bin/atom')
else
installAtomCommand('/opt/github/bin/atom')
handleWindowEvents()
config.load()
atom.loadTextPackage()
@@ -47,12 +53,10 @@ window.startup = ->
atom.loadThemes()
atom.loadPackages()
keymap.loadUserKeymaps()
atom.requireUserInitScript()
$(window).on 'beforeunload', -> shutdown(); false
$(window).focus()
pathToOpen = atom.getPathToOpen()
rootView.open(pathToOpen) if !pathToOpen or fs.isFile(pathToOpen)
window.shutdown = ->
return if not project and not rootView
atom.setWindowState('pathToOpen', project.getPath())
@@ -65,6 +69,14 @@ window.shutdown = ->
window.rootView = null
window.project = null
window.installAtomCommand = (commandPath) ->
return if fs.exists(commandPath)
bundledCommandPath = fs.resolve(window.resourcePath, 'atom.sh')
if bundledCommandPath?
fs.write(commandPath, fs.read(bundledCommandPath))
ChildProcess.exec("chmod u+x '#{commandPath}'")
window.handleWindowEvents = ->
$(window).on 'core:close', => window.close()
$(window).command 'window:close', => window.close()
@@ -76,12 +88,14 @@ window.buildProjectAndRootView = ->
RootView = require 'root-view'
Project = require 'project'
windowState = atom.getRootViewStateForPath(atom.getPathToOpen())
if windowState?.project?
window.project = deserialize(windowState.project)
window.rootView = deserialize(windowState.rootView)
window.project ?= new Project(atom.getPathToOpen())
window.rootView ?= new RootView
pathToOpen = atom.getPathToOpen()
windowState = atom.getRootViewStateForPath(pathToOpen) ? {}
window.project = deserialize(windowState.project) ? new Project(pathToOpen)
window.rootView = deserialize(windowState.rootView) ? new RootView
if !windowState.rootView and (!pathToOpen or fs.isFile(pathToOpen))
rootView.open(pathToOpen)
$(rootViewParentSelector).append(rootView)
window.stylesheetElementForId = (id) ->
@@ -126,7 +140,7 @@ window.registerDeserializer = (klass) ->
deserializers[klass.name] = klass
window.deserialize = (state) ->
deserializers[state.deserializer]?.deserialize(state)
deserializers[state?.deserializer]?.deserialize(state)
window.measure = (description, fn) ->
start = new Date().getTime()

View File

@@ -7,4 +7,3 @@ module.exports =
rootView.eachEditor (editor) =>
if editor.attached and not editor.mini
@autoCompleteViews.push new AutocompleteView(editor)

View File

@@ -11,7 +11,7 @@ module.exports =
class CommandPanelView extends View
@content: ->
@div class: 'command-panel tool-panel', =>
@div class: 'loading', outlet: 'loadingMessage'
@div class: 'loading is-loading', outlet: 'loadingMessage', 'Searching...'
@div class: 'header', outlet: 'previewHeader', =>
@ul outlet: 'expandCollapse', class: 'expand-collapse', =>
@li class: 'expand', 'Expand All'
@@ -53,6 +53,7 @@ class CommandPanelView extends View
@previewList.hide()
@previewHeader.hide()
@errorMessages.hide()
@loadingMessage.hide()
@prompt.iconSize(@miniEditor.getFontSize())
@history = state.history ? []
@@ -88,14 +89,7 @@ class CommandPanelView extends View
@miniEditor.focus()
toggleLoading: ->
if @loadingMessage.hasClass 'is-loading'
@loadingMessage.removeClass 'is-loading'
@loadingMessage.html ''
@loadingMessage.hide()
else
@loadingMessage.addClass 'is-loading'
@loadingMessage.html 'Searching...'
@loadingMessage.show()
@loadingMessage.toggle()
onExpandAll: (event) =>
@previewList.expandAllPaths()

View File

@@ -3,7 +3,7 @@ CommandPanelView = require 'command-panel/lib/command-panel-view'
_ = require 'underscore'
describe "CommandPanel", ->
[editor, buffer, commandPanel, CommandPanel] = []
[editor, buffer, commandPanel] = []
beforeEach ->
window.rootView = new RootView

View File

@@ -2,7 +2,7 @@ module.exports =
class StatsTracker
startDate: new Date
hours: 6
eventLog: []
eventLog: {}
constructor: ->
date = new Date(@startDate)
@@ -16,7 +16,7 @@ class StatsTracker
rootView.on 'mouseup', => @track()
clear: ->
@eventLog = []
@eventLog = {}
track: ->
date = new Date

View File

@@ -1,9 +1,10 @@
$ = require 'jquery'
_ = require 'underscore'
RootView = require 'root-view'
EditorStats = require 'editor-stats/lib/editor-stats-view'
describe "EditorStats", ->
[editorStats, time] = []
[editorStats] = []
simulateKeyUp = (key) ->
e = $.Event "keydown", keyCode: key.charCodeAt(0)
@@ -16,26 +17,22 @@ describe "EditorStats", ->
beforeEach ->
window.rootView = new RootView
rootView.open('sample.js')
date = new Date()
mins = date.getMinutes()
hours = date.getHours()
mins = if mins == 60 then '01' else mins + 1
time = "#{hours}:#{mins}"
editorStats = window.loadPackage('editor-stats').packageMain.stats
describe "when a keyup event is triggered", ->
beforeEach ->
expect(_.values(editorStats.eventLog)).not.toContain 1
expect(_.values(editorStats.eventLog)).not.toContain 2
it "records the number of times a keyup is triggered", ->
simulateKeyUp('a')
expect(editorStats.eventLog[time]).toBe 1
expect(_.values(editorStats.eventLog)).toContain 1
simulateKeyUp('b')
expect(editorStats.eventLog[time]).toBe 2
expect(_.values(editorStats.eventLog)).toContain 2
describe "when a mouseup event is triggered", ->
it "records the number of times a mouseup is triggered", ->
simulateClick()
expect(editorStats.eventLog[time]).toBe 1
expect(_.values(editorStats.eventLog)).toContain 1
simulateClick()
expect(editorStats.eventLog[time]).toBe 2
expect(_.values(editorStats.eventLog)).toContain 2

View File

@@ -0,0 +1,4 @@
'name': 'Indent'
'scope': 'source.gfm'
'settings':
'increaseIndentPattern': '^\\s*([\\*\\+-])[ \\t]+'

View File

@@ -132,3 +132,10 @@ 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 = fixturesProject.buildEditSessionForPath('gfm.md')
editSession.insertNewlineBelow()
expect(editSession.buffer.lineForRow(1)).toBe ' '

View File

@@ -0,0 +1,2 @@
'.editor':
'meta-0': 'editor:correct-misspelling'

View File

@@ -0,0 +1,71 @@
{$$} = require 'space-pen'
Range = require 'range'
SelectList = require 'select-list'
module.exports =
class CorrectionsView extends SelectList
@viewClass: -> "corrections #{super} popover-list"
editor: null
corrections: null
misspellingRange: null
aboveCursor: false
initialize: (@editor, @corrections, @misspellingRange) ->
super
@attach()
itemForElement: (word) ->
$$ ->
@li word
selectNextItem: ->
super
false
selectPreviousItem: ->
super
false
confirmed: (correction) ->
@cancel()
return unless correction
@editor.transact =>
@editor.setSelectedBufferRange(@editor.bufferRangeForScreenRange(@misspellingRange))
@editor.insertText(correction)
attach: ->
@aboveCursor = false
if @corrections.length > 0
@setArray(@corrections)
else
@setError("No corrections found")
@editor.appendToLinesView(this)
@setPosition()
@miniEditor.focus()
detach: ->
super
@editor.focus()
setPosition: ->
{ left, top } = @editor.pixelPositionForScreenPosition(@misspellingRange.start)
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')
populateList: ->
super
@setPosition()

View File

@@ -0,0 +1,61 @@
{View} = require 'space-pen'
Range = require 'range'
CorrectionsView = require './corrections-view'
module.exports =
class MisspellingView extends View
@content: ->
@div class: 'misspelling'
initialize: (range, @editor) ->
@editSession = @editor.activeEditSession
range = @editSession.screenRangeForBufferRange(Range.fromObject(range))
@startPosition = range.start
@endPosition = range.end
@misspellingValid = true
@marker = @editSession.markScreenRange(range, invalidationStrategy: 'between')
@editSession.observeMarker @marker, ({newHeadScreenPosition, newTailScreenPosition, valid}) =>
@startPosition = newTailScreenPosition
@endPosition = newHeadScreenPosition
@updateDisplayPosition = valid
@misspellingValid = valid
@hide() unless valid
@subscribe @editor, 'editor:display-updated', =>
@updatePosition() if @updateDisplayPosition
@editor.command 'editor:correct-misspelling', =>
return unless @misspellingValid and @containsCursor()
screenRange = @getScreenRange()
misspelling = @editor.getTextInRange(@editor.bufferRangeForScreenRange(screenRange))
corrections = $native.getCorrectionsForMisspelling(misspelling)
@correctionsView?.remove()
@correctionsView = new CorrectionsView(@editor, corrections, screenRange)
@updatePosition()
getScreenRange: ->
new Range(@startPosition, @endPosition)
containsCursor: ->
cursor = @editor.getCursorScreenPosition()
@getScreenRange().containsPoint(cursor, exclusive: false)
updatePosition: ->
@updateDisplayPosition = false
startPixelPosition = @editor.pixelPositionForScreenPosition(@startPosition)
endPixelPosition = @editor.pixelPositionForScreenPosition(@endPosition)
@css
top: startPixelPosition.top
left: startPixelPosition.left
width: endPixelPosition.left - startPixelPosition.left
height: @editor.lineHeight
@show()
destroy: ->
@misspellingValid = false
@editSession.destroyMarker(@marker)
@correctionsView?.remove()
@remove()

View File

@@ -0,0 +1,14 @@
module.exports =
findMisspellings: (text) ->
wordRegex = /(?:^|[\s\[\]])([a-zA-Z']+)(?=[\s\.\[\]]|$)/g
row = 0
misspellings = []
for line in text.split('\n')
while matches = wordRegex.exec(line)
word = matches[1]
continue unless $native.isMisspelled(word)
startColumn = matches.index + matches[0].length - word.length
endColumn = startColumn + word.length
misspellings.push([[row, startColumn], [row, endColumn]])
row++
callTaskMethod('misspellingsFound', misspellings)

View File

@@ -0,0 +1,13 @@
Task = require 'task'
module.exports =
class SpellCheckTask extends Task
constructor: (@text, @callback) ->
super('spell-check/lib/spell-check-handler')
started: ->
@callWorkerMethod('findMisspellings', @text)
misspellingsFound: (misspellings) ->
@done()
@callback(misspellings)

View File

@@ -0,0 +1,53 @@
{View} = require 'space-pen'
_ = require 'underscore'
SpellCheckTask = require './spell-check-task'
MisspellingView = require './misspelling-view'
module.exports =
class SpellCheckView extends View
@content: ->
@div class: 'spell-check'
views: []
initialize: (@editor) ->
@subscribe @editor, 'editor:path-changed', => @subscribeToBuffer()
@subscribe @editor, 'editor:grammar-changed', => @subscribeToBuffer()
@observeConfig 'editor.fontSize', => @subscribeToBuffer()
@observeConfig 'spell-check.grammars', => @subscribeToBuffer()
@subscribeToBuffer()
subscribeToBuffer: ->
@destroyViews()
@task?.abort()
return unless @spellCheckCurrentGrammar()
@buffer?.off '.spell-check'
@buffer = @editor.getBuffer()
@buffer.on 'contents-modified.spell-check', => @updateMisspellings()
@updateMisspellings()
spellCheckCurrentGrammar: ->
grammar = @editor.getGrammar().scopeName
_.contains config.get('spell-check.grammars'), grammar
destroyViews: ->
if @views
view.destroy() for view in @views
@views = []
addViews: (misspellings) ->
for misspelling in misspellings
view = new MisspellingView(misspelling, @editor)
@views.push(view)
@append(view)
updateMisspellings: ->
@task?.abort()
callback = (misspellings) =>
@destroyViews()
@addViews(misspellings)
@task = new SpellCheckTask(@buffer.getText(), callback)
@task.start()

View File

@@ -0,0 +1,20 @@
SpellCheckView = require './spell-check-view'
module.exports =
configDefaults:
grammars: [
'text.plain'
'source.gfm'
'text.git-commit'
]
activate: ->
if syntax.grammars.length > 1
@subscribeToEditors()
else
syntax.on 'grammars-loaded', => @subscribeToEditors()
subscribeToEditors: ->
rootView.eachEditor (editor) ->
if editor.attached and not editor.mini
editor.underlayer.append(new SpellCheckView(editor))

View File

@@ -0,0 +1 @@
'main': 'lib/spell-check.coffee'

View File

@@ -0,0 +1,98 @@
RootView = require 'root-view'
describe "Spell check", ->
[editor] = []
beforeEach ->
window.rootView = new RootView
rootView.open('sample.js')
config.set('spell-check.grammars', [])
window.loadPackage('spell-check')
rootView.attachToDom()
editor = rootView.getActiveEditor()
it "decorates all misspelled words", ->
editor.setText("This middle of thiss sentencts has issues.")
config.set('spell-check.grammars', ['source.js'])
waitsFor ->
editor.find('.misspelling').length > 0
runs ->
expect(editor.find('.misspelling').length).toBe 2
typo1StartPosition = editor.pixelPositionForBufferPosition([0, 15])
typo1EndPosition = editor.pixelPositionForBufferPosition([0, 20])
expect(editor.find('.misspelling:eq(0)').position()).toEqual typo1StartPosition
expect(editor.find('.misspelling:eq(0)').width()).toBe typo1EndPosition.left - typo1StartPosition.left
typo2StartPosition = editor.pixelPositionForBufferPosition([0, 21])
typo2EndPosition = editor.pixelPositionForBufferPosition([0, 30])
expect(editor.find('.misspelling:eq(1)').position()).toEqual typo2StartPosition
expect(editor.find('.misspelling:eq(1)').width()).toBe typo2EndPosition.left - typo2StartPosition.left
it "hides decorations when a misspelled word is edited", ->
editor.setText('notaword')
advanceClock(editor.getBuffer().stoppedChangingDelay)
config.set('spell-check.grammars', ['source.js'])
waitsFor ->
editor.find('.misspelling').length > 0
runs ->
expect(editor.find('.misspelling').length).toBe 1
editor.moveCursorToEndOfLine()
editor.insertText('a')
advanceClock(editor.getBuffer().stoppedChangingDelay)
expect(editor.find('.misspelling')).toBeHidden()
describe "when spell checking for a grammar is removed", ->
it "removes all current decorations", ->
editor.setText('notaword')
advanceClock(editor.getBuffer().stoppedChangingDelay)
config.set('spell-check.grammars', ['source.js'])
waitsFor ->
editor.find('.misspelling').length > 0
runs ->
expect(editor.find('.misspelling').length).toBe 1
config.set('spell-check.grammars', [])
expect(editor.find('.misspelling').length).toBe 0
describe "when 'editor:correct-misspelling' is triggered on the editor", ->
describe "when the cursor touches a misspelling that has corrections", ->
it "displays the corrections for the misspelling and replaces the misspelling when a correction is selected", ->
editor.setText('fryday')
advanceClock(editor.getBuffer().stoppedChangingDelay)
config.set('spell-check.grammars', ['source.js'])
waitsFor ->
editor.find('.misspelling').length > 0
runs ->
editor.trigger 'editor:correct-misspelling'
expect(editor.find('.corrections').length).toBe 1
expect(editor.find('.corrections li').length).toBeGreaterThan 0
expect(editor.find('.corrections li:first').text()).toBe "Friday"
editor.find('.corrections').view().confirmSelection()
expect(editor.getText()).toBe 'Friday'
expect(editor.getCursorBufferPosition()).toEqual [0, 6]
advanceClock(editor.getBuffer().stoppedChangingDelay)
expect(editor.find('.misspelling')).toBeHidden()
expect(editor.find('.corrections').length).toBe 0
describe "when the cursor touches a misspelling that has no corrections", ->
it "displays a message saying no corrections found", ->
editor.setText('asdfasdf')
advanceClock(editor.getBuffer().stoppedChangingDelay)
config.set('spell-check.grammars', ['source.js'])
waitsFor ->
editor.find('.misspelling').length > 0
runs ->
editor.trigger 'editor:correct-misspelling'
expect(editor.find('.corrections').length).toBe 1
expect(editor.find('.corrections li').length).toBe 0
expect(editor.find('.corrections .error').text()).toBe "No corrections found"

View File

@@ -0,0 +1,4 @@
.misspelling {
border-bottom: 1px dashed rgba(250, 128, 114, .5);
position: absolute;
}

View File

@@ -27,12 +27,11 @@ class SymbolsView extends SelectList
$$ ->
@li =>
@div name, class: 'label'
@div class: 'right', =>
if position
text = "Line #{position.row + 1}"
else
text = fs.base(file)
@div text, class: 'function-details'
if position
text = "Line #{position.row + 1}"
else
text = fs.base(file)
@div text, class: 'right function-details'
toggleFileSymbols: ->
if @hasParent()

View File

@@ -104,28 +104,34 @@ describe "SymbolsView", ->
describe "TagGenerator", ->
it "generates tags for all JavaScript functions", ->
tags = []
waitsForPromise ->
tags = []
path = require.resolve('fixtures/sample.js')
callback = (tag) ->
tags.push tag
generator = new TagGenerator(path, callback)
generator.generate().done ->
expect(tags.length).toBe 2
expect(tags[0].name).toBe "quicksort"
expect(tags[0].position.row).toBe 0
expect(tags[1].name).toBe "quicksort.sort"
expect(tags[1].position.row).toBe 1
generator.generate()
runs ->
expect(tags.length).toBe 2
expect(tags[0].name).toBe "quicksort"
expect(tags[0].position.row).toBe 0
expect(tags[1].name).toBe "quicksort.sort"
expect(tags[1].position.row).toBe 1
it "generates no tags for text file", ->
tags = []
waitsForPromise ->
tags = []
path = require.resolve('fixtures/sample.txt')
callback = (tag) ->
tags.push tag
generator = new TagGenerator(path, callback)
generator.generate().done ->
expect(tags.length).toBe 0
generator.generate()
runs ->
expect(tags.length).toBe 0
describe "go to declaration", ->
it "doesn't move the cursor when no declaration is found", ->
@@ -155,11 +161,15 @@ describe "SymbolsView", ->
expect(rootView.getActiveEditor().getCursorBufferPosition()).toEqual [0,4]
describe "when the tag is in a file that doesn't exist", ->
renamedPath = null
beforeEach ->
fs.move(project.resolve("tagged-duplicate.js"), project.resolve("tagged-duplicate-renamed.js"))
renamedPath = project.resolve("tagged-duplicate-renamed.js")
fs.remove(renamedPath) if fs.exists(renamedPath)
fs.move(project.resolve("tagged-duplicate.js"), renamedPath)
afterEach ->
fs.move(project.resolve("tagged-duplicate-renamed.js"), project.resolve("tagged-duplicate.js"))
fs.move(renamedPath, project.resolve("tagged-duplicate.js"))
it "doesn't display the tag", ->
rootView.open("tagged.js")

View File

@@ -0,0 +1,50 @@
'name': 'TOML'
'scopeName': 'source.toml'
'fileTypes': ['toml']
'patterns': [
{
'match': '(?:^\\s*)(\\[([^\\]]+)\\])'
'captures':
'2': 'name': 'variable.keygroup.toml'
'name': 'keygroup.toml'
}
{
'match': '(?:^\\s*)(\\S+)\\s*='
'captures':
'1': 'name': 'entity.key.toml'
'name': 'key.toml'
}
{
'begin': '"'
'beginCaptures':
'0': 'name': 'string.begin.toml'
'end': '"'
'endCaptures':
'0': 'name': 'string.end.toml'
'name': 'string.toml'
'patterns': [
'match': '\\\\[nt0r"\\\\]'
'name' : 'constant.character.escape.toml'
]
}
{
'match': '#.*$'
'name': 'comment.toml'
}
{
'match': 'true'
'name': 'constant.language.boolean.true.toml'
}
{
'match': 'false'
'name': 'constant.language.boolean.false.toml'
}
{
'match': '\\d{4}-\\d{2}-\\d{2}(T)\\d{2}:\\d{2}:\\d{2}(Z)'
'name': 'support.date.toml'
}
{
'match': '-?\\d+(\\.?\\d+)?'
'name': 'constant.numeric.toml'
}
]

View File

@@ -0,0 +1,65 @@
TextMatePackage = require 'text-mate-package'
describe "TOML grammar", ->
grammar = null
beforeEach ->
spyOn(syntax, "addGrammar")
pack = new TextMatePackage(require.resolve("toml.tmbundle"))
pack.load()
grammar = pack.grammars[0]
it "parses the grammar", ->
expect(grammar).toBeTruthy()
expect(grammar.scopeName).toBe "source.toml"
it "tokenizes comments", ->
{tokens} = grammar.tokenizeLine("# I am a comment")
expect(tokens[0]).toEqual value: "# I am a comment", scopes: ["source.toml", "comment.toml"]
it "tokenizes strings", ->
{tokens} = grammar.tokenizeLine('"I am a string"')
expect(tokens[0]).toEqual value: '"', scopes: ["source.toml", "string.toml", "string.begin.toml"]
expect(tokens[1]).toEqual value: 'I am a string', scopes: ["source.toml", "string.toml"]
expect(tokens[2]).toEqual value: '"', scopes: ["source.toml", "string.toml","string.end.toml"]
{tokens} = grammar.tokenizeLine('"I\'m \\n escaped"')
expect(tokens[0]).toEqual value: '"', scopes: ["source.toml", "string.toml", "string.begin.toml"]
expect(tokens[1]).toEqual value: "I'm ", scopes: ["source.toml", "string.toml"]
expect(tokens[2]).toEqual value: "\\n", scopes: ["source.toml", "string.toml", "constant.character.escape.toml"]
expect(tokens[3]).toEqual value: " escaped", scopes: ["source.toml", "string.toml"]
expect(tokens[4]).toEqual value: '"', scopes: ["source.toml", "string.toml", "string.end.toml"]
it "tokenizes booleans", ->
{tokens} = grammar.tokenizeLine("true")
expect(tokens[0]).toEqual value: "true", scopes: ["source.toml", "constant.language.boolean.true.toml"]
{tokens} = grammar.tokenizeLine("false")
expect(tokens[0]).toEqual value: "false", scopes: ["source.toml", "constant.language.boolean.false.toml"]
it "tokenizes numbers", ->
{tokens} = grammar.tokenizeLine("123")
expect(tokens[0]).toEqual value: "123", scopes: ["source.toml", "constant.numeric.toml"]
{tokens} = grammar.tokenizeLine("-1")
expect(tokens[0]).toEqual value: "-1", scopes: ["source.toml", "constant.numeric.toml"]
{tokens} = grammar.tokenizeLine("3.14")
expect(tokens[0]).toEqual value: "3.14", scopes: ["source.toml", "constant.numeric.toml"]
{tokens} = grammar.tokenizeLine("-123.456")
expect(tokens[0]).toEqual value: "-123.456", scopes: ["source.toml", "constant.numeric.toml"]
it "tokenizes dates", ->
{tokens} = grammar.tokenizeLine("1979-05-27T07:32:00Z")
expect(tokens[0]).toEqual value: "1979-05-27T07:32:00Z", scopes: ["source.toml", "support.date.toml"]
it "tokenizes keygroups", ->
{tokens} = grammar.tokenizeLine("[keygroup]")
expect(tokens[0]).toEqual value: "[", scopes: ["source.toml", "keygroup.toml"]
expect(tokens[1]).toEqual value: "keygroup", scopes: ["source.toml", "keygroup.toml", "variable.keygroup.toml"]
expect(tokens[2]).toEqual value: "]", scopes: ["source.toml", "keygroup.toml"]
it "tokenizes keys", ->
{tokens} = grammar.tokenizeLine("key =")
expect(tokens[0]).toEqual value: "key", scopes: ["source.toml", "key.toml", "entity.key.toml"]
expect(tokens[1]).toEqual value: " =", scopes: ["source.toml", "key.toml"]

View File

@@ -72,15 +72,12 @@ class TreeView extends ScrollView
if @hasFocus()
@detach()
else
if @hasParent()
@focus()
else
@attach()
@attach() unless @hasParent()
@focus()
attach: ->
return unless project.getPath()
rootView.horizontal.prepend(this)
@focus()
detach: ->
@scrollTopAfterAttach = @scrollTop()
@@ -134,6 +131,7 @@ class TreeView extends ScrollView
revealActiveFile: ->
@attach()
@focus()
return unless activeFilePath = rootView.getActiveEditor()?.getPath()

View File

@@ -1,11 +1,11 @@
module.exports =
class GitRepository
constructor: (path) ->
@open: (path) ->
unless repo = $git.getRepository(path)
throw new Error("No Git repository found searching path: " + path)
throw new Error("No Git repository found searching path: #{path}")
repo.constructor = GitRepository
repo.__proto__ = GitRepository.prototype
return repo
repo
getHead: $git.getHead
getPath: $git.getPath

View File

@@ -1,11 +1,11 @@
module.exports =
class OnigRegExp
constructor: (source) ->
@create: (source) ->
regexp = $onigRegExp.buildOnigRegExp(source);
regexp.constructor = OnigRegExp
regexp.__proto__ = OnigRegExp.prototype
regexp.source = source
return regexp
regexp
search: $onigRegExp.search
test: $onigRegExp.test

View File

@@ -1,10 +1,10 @@
module.exports =
class OnigScanner
constructor: (sources) ->
@create: (sources) ->
scanner = $onigScanner.buildScanner(sources)
scanner.constructor = OnigScanner
scanner.__proto__ = OnigScanner.prototype
scanner.sources = sources
return scanner
scanner
findNextMatch: $onigScanner.findNextMatch

View File

@@ -53,7 +53,7 @@ exts =
code or= __read file
eval("define(function(require, exports, module) { 'use strict';#{code}})\n//@ sourceURL=#{file}")
__defines.pop()?.call()
coffee: (file, retry=true) ->
coffee: (file) ->
cacheFilePath = getCacheFilePath(file)
if __exists(cacheFilePath)
compiled = __read(cacheFilePath)

View File

@@ -61,22 +61,20 @@
content: '\f078';
font-family: 'Octicons Regular';
-webkit-font-smoothing: antialiased;
color: #fba0e3;
opacity: .8;
visibility: hidden;
}
.editor .fold-marker:after {
font-size: .8em;
content: '\f09a';
opacity: .8;
color: #fba0e3;
padding-left: .2em;
font-family: 'Octicons Regular';
-webkit-font-smoothing: antialiased;
position: relative;
top: 2px;
}
.editor .line.cursor-line .fold-marker {
.editor .line.cursor-line .fold-marker:after {
opacity: 1;
}

View File

@@ -4,9 +4,10 @@
overflow-y: auto;
max-height: 312px;
margin: 0;
background: red;
padding: 0;
line-height: 100%;
-webkit-user-select: none;
cursor: default;
}
.select-list ol:empty {
@@ -42,4 +43,19 @@
font-weight: bold;
color: white;
text-shadow: 0 1px 0 #4E0000;
}
}
.select-list li.active-item:before {
font-family: 'Octicons Regular';
font-size: 14px;
width: 14px;
height: 14px;
margin-right: 5px;
-webkit-font-smoothing: antialiased;
color: #9d9d9d;
content: '\f03a';
}
.select-list li.inactive-item {
padding-left: 29px;
}

View File

@@ -3,6 +3,12 @@
color: #c5c8c6;
}
.editor .gutter .line-number.fold,
.editor .gutter .line-number:after,
.editor .fold-marker:after {
color: #fba0e3;
}
.editor .invisible {
color: #c5c8c6;
}

View File

@@ -18,8 +18,3 @@
-webkit-animation-duration: 1s;
-webkit-animation-iteration-count: 1;
}
.editor .gutter .line-number.fold {
color: #fba0e3;
opacity: .8;
}

View File

@@ -7,12 +7,6 @@
color: #ddd;
}
.select-list li .right {
color: #999;
display: inline-block;
margin-top: -3px;
}
.select-list .key-binding {
background: -webkit-linear-gradient(
rgba(100, 100, 100, 0.5),
@@ -24,13 +18,14 @@
}
.select-list li:hover {
background-color: #555;
background-color: #2c2c2c;
}
.select-list ol .selected {
background-image: -webkit-linear-gradient(#7e7e7e, #737373);
}
.select-list .right,
.select-list .directory {
color: #777;
}
@@ -39,6 +34,7 @@
color: #fff;
}
.select-list ol .selected .right,
.select-list ol .selected .directory {
color: #ccc;
}
}

View File

@@ -3,6 +3,12 @@
color: #555;
}
.editor .gutter .line-number.fold,
.editor .gutter .line-number:after,
.editor .fold-marker:after {
color: #e87b00;
}
.editor .invisible {
color: #555;
}

View File

@@ -103,3 +103,11 @@
.command-panel li.operation.selected .preview {
color: #333;
}
.error-messages {
color: #333;
}
.error .error-messages {
color: white;
}

View File

@@ -24,8 +24,3 @@
-webkit-animation-duration: 1s;
-webkit-animation-iteration-count: 1;
}
.editor .gutter .line-number.fold {
color: #fba0e3;
opacity: .8;
}

View File

@@ -0,0 +1,6 @@
.overlay {
background-color: #e5e5e5;
border-top: 1px solid #979797;
color: #333;
padding: 10px;
}

View File

@@ -1,6 +1,7 @@
'stylesheets': [
'atom.css'
'editor.css'
'overlay.css'
'select-list.css'
'tree-view.css'
'tabs.css'

View File

@@ -20,4 +20,14 @@
.select-list ol .selected {
background-color: #e0e0e0;
}
}
.select-list .right,
.select-list .directory {
color: #777;
}
.select-list ol .selected .right,
.select-list ol .selected .directory {
color: #333;
}

View File

@@ -12,4 +12,10 @@
.status-bar .git-status.octicons.new-status-icon {
color: #5293d8;
display: inline-block;
}
}
.status-bar .grammar-name:hover {
color: #000;
border: 1px solid rgba(50, 50, 50, 0.2);
border-radius: 2px;
}

1
vendor/packages/php.tmbundle vendored Submodule