diff --git a/Rakefile b/Rakefile index e75cd8834..3f3c926eb 100644 --- a/Rakefile +++ b/Rakefile @@ -28,7 +28,7 @@ end desc "Download node binary" task "update-node" do - `script/update-node` + `script/update-node v0.10.1` end desc "Download debug symbols for CEF" @@ -86,6 +86,7 @@ task :clean do `rm -rf #{BUILD_DIR}` `rm -rf /tmp/atom-compiled-scripts` `rm -rf node_modules` + `rm -rf cef` end desc "Run the specs" diff --git a/native/atom_cef_render_process_handler.h b/native/atom_cef_render_process_handler.h index 973086245..96fe15a5c 100644 --- a/native/atom_cef_render_process_handler.h +++ b/native/atom_cef_render_process_handler.h @@ -16,17 +16,6 @@ class AtomCefRenderProcessHandler : public CefRenderProcessHandler { virtual bool OnProcessMessageReceived(CefRefPtr browser, CefProcessId source_process, CefRefPtr message) OVERRIDE; - virtual void OnWorkerContextCreated(int worker_id, - const CefString& url, - CefRefPtr context) OVERRIDE; - virtual void OnWorkerContextReleased(int worker_id, - const CefString& url, - CefRefPtr context) OVERRIDE; - virtual void OnWorkerUncaughtException(int worker_id, - const CefString& url, - CefRefPtr context, - CefRefPtr exception, - CefRefPtr stackTrace) OVERRIDE; void Reload(CefRefPtr browser); void Shutdown(CefRefPtr browser); @@ -37,4 +26,3 @@ class AtomCefRenderProcessHandler : public CefRenderProcessHandler { }; #endif // ATOM_CEF_RENDER_PROCESS_HANDLER_H_ - diff --git a/native/atom_cef_render_process_handler.mm b/native/atom_cef_render_process_handler.mm index fd0a60a58..b3147478c 100644 --- a/native/atom_cef_render_process_handler.mm +++ b/native/atom_cef_render_process_handler.mm @@ -21,27 +21,6 @@ void AtomCefRenderProcessHandler::OnContextReleased(CefRefPtr browse [PathWatcher removePathWatcherForContext:context]; } -void AtomCefRenderProcessHandler::OnWorkerContextCreated(int worker_id, - const CefString& url, - CefRefPtr context) { - InjectExtensionsIntoV8Context(context); -} - -void AtomCefRenderProcessHandler::OnWorkerContextReleased(int worker_id, - const CefString& url, - CefRefPtr context) { -} - -void AtomCefRenderProcessHandler::OnWorkerUncaughtException(int worker_id, - const CefString& url, - CefRefPtr context, - CefRefPtr exception, - CefRefPtr stackTrace) { - - std::string message = exception->GetMessage().ToString(); - NSLog(@"Exception throw in worker thread %s", message.c_str()); -} - bool AtomCefRenderProcessHandler::OnProcessMessageReceived(CefRefPtr browser, CefProcessId source_process, CefRefPtr message) { diff --git a/native/v8_extensions/native.mm b/native/v8_extensions/native.mm index 853d27c5f..03c92eda0 100644 --- a/native/v8_extensions/native.mm +++ b/native/v8_extensions/native.mm @@ -24,8 +24,7 @@ namespace v8_extensions { const char* methodNames[] = { "writeToPasteboard", "readFromPasteboard", "quit", "watchPath", "unwatchPath", "getWatchedPaths", "unwatchAllPaths", "moveToTrash", - "reload", "setWindowState", "getWindowState", "isMisspelled", - "getCorrectionsForMisspelling" + "reload", "setWindowState", "getWindowState", "beep" }; CefRefPtr nativeObject = CefV8Value::CreateObject(NULL); @@ -79,8 +78,8 @@ namespace v8_extensions { CefV8ValueList args; - args.push_back(CefV8Value::CreateString(std::string([eventType UTF8String], [eventType lengthOfBytesUsingEncoding:NSUTF8StringEncoding]))); - args.push_back(CefV8Value::CreateString(std::string([path UTF8String], [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding]))); + args.push_back(CefV8Value::CreateString(string([eventType UTF8String], [eventType lengthOfBytesUsingEncoding:NSUTF8StringEncoding]))); + args.push_back(CefV8Value::CreateString(string([path UTF8String], [path lengthOfBytesUsingEncoding:NSUTF8StringEncoding]))); function->ExecuteFunction(function, args); context->Exit(); @@ -92,7 +91,7 @@ namespace v8_extensions { retval = CefV8Value::CreateString([watchId UTF8String]); } else { - exception = std::string("Failed to watch path '") + std::string([path UTF8String]) + std::string("' (it may not exist)"); + exception = string("Failed to watch path '") + string([path UTF8String]) + string("' (it may not exist)"); } return true; @@ -138,7 +137,7 @@ namespace v8_extensions { tag:nil]; if (!success) { - std::string exception = "Can not move "; + string exception = "Can not move "; exception += [sourcePath UTF8String]; exception += " to trash."; } @@ -163,32 +162,8 @@ namespace v8_extensions { return true; } - else if (name == "isMisspelled") { - NSString *word = stringFromCefV8Value(arguments[0]); - NSSpellChecker *spellChecker = [NSSpellChecker sharedSpellChecker]; - @synchronized(spellChecker) { - NSRange range = [spellChecker 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]; - @synchronized(spellChecker) { - 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 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; + else if (name == "beep") { + NSBeep(); } return false; @@ -196,7 +171,7 @@ namespace v8_extensions { }; NSString *stringFromCefV8Value(const CefRefPtr& value) { - std::string cc_value = value->GetStringValue().ToString(); + string cc_value = value->GetStringValue().ToString(); return [NSString stringWithUTF8String:cc_value.c_str()]; } @@ -204,7 +179,7 @@ namespace v8_extensions { CefV8ValueList arguments; message = [message stringByAppendingFormat:@"\n%s", exception->GetMessage().ToString().c_str()]; - arguments.push_back(CefV8Value::CreateString(std::string([message UTF8String], [message lengthOfBytesUsingEncoding:NSUTF8StringEncoding]))); + arguments.push_back(CefV8Value::CreateString(string([message UTF8String], [message lengthOfBytesUsingEncoding:NSUTF8StringEncoding]))); CefRefPtr console = global->GetValue("console"); console->GetValue("error")->ExecuteFunction(console, arguments); diff --git a/package.json b/package.json index 4f7be7832..a1152052b 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,10 @@ "coffee-cache": "0.1.0", "pegjs": "0.7.0", "async": "0.2.6", + "nak": "0.2.4", + "spellchecker": "0.2.0", "plist": "git://github.com/nathansobo/node-plist.git", - "nak": "0.2.4" + "space-pen": "git://github.com/nathansobo/space-pen.git" }, "private": true, @@ -22,4 +24,4 @@ "scripts": { "preinstall": "true" } -} +} \ No newline at end of file diff --git a/script/bootstrap b/script/bootstrap index a3ffd75bf..4db2c1279 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -21,8 +21,8 @@ exit_unless_npm_exists npm install npm --silent NODE_DIR="$HOME/.cefode-gyp" -NODE_VERSION="0.8.21" -NODE_URL="https://gh-contractor-zcbenz.s3.amazonaws.com/cefode/dist" +NODE_VERSION="0.10.1" +NODE_URL="https://gh-contractor-zcbenz.s3.amazonaws.com/cefode2/dist" if [ ! -d "node_modules/node-gyp" ]; then ./node_modules/.bin/npm install node-gyp --silent HOME="$NODE_DIR" ./node_modules/.bin/node-gyp install --target="$NODE_VERSION" --dist-url="$NODE_URL" --arch=ia32 diff --git a/script/update-cefode b/script/update-cefode index 7f582be19..a715242c6 100755 --- a/script/update-cefode +++ b/script/update-cefode @@ -13,7 +13,7 @@ else TARGET=$1 fi -DISTURL="https://gh-contractor-zcbenz.s3.amazonaws.com/cefode/prebuilt-cef" +DISTURL="https://gh-contractor-zcbenz.s3.amazonaws.com/cefode2/prebuilt-cef" CEF_BASENAME="cef_binary_3.1423.1133_macosx" CEF_SYMBOLS_BASENAME="${CEF_BASENAME}_symbols" @@ -29,7 +29,7 @@ fi CURRENT_VERSION=`cat cef/version 2>&1` if [[ $LATEST_VERSION != $CURRENT_VERSION ]]; then - echo "Downloading/extracting cefode u${LATEST_VERSION}..." + echo "Downloading/extracting cefode2 u${LATEST_VERSION}..." curl --progress-bar "${DISTURL}/cef_binary_latest.zip" > "${TEMP_DIR}/cef.zip" unzip -q "${TEMP_DIR}/cef.zip" -d "${TEMP_DIR}" [ -e "${TARGET}" ] && rm -rf "${TARGET}" @@ -41,7 +41,7 @@ if [[ "${SYMBOLS}" != "1" ]]; then exit 0 fi -echo "Downloading/extracting symbols for cefode u${LATEST_VERSION}..." +echo "Downloading/extracting symbols for cefode2 u${LATEST_VERSION}..." curl --progress-bar "${DISTURL}/cef_binary_latest_symbols.zip" > "${TEMP_DIR}/symbols.zip" unzip -q "${TEMP_DIR}/symbols.zip" -d "${TEMP_DIR}" mv "${TEMP_DIR}/${CEF_SYMBOLS_BASENAME}"/* "${TARGET}/Release" diff --git a/script/update-node b/script/update-node index e3a249707..bbbb6d9e3 100755 --- a/script/update-node +++ b/script/update-node @@ -4,35 +4,24 @@ set -e cd "$(dirname $0)/.." +NODE_VERSION=v0.10.1 +[ -z $1 ] || NODE_VERSION=$1 + # Test whether we need update. -if [ -f "node/node" ]; then +if [ -f "node/node" ] && [[ `node/node --version` == $NODE_VERSION ]] ; then exit 0 fi -if ! DOWNLOAD_PAGE=$(curl -fsSkL http://nodejs.org/download/); then - exit 1; -fi - -NODE_VERSION=$(echo "$DOWNLOAD_PAGE" \ - | awk '/Current version:/ { print }' \ - | awk -F"[<>]" '{ print $5 }') - case $OSTYPE in darwin*) NODE_PLATFORM=darwin ;; linux*) NODE_PLATFORM=linux ;; *) echo "Unsupported platform $OSTYPE" && exit 1 ;; esac -if uname -a | grep 'x86_64' > /dev/null ; then - NODE_ARCH=x64 -else - NODE_ARCH=x86 -fi - -NODE_DIST_NAME="node-$NODE_VERSION-$NODE_PLATFORM-$NODE_ARCH" +NODE_DIST_NAME="node-$NODE_VERSION-$NODE_PLATFORM-x86" # Download node and untar -NODE_TARBALL_URL="http://nodejs.org/dist/$NODE_VERSION/$NODE_DIST_NAME.tar.gz" +NODE_TARBALL_URL="https://gh-contractor-zcbenz.s3.amazonaws.com/node/dist/$NODE_DIST_NAME.tar.gz" TARGET_DIR='node' [ -d "$TARGET_DIR" ] || mkdir "$TARGET_DIR" cd "$TARGET_DIR" diff --git a/spec/app/atom-package-spec.coffee b/spec/app/atom-package-spec.coffee deleted file mode 100644 index 1f291b896..000000000 --- a/spec/app/atom-package-spec.coffee +++ /dev/null @@ -1,98 +0,0 @@ -RootView = require 'root-view' -AtomPackage = require 'atom-package' -fs = require 'fs-utils' - -describe "AtomPackage", -> - [packageMainModule, pack] = [] - - beforeEach -> - pack = new AtomPackage(fs.resolve(config.packageDirPaths..., 'package-with-activation-events')) - pack.load() - - describe ".load()", -> - describe "if the package's metadata has a `deferredDeserializers` array", -> - it "requires the package's main module attempting to use deserializers named in the array", -> - expect(pack.mainModule).toBeNull() - object = deserialize(deserializer: 'Foo', data: "Hello") - expect(object.constructor.name).toBe 'Foo' - expect(object.data).toBe 'Hello' - expect(pack.mainModule).toBeDefined() - expect(pack.mainModule.activateCallCount).toBe 0 - - describe ".activate()", -> - beforeEach -> - window.rootView = new RootView - packageMainModule = require 'fixtures/packages/package-with-activation-events/main' - spyOn(packageMainModule, 'activate').andCallThrough() - - describe "when the package metadata includes activation events", -> - beforeEach -> - pack.activate() - - it "defers activating the package until an activation event bubbles to the root view", -> - expect(packageMainModule.activate).not.toHaveBeenCalled() - rootView.trigger 'activation-event' - expect(packageMainModule.activate).toHaveBeenCalled() - - it "triggers the activation event on all handlers registered during activation", -> - rootView.open('sample.js') - editor = rootView.getActiveView() - eventHandler = jasmine.createSpy("activation-event") - editor.command 'activation-event', eventHandler - editor.trigger 'activation-event' - expect(packageMainModule.activate.callCount).toBe 1 - expect(packageMainModule.activationEventCallCount).toBe 1 - expect(eventHandler.callCount).toBe 1 - editor.trigger 'activation-event' - expect(packageMainModule.activationEventCallCount).toBe 2 - expect(eventHandler.callCount).toBe 2 - expect(packageMainModule.activate.callCount).toBe 1 - - describe "when the package does not specify a main module", -> - describe "when the package has an index.coffee", -> - it "uses index.coffee as the main module", -> - pack = new AtomPackage(fs.resolve(config.packageDirPaths..., 'package-with-module')) - packageMainModule = require 'fixtures/packages/package-with-module' - spyOn(packageMainModule, 'activate').andCallThrough() - - expect(packageMainModule.activate).not.toHaveBeenCalled() - pack.load() - pack.activate() - expect(packageMainModule.activate).toHaveBeenCalled() - - describe "when the package doesn't have an index.coffee", -> - it "does not throw an exception or log an error", -> - spyOn(console, "error") - spyOn(console, "warn").andCallThrough() - pack = new AtomPackage(fs.resolve(config.packageDirPaths..., 'package-with-keymaps-manifest')) - - expect(-> pack.load()).not.toThrow() - expect(console.error).not.toHaveBeenCalled() - expect(console.warn).not.toHaveBeenCalled() - - describe "when a package is activated", -> - it "loads config defaults based on the `configDefaults` key", -> - expect(config.get('package-with-module.numbers.one')).toBeUndefined() - window.loadPackage("package-with-module") - expect(config.get('package-with-module.numbers.one')).toBe 1 - expect(config.get('package-with-module.numbers.two')).toBe 2 - - describe "when the package has a grammars directory", -> - it "loads the grammar and correctly parses a keyword", -> - spyOn(syntax, 'addGrammar') - window.loadPackage("package-with-a-cson-grammar") - expect(syntax.addGrammar).toHaveBeenCalled() - grammar = syntax.addGrammar.argsForCall[0][0] - expect(grammar.scopeName).toBe "source.alot" - {tokens} = grammar.tokenizeLine("this is alot of code") - expect(tokens[1]).toEqual value: "alot", scopes: ["source.alot", "keyword.alot"] - - describe "when the package has a scoped properties directory", -> - it "loads the scoped properties", -> - spyOn(syntax, 'addProperties') - window.loadPackage("package-with-scoped-properties") - - expect(syntax.addProperties).toHaveBeenCalled() - [selector, properties] = syntax.addProperties.argsForCall[0] - expect(selector).toBe ".source.omg" - expect(properties).toEqual {editor: increaseIndentPattern: '^a'} diff --git a/spec/app/atom-spec.coffee b/spec/app/atom-spec.coffee index 3b765e1f8..8b32d3d79 100644 --- a/spec/app/atom-spec.coffee +++ b/spec/app/atom-spec.coffee @@ -1,3 +1,4 @@ +$ = require 'jquery' RootView = require 'root-view' {$$} = require 'space-pen' fs = require 'fs-utils' @@ -6,115 +7,239 @@ describe "the `atom` global", -> beforeEach -> window.rootView = new RootView - describe "when a package is built and loaded", -> - [extension, stylesheetPath] = [] + describe "package lifecycle methods", -> + describe ".loadPackage(id)", -> + describe "when the package has deferred deserializers", -> + it "requires the package's main module if one of its deferred deserializers is referenced", -> + pack = atom.loadPackage('package-with-activation-events') + expect(pack.mainModule).toBeNull() + object = deserialize({deserializer: 'Foo', data: 5}) + expect(pack.mainModule).toBeDefined() + expect(object.constructor.name).toBe 'Foo' + expect(object.data).toBe 5 - beforeEach -> - extension = require "package-with-module" - stylesheetPath = fs.resolveOnLoadPath("fixtures/packages/package-with-module/stylesheets/styles.css") + describe ".activatePackage(id)", -> + describe "atom packages", -> + describe "when the package has a main module", -> + describe "when the metadata specifies a main module path˜", -> + it "requires the module at the specified path", -> + mainModule = require('package-with-main/main-module') + spyOn(mainModule, 'activate') + pack = atom.activatePackage('package-with-main') + expect(mainModule.activate).toHaveBeenCalled() + expect(pack.mainModule).toBe mainModule - afterEach -> - removeStylesheet(stylesheetPath) + describe "when the metadata does not specify a main module", -> + it "requires index.coffee", -> + indexModule = require('package-with-index/index') + spyOn(indexModule, 'activate') + pack = atom.activatePackage('package-with-index') + expect(indexModule.activate).toHaveBeenCalled() + expect(pack.mainModule).toBe indexModule - it "requires and activates the package's main module if it exists", -> - spyOn(atom, 'activateAtomPackage').andCallThrough() - window.loadPackage("package-with-module") - expect(atom.activateAtomPackage).toHaveBeenCalled() + it "assigns config defaults from the module", -> + expect(config.get('package-with-config-defaults.numbers.one')).toBeUndefined() + atom.activatePackage('package-with-config-defaults') + expect(config.get('package-with-config-defaults.numbers.one')).toBe 1 + expect(config.get('package-with-config-defaults.numbers.two')).toBe 2 - it "logs warning instead of throwing an exception if a package fails to load", -> - config.set("core.disabledPackages", []) - spyOn(console, "warn") - expect(-> window.loadPackage("package-that-throws-an-exception")).not.toThrow() - expect(console.warn).toHaveBeenCalled() + describe "when the package metadata includes activation events", -> + [mainModule, pack] = [] - describe "keymap loading", -> - describe "when package.json does not contain a 'keymaps' manifest", -> - it "loads all the .cson/.json files in the keymaps directory", -> - element1 = $$ -> @div class: 'test-1' - element2 = $$ -> @div class: 'test-2' - element3 = $$ -> @div class: 'test-3' + beforeEach -> + mainModule = require 'package-with-activation-events/index' + spyOn(mainModule, 'activate').andCallThrough() + AtomPackage = require 'atom-package' + spyOn(AtomPackage.prototype, 'requireMainModule').andCallThrough() + pack = atom.activatePackage('package-with-activation-events') - expect(keymap.bindingsForElement(element1)['ctrl-z']).toBeUndefined() - expect(keymap.bindingsForElement(element2)['ctrl-z']).toBeUndefined() - expect(keymap.bindingsForElement(element3)['ctrl-z']).toBeUndefined() + it "defers requiring/activating the main module until an activation event bubbles to the root view", -> + expect(pack.requireMainModule).not.toHaveBeenCalled() + expect(mainModule.activate).not.toHaveBeenCalled() + rootView.trigger 'activation-event' + expect(mainModule.activate).toHaveBeenCalled() - window.loadPackage("package-with-module") + it "triggers the activation event on all handlers registered during activation", -> + rootView.open() + editor = rootView.getActiveView() + eventHandler = jasmine.createSpy("activation-event") + editor.command 'activation-event', eventHandler + editor.trigger 'activation-event' + expect(mainModule.activate.callCount).toBe 1 + expect(mainModule.activationEventCallCount).toBe 1 + expect(eventHandler.callCount).toBe 1 + editor.trigger 'activation-event' + expect(mainModule.activationEventCallCount).toBe 2 + expect(eventHandler.callCount).toBe 2 + expect(mainModule.activate.callCount).toBe 1 - expect(keymap.bindingsForElement(element1)['ctrl-z']).toBe "test-1" - expect(keymap.bindingsForElement(element2)['ctrl-z']).toBe "test-2" - expect(keymap.bindingsForElement(element3)['ctrl-z']).toBeUndefined() + describe "when the package has no main module", -> + it "does not throw an exception", -> + spyOn(console, "error") + spyOn(console, "warn").andCallThrough() + expect(-> atom.activatePackage('package-without-module')).not.toThrow() + expect(console.error).not.toHaveBeenCalled() + expect(console.warn).not.toHaveBeenCalled() - describe "when package.json contains a 'keymaps' manifest", -> - it "loads only the keymaps specified by the manifest, in the specified order", -> - element1 = $$ -> @div class: 'test-1' - element3 = $$ -> @div class: 'test-3' + it "passes the activate method the package's previously serialized state if it exists", -> + pack = atom.activatePackage("package-with-serialization") + expect(pack.mainModule.someNumber).not.toBe 77 + pack.mainModule.someNumber = 77 + atom.deactivatePackage("package-with-serialization") + spyOn(pack.mainModule, 'activate').andCallThrough() + atom.activatePackage("package-with-serialization") + expect(pack.mainModule.activate).toHaveBeenCalledWith({someNumber: 77}) - expect(keymap.bindingsForElement(element1)['ctrl-z']).toBeUndefined() + it "logs warning instead of throwing an exception if the package fails to load", -> + config.set("core.disabledPackages", []) + spyOn(console, "warn") + expect(-> atom.activatePackage("package-that-throws-an-exception")).not.toThrow() + expect(console.warn).toHaveBeenCalled() - window.loadPackage("package-with-keymaps-manifest") + describe "keymap loading", -> + describe "when the metadata does not contain a 'keymaps' manifest", -> + it "loads all the .cson/.json files in the keymaps directory", -> + element1 = $$ -> @div class: 'test-1' + element2 = $$ -> @div class: 'test-2' + element3 = $$ -> @div class: 'test-3' - expect(keymap.bindingsForElement(element1)['ctrl-z']).toBe 'keymap-1' - expect(keymap.bindingsForElement(element1)['ctrl-n']).toBe 'keymap-2' - expect(keymap.bindingsForElement(element3)['ctrl-y']).toBeUndefined() + expect(keymap.bindingsForElement(element1)['ctrl-z']).toBeUndefined() + expect(keymap.bindingsForElement(element2)['ctrl-z']).toBeUndefined() + expect(keymap.bindingsForElement(element3)['ctrl-z']).toBeUndefined() - it "loads stylesheets associated with the package", -> - stylesheetPath = fs.resolveOnLoadPath("fixtures/packages/package-with-module/stylesheets/styles.css") - expect(stylesheetElementForId(stylesheetPath).length).toBe 0 - window.loadPackage("package-with-module") - expect(stylesheetElementForId(stylesheetPath).length).toBe 1 + atom.activatePackage("package-with-keymaps") - describe "package lifecycle", -> - describe "activation", -> - it "calls activate on the package main with its previous state", -> - pack = window.loadPackage('package-with-module') - spyOn(pack.mainModule, 'activate') + expect(keymap.bindingsForElement(element1)['ctrl-z']).toBe "test-1" + expect(keymap.bindingsForElement(element2)['ctrl-z']).toBe "test-2" + expect(keymap.bindingsForElement(element3)['ctrl-z']).toBeUndefined() - serializedState = rootView.serialize() - rootView.deactivate() - RootView.deserialize(serializedState) - window.loadPackage('package-with-module') + describe "when the metadata contains a 'keymaps' manifest", -> + it "loads only the keymaps specified by the manifest, in the specified order", -> + element1 = $$ -> @div class: 'test-1' + element3 = $$ -> @div class: 'test-3' - expect(pack.mainModule.activate).toHaveBeenCalledWith(someNumber: 1) + expect(keymap.bindingsForElement(element1)['ctrl-z']).toBeUndefined() - describe "deactivation", -> - it "deactivates and removes the package module from the package module map", -> - pack = window.loadPackage('package-with-module') - expect(atom.activatedAtomPackages.length).toBe 1 - spyOn(pack.mainModule, "deactivate").andCallThrough() - atom.deactivateAtomPackages() - expect(pack.mainModule.deactivate).toHaveBeenCalled() - expect(atom.activatedAtomPackages.length).toBe 0 + atom.activatePackage("package-with-keymaps-manifest") - describe "serialization", -> - it "uses previous serialization state on packages whose activation has been deferred", -> - atom.atomPackageStates['package-with-activation-events'] = {previousData: 'exists'} - unactivatedPackage = window.loadPackage('package-with-activation-events') - activatedPackage = window.loadPackage('package-with-module') + expect(keymap.bindingsForElement(element1)['ctrl-z']).toBe 'keymap-1' + expect(keymap.bindingsForElement(element1)['ctrl-n']).toBe 'keymap-2' + expect(keymap.bindingsForElement(element3)['ctrl-y']).toBeUndefined() - expect(atom.serializeAtomPackages()).toEqual - 'package-with-module': - 'someNumber': 1 - 'package-with-activation-events': - 'previousData': 'exists' + describe "stylesheet loading", -> + describe "when the metadata contains a 'stylesheets' manifest", -> + it "loads stylesheets from the stylesheets directory as specified by the manifest", -> + one = fs.resolveOnLoadPath("package-with-stylesheets-manifest/stylesheets/1.css") + two = fs.resolveOnLoadPath("package-with-stylesheets-manifest/stylesheets/2.less") + three = fs.resolveOnLoadPath("package-with-stylesheets-manifest/stylesheets/3.css") + expect(stylesheetElementForId(one)).not.toExist() + expect(stylesheetElementForId(two)).not.toExist() + expect(stylesheetElementForId(three)).not.toExist() - # ensure serialization occurs when the packageis activated - unactivatedPackage.deferActivation = false - unactivatedPackage.activate() - expect(atom.serializeAtomPackages()).toEqual - 'package-with-module': - 'someNumber': 1 - 'package-with-activation-events': - 'previousData': 'overwritten' + atom.activatePackage("package-with-stylesheets-manifest") - it "absorbs exceptions that are thrown by the package module's serialize methods", -> - spyOn(console, 'error') - window.loadPackage('package-with-module', activateImmediately: true) - window.loadPackage('package-with-serialize-error', activateImmediately: true) + expect(stylesheetElementForId(one)).toExist() + expect(stylesheetElementForId(two)).toExist() + expect(stylesheetElementForId(three)).not.toExist() + expect($('#jasmine-content').css('font-size')).toBe '1px' - packageStates = atom.serializeAtomPackages() - expect(packageStates['package-with-module']).toEqual someNumber: 1 - expect(packageStates['package-with-serialize-error']).toBeUndefined() - expect(console.error).toHaveBeenCalled() + describe "when the metadata does not contain a 'stylesheets' manifest", -> + it "loads all stylesheets from the stylesheets directory", -> + one = fs.resolveOnLoadPath("package-with-stylesheets/stylesheets/1.css") + two = fs.resolveOnLoadPath("package-with-stylesheets/stylesheets/2.less") + three = fs.resolveOnLoadPath("package-with-stylesheets/stylesheets/3.css") + expect(stylesheetElementForId(one)).not.toExist() + expect(stylesheetElementForId(two)).not.toExist() + expect(stylesheetElementForId(three)).not.toExist() + + atom.activatePackage("package-with-stylesheets") + expect(stylesheetElementForId(one)).toExist() + expect(stylesheetElementForId(two)).toExist() + expect(stylesheetElementForId(three)).toExist() + expect($('#jasmine-content').css('font-size')).toBe '3px' + + describe "grammar loading", -> + it "loads the package's grammars", -> + atom.activatePackage('package-with-grammars') + expect(syntax.selectGrammar('a.alot').name).toBe 'Alot' + expect(syntax.selectGrammar('a.alittle').name).toBe 'Alittle' + + describe "scoped-property loading", -> + it "loads the scoped properties", -> + atom.activatePackage("package-with-scoped-properties") + expect(syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBe '^a' + + describe "textmate packages", -> + it "loads the package's grammars", -> + expect(syntax.selectGrammar("file.rb").name).toBe "Null Grammar" + atom.activatePackage('ruby.tmbundle', sync: true) + expect(syntax.selectGrammar("file.rb").name).toBe "Ruby" + + it "translates the package's scoped properties to Atom terms", -> + expect(syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBeUndefined() + atom.activatePackage('ruby.tmbundle', sync: true) + expect(syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBe '# ' + + describe ".deactivatePackage(id)", -> + describe "atom packages", -> + it "calls `deactivate` on the package's main module", -> + pack = atom.activatePackage("package-with-deactivate") + expect(atom.isPackageActive("package-with-deactivate")).toBeTruthy() + spyOn(pack.mainModule, 'deactivate').andCallThrough() + + atom.deactivatePackage("package-with-deactivate") + expect(pack.mainModule.deactivate).toHaveBeenCalled() + expect(atom.isPackageActive("package-with-module")).toBeFalsy() + + it "absorbs exceptions that are thrown by the package module's serialize methods", -> + spyOn(console, 'error') + atom.activatePackage('package-with-serialize-error', immediate: true) + atom.activatePackage('package-with-serialization', immediate: true) + atom.deactivatePackages() + expect(atom.packageStates['package-with-serialize-error']).toBeUndefined() + expect(atom.packageStates['package-with-serialization']).toEqual someNumber: 1 + expect(console.error).toHaveBeenCalled() + + it "removes the package's grammars", -> + atom.activatePackage('package-with-grammars') + atom.deactivatePackage('package-with-grammars') + expect(syntax.selectGrammar('a.alot').name).toBe 'Null Grammar' + expect(syntax.selectGrammar('a.alittle').name).toBe 'Null Grammar' + + it "removes the package's keymaps", -> + atom.activatePackage('package-with-keymaps') + atom.deactivatePackage('package-with-keymaps') + expect(keymap.bindingsForElement($$ -> @div class: 'test-1')['ctrl-z']).toBeUndefined() + expect(keymap.bindingsForElement($$ -> @div class: 'test-2')['ctrl-z']).toBeUndefined() + + it "removes the package's stylesheets", -> + atom.activatePackage('package-with-stylesheets') + atom.deactivatePackage('package-with-stylesheets') + one = fs.resolveOnLoadPath("package-with-stylesheets/stylesheets/1.css") + two = fs.resolveOnLoadPath("package-with-stylesheets/stylesheets/2.less") + three = fs.resolveOnLoadPath("package-with-stylesheets/stylesheets/3.css") + expect(stylesheetElementForId(one)).not.toExist() + expect(stylesheetElementForId(two)).not.toExist() + expect(stylesheetElementForId(three)).not.toExist() + + it "removes the package's scoped-properties", -> + atom.activatePackage("package-with-scoped-properties") + expect(syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBe '^a' + atom.deactivatePackage("package-with-scoped-properties") + expect(syntax.getProperty ['.source.omg'], 'editor.increaseIndentPattern').toBeUndefined() + + describe "texmate packages", -> + it "removes the package's grammars", -> + expect(syntax.selectGrammar("file.rb").name).toBe "Null Grammar" + atom.activatePackage('ruby.tmbundle', sync: true) + expect(syntax.selectGrammar("file.rb").name).toBe "Ruby" + atom.deactivatePackage('ruby.tmbundle') + expect(syntax.selectGrammar("file.rb").name).toBe "Null Grammar" + + it "removes the package's scoped properties", -> + atom.activatePackage('ruby.tmbundle', sync: true) + atom.deactivatePackage('ruby.tmbundle') + expect(syntax.getProperty(['.source.ruby'], 'editor.commentStart')).toBeUndefined() describe ".getVersion(callback)", -> it "calls the callback with the current version number", -> diff --git a/spec/app/display-buffer-spec.coffee b/spec/app/display-buffer-spec.coffee index a46f2f63d..8227a94e2 100644 --- a/spec/app/display-buffer-spec.coffee +++ b/spec/app/display-buffer-spec.coffee @@ -6,6 +6,7 @@ describe "DisplayBuffer", -> [editSession, displayBuffer, buffer, changeHandler, tabLength] = [] beforeEach -> tabLength = 2 + atom.activatePackage('javascript.tmbundle', sync: true) editSession = project.buildEditSession('sample.js', { tabLength }) { buffer, displayBuffer } = editSession changeHandler = jasmine.createSpy 'changeHandler' diff --git a/spec/app/edit-session-spec.coffee b/spec/app/edit-session-spec.coffee index 7afe8d888..3337d526b 100644 --- a/spec/app/edit-session-spec.coffee +++ b/spec/app/edit-session-spec.coffee @@ -9,6 +9,7 @@ describe "EditSession", -> buffer.setText(buffer.getText().replace(/[ ]{2}/g, "\t")) beforeEach -> + atom.activatePackage('javascript.tmbundle', sync: true) editSession = project.buildEditSession('sample.js', autoIndent: false) buffer = editSession.buffer lineLengths = buffer.getLines().map (line) -> line.length @@ -395,6 +396,20 @@ describe "EditSession", -> buffer.insert([8, 0], '...') expect(cursorMovedHandler).not.toHaveBeenCalled() + describe "addCursorAtScreenPosition(screenPosition)", -> + describe "when a cursor already exists at the position", -> + it "returns the existing cursor", -> + cursor1 = editSession.addCursorAtScreenPosition([0,2]) + cursor2 = editSession.addCursorAtScreenPosition([0,2]) + expect(cursor2.marker).toBe cursor1.marker + + describe "addCursorAtBufferPosition(bufferPosition)", -> + describe "when a cursor already exists at the position", -> + it "returns the existing cursor", -> + cursor1 = editSession.addCursorAtBufferPosition([1,4]) + cursor2 = editSession.addCursorAtBufferPosition([1,4]) + expect(cursor2.marker).toBe cursor1.marker + describe "selection", -> selection = null @@ -2048,13 +2063,14 @@ describe "EditSession", -> describe "when the 'grammars-loaded' event is triggered on the syntax global", -> it "reloads the edit session's grammar and re-tokenizes the buffer if it changes", -> editSession.destroy() - grammarToReturn = syntax.grammarByPath('a.txt') + jsGrammar = syntax.selectGrammar('a.js') + grammarToReturn = syntax.nullGrammar spyOn(syntax, 'selectGrammar').andCallFake -> grammarToReturn editSession = project.buildEditSession('sample.js', autoIndent: false) expect(editSession.lineForScreenRow(0).tokens.length).toBe 1 - grammarToReturn = syntax.grammarByPath('a.js') + grammarToReturn = jsGrammar syntax.trigger 'grammars-loaded' expect(editSession.lineForScreenRow(0).tokens.length).toBeGreaterThan 1 diff --git a/spec/app/editor-spec.coffee b/spec/app/editor-spec.coffee index a642e3559..885981485 100644 --- a/spec/app/editor-spec.coffee +++ b/spec/app/editor-spec.coffee @@ -12,6 +12,8 @@ describe "Editor", -> [buffer, editor, editSession, cachedLineHeight, cachedCharWidth] = [] beforeEach -> + atom.activatePackage('text.tmbundle', sync: true) + atom.activatePackage('javascript.tmbundle', sync: true) editSession = project.buildEditSession('sample.js') buffer = editSession.buffer editor = new Editor(editSession) @@ -556,6 +558,20 @@ describe "Editor", -> expect(editor.scrollTop()).toBe 0 + it "ignores non left-click and drags", -> + editor.attachToDom() + editor.css(position: 'absolute', top: 10, left: 10) + + event = mousedownEvent(editor: editor, point: [4, 10]) + event.originalEvent.which = 2 + editor.renderedLines.trigger(event) + $(document).trigger mousemoveEvent(editor: editor, point: [5, 27]) + $(document).trigger 'mouseup' + + range = editor.getSelection().getScreenRange() + expect(range.start).toEqual({row: 4, column: 10}) + expect(range.end).toEqual({row: 4, column: 10}) + describe "double-click and drag", -> it "selects the word under the cursor, then continues to select by word in either direction as the mouse is dragged", -> expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0) @@ -975,6 +991,15 @@ describe "Editor", -> editor.setCursorScreenPosition([2, 5]) expect(editor.scrollView.scrollLeft()).toBe 0 + describe "when editor:toggle-soft-wrap is toggled", -> + describe "when the text exceeds the editor width and the scroll-view is horizontally scrolled", -> + it "wraps the text and renders properly", -> + editor.attachToDom(heightInLines: 30, widthInChars: 30) + editor.setText("Fashion axe umami jean shorts retro hashtag carles mumblecore. Photo booth skateboard Austin gentrify occupy ethical. Food truck gastropub keffiyeh, squid deep v pinterest literally sustainable salvia scenester messenger bag. Neutra messenger bag flexitarian four loko, shoreditch VHS pop-up tumblr seitan synth master cleanse. Marfa selvage ugh, raw denim authentic try-hard mcsweeney's trust fund fashion axe actually polaroid viral sriracha. Banh mi marfa plaid single-origin coffee. Pickled mumblecore lomo ugh bespoke.") + editor.scrollView.scrollLeft(editor.charWidth * 30) + editor.trigger "editor:toggle-soft-wrap" + expect(editor.scrollView.scrollLeft()).toBe 0 + describe "text rendering", -> describe "when all lines in the buffer are visible on screen", -> beforeEach -> @@ -1048,90 +1073,6 @@ describe "Editor", -> buffer.insert([1,0], "/*") expect(editor.renderedLines.find('.line:eq(2) > span:first > span:first')).toMatchSelector '.comment' - describe "when soft-wrap is enabled", -> - beforeEach -> - setEditorHeightInLines(editor, 20) - setEditorWidthInChars(editor, 50) - editor.setSoftWrap(true) - expect(editor.activeEditSession.softWrapColumn).toBe 50 - - it "wraps lines that are too long to fit within the editor's width, adjusting cursor positioning accordingly", -> - expect(editor.renderedLines.find('.line').length).toBe 16 - expect(editor.renderedLines.find('.line:eq(3)').text()).toBe " var pivot = items.shift(), current, left = [], " - expect(editor.renderedLines.find('.line:eq(4)').text()).toBe "right = [];" - - editor.setCursorBufferPosition([3, 51], wrapAtSoftNewlines: true) - expect(editor.find('.cursor').offset()).toEqual(editor.renderedLines.find('.line:eq(4)').offset()) - - editor.setCursorBufferPosition([4, 0]) - expect(editor.find('.cursor').offset()).toEqual(editor.renderedLines.find('.line:eq(5)').offset()) - - editor.getSelection().setBufferRange(new Range([6, 30], [6, 55])) - [region1, region2] = editor.getSelectionView().regions - expect(region1.offset().top).toBeCloseTo(editor.renderedLines.find('.line:eq(7)').offset().top) - expect(region2.offset().top).toBeCloseTo(editor.renderedLines.find('.line:eq(8)').offset().top) - - it "handles changes to wrapped lines correctly", -> - buffer.insert([6, 28], '1234567') - expect(editor.renderedLines.find('.line:eq(7)').text()).toBe ' current < pivot ? left1234567.push(current) ' - expect(editor.renderedLines.find('.line:eq(8)').text()).toBe ': right.push(current);' - expect(editor.renderedLines.find('.line:eq(9)').text()).toBe ' }' - - it "changes the max line length and repositions the cursor when the window size changes", -> - editor.setCursorBufferPosition([3, 60]) - setEditorWidthInChars(editor, 40) - expect(editor.renderedLines.find('.line').length).toBe 19 - expect(editor.renderedLines.find('.line:eq(4)').text()).toBe "left = [], right = [];" - expect(editor.renderedLines.find('.line:eq(5)').text()).toBe " while(items.length > 0) {" - expect(editor.bufferPositionForScreenPosition(editor.getCursorScreenPosition())).toEqual [3, 60] - - it "does not wrap the lines of any newly assigned buffers", -> - otherEditSession = project.buildEditSession() - otherEditSession.buffer.setText([1..100].join('')) - editor.edit(otherEditSession) - expect(editor.renderedLines.find('.line').length).toBe(1) - - it "unwraps lines and cancels window resize listener when softwrap is disabled", -> - editor.toggleSoftWrap() - expect(editor.renderedLines.find('.line:eq(3)').text()).toBe ' var pivot = items.shift(), current, left = [], right = [];' - - spyOn(editor, 'setSoftWrapColumn') - $(window).trigger 'resize' - expect(editor.setSoftWrapColumn).not.toHaveBeenCalled() - - it "allows the cursor to move down to the last line", -> - _.times editor.getLastScreenRow(), -> editor.moveCursorDown() - expect(editor.getCursorScreenPosition()).toEqual [editor.getLastScreenRow(), 0] - editor.moveCursorDown() - expect(editor.getCursorScreenPosition()).toEqual [editor.getLastScreenRow(), 2] - - it "allows the cursor to move up to a shorter soft wrapped line", -> - editor.setCursorScreenPosition([11, 15]) - editor.moveCursorUp() - expect(editor.getCursorScreenPosition()).toEqual [10, 10] - editor.moveCursorUp() - editor.moveCursorUp() - expect(editor.getCursorScreenPosition()).toEqual [8, 15] - - it "it allows the cursor to wrap when moving horizontally past the beginning / end of a wrapped line", -> - editor.setCursorScreenPosition([11, 0]) - editor.moveCursorLeft() - expect(editor.getCursorScreenPosition()).toEqual [10, 10] - - editor.moveCursorRight() - expect(editor.getCursorScreenPosition()).toEqual [11, 0] - - it "calls .setSoftWrapColumn() when the editor is attached because now its dimensions are available to calculate it", -> - otherEditor = new Editor(editSession: project.buildEditSession('sample.js')) - spyOn(otherEditor, 'setSoftWrapColumn') - - otherEditor.setSoftWrap(true) - expect(otherEditor.setSoftWrapColumn).not.toHaveBeenCalled() - - otherEditor.simulateDomAttachment() - expect(otherEditor.setSoftWrapColumn).toHaveBeenCalled() - otherEditor.remove() - describe "when some lines at the end of the buffer are not visible on screen", -> beforeEach -> editor.attachToDom(heightInLines: 5.5) @@ -1580,6 +1521,91 @@ describe "Editor", -> expect(editor.renderedLines.find('.line:eq(10) .indent-guide').length).toBe 2 expect(editor.renderedLines.find('.line:eq(10) .indent-guide').text()).toBe ' ' + describe "when soft-wrap is enabled", -> + beforeEach -> + editor.attachToDom() + setEditorHeightInLines(editor, 20) + setEditorWidthInChars(editor, 50) + editor.setSoftWrap(true) + expect(editor.activeEditSession.softWrapColumn).toBe 50 + + it "wraps lines that are too long to fit within the editor's width, adjusting cursor positioning accordingly", -> + expect(editor.renderedLines.find('.line').length).toBe 16 + expect(editor.renderedLines.find('.line:eq(3)').text()).toBe " var pivot = items.shift(), current, left = [], " + expect(editor.renderedLines.find('.line:eq(4)').text()).toBe "right = [];" + + editor.setCursorBufferPosition([3, 51], wrapAtSoftNewlines: true) + expect(editor.find('.cursor').offset()).toEqual(editor.renderedLines.find('.line:eq(4)').offset()) + + editor.setCursorBufferPosition([4, 0]) + expect(editor.find('.cursor').offset()).toEqual(editor.renderedLines.find('.line:eq(5)').offset()) + + editor.getSelection().setBufferRange(new Range([6, 30], [6, 55])) + [region1, region2] = editor.getSelectionView().regions + expect(region1.offset().top).toBeCloseTo(editor.renderedLines.find('.line:eq(7)').offset().top) + expect(region2.offset().top).toBeCloseTo(editor.renderedLines.find('.line:eq(8)').offset().top) + + it "handles changes to wrapped lines correctly", -> + buffer.insert([6, 28], '1234567') + expect(editor.renderedLines.find('.line:eq(7)').text()).toBe ' current < pivot ? left1234567.push(current) ' + expect(editor.renderedLines.find('.line:eq(8)').text()).toBe ': right.push(current);' + expect(editor.renderedLines.find('.line:eq(9)').text()).toBe ' }' + + it "changes the max line length and repositions the cursor when the window size changes", -> + editor.setCursorBufferPosition([3, 60]) + setEditorWidthInChars(editor, 40) + expect(editor.renderedLines.find('.line').length).toBe 19 + expect(editor.renderedLines.find('.line:eq(4)').text()).toBe "left = [], right = [];" + expect(editor.renderedLines.find('.line:eq(5)').text()).toBe " while(items.length > 0) {" + expect(editor.bufferPositionForScreenPosition(editor.getCursorScreenPosition())).toEqual [3, 60] + + it "does not wrap the lines of any newly assigned buffers", -> + otherEditSession = project.buildEditSession() + otherEditSession.buffer.setText([1..100].join('')) + editor.edit(otherEditSession) + expect(editor.renderedLines.find('.line').length).toBe(1) + + it "unwraps lines and cancels window resize listener when softwrap is disabled", -> + editor.toggleSoftWrap() + expect(editor.renderedLines.find('.line:eq(3)').text()).toBe ' var pivot = items.shift(), current, left = [], right = [];' + + spyOn(editor, 'setSoftWrapColumn') + $(window).trigger 'resize' + expect(editor.setSoftWrapColumn).not.toHaveBeenCalled() + + it "allows the cursor to move down to the last line", -> + _.times editor.getLastScreenRow(), -> editor.moveCursorDown() + expect(editor.getCursorScreenPosition()).toEqual [editor.getLastScreenRow(), 0] + editor.moveCursorDown() + expect(editor.getCursorScreenPosition()).toEqual [editor.getLastScreenRow(), 2] + + it "allows the cursor to move up to a shorter soft wrapped line", -> + editor.setCursorScreenPosition([11, 15]) + editor.moveCursorUp() + expect(editor.getCursorScreenPosition()).toEqual [10, 10] + editor.moveCursorUp() + editor.moveCursorUp() + expect(editor.getCursorScreenPosition()).toEqual [8, 15] + + it "it allows the cursor to wrap when moving horizontally past the beginning / end of a wrapped line", -> + editor.setCursorScreenPosition([11, 0]) + editor.moveCursorLeft() + expect(editor.getCursorScreenPosition()).toEqual [10, 10] + + editor.moveCursorRight() + expect(editor.getCursorScreenPosition()).toEqual [11, 0] + + it "calls .setSoftWrapColumn() when the editor is attached because now its dimensions are available to calculate it", -> + otherEditor = new Editor(editSession: project.buildEditSession('sample.js')) + spyOn(otherEditor, 'setSoftWrapColumn') + + otherEditor.setSoftWrap(true) + expect(otherEditor.setSoftWrapColumn).not.toHaveBeenCalled() + + otherEditor.simulateDomAttachment() + expect(otherEditor.setSoftWrapColumn).toHaveBeenCalled() + otherEditor.remove() + describe "gutter rendering", -> beforeEach -> editor.attachToDom(heightInLines: 5.5) @@ -1707,6 +1733,18 @@ describe "Editor", -> miniEditor.setText(" a line with tabs\tand spaces ") expect(miniEditor.renderedLines.find('.line').text()).toBe "#{space}a line with tabs#{tab} and spaces#{space}" + it "lets you set the grammar", -> + miniEditor = new Editor(mini: true) + miniEditor.setText("var something") + previousTokens = miniEditor.lineForScreenRow(0).tokens + miniEditor.setGrammar(syntax.selectGrammar('something.js')) + expect(miniEditor.getGrammar().name).toBe "JavaScript" + expect(previousTokens).not.toEqual miniEditor.lineForScreenRow(0).tokens + + # doesn't allow regular editors to set grammars + expect(-> editor.setGrammar()).toThrow() + + describe "when config.editor.showLineNumbers is false", -> it "doesn't render any line numbers", -> expect(editor.gutter.lineNumbers).toBeVisible() diff --git a/spec/app/keymap-spec.coffee b/spec/app/keymap-spec.coffee index b813ad50c..2e6e7a534 100644 --- a/spec/app/keymap-spec.coffee +++ b/spec/app/keymap-spec.coffee @@ -1,5 +1,6 @@ Keymap = require 'keymap' $ = require 'jquery' +{$$} = require 'space-pen' RootView = require 'root-view' describe "Keymap", -> @@ -234,7 +235,7 @@ describe "Keymap", -> it "returns false to prevent the browser from transferring focus", -> expect(keymap.handleKeyEvent(keydownEvent('U+0009', target: fragment[0]))).toBe false - describe ".bindKeys(selector, hash)", -> + describe ".bindKeys(selector, bindings)", -> it "normalizes the key patterns in the hash to put the modifiers in alphabetical order", -> fooHandler = jasmine.createSpy('fooHandler') fragment.on 'foo', fooHandler @@ -249,6 +250,24 @@ describe "Keymap", -> expect(result).toBe(false) expect(fooHandler).toHaveBeenCalled() + describe ".remove(name)", -> + it "removes the binding set with the given selector and bindings", -> + keymap.add 'nature', + '.green': + 'ctrl-c': 'cultivate' + '.brown': + 'ctrl-h': 'harvest' + + expect(keymap.bindingsForElement($$ -> @div class: 'green')).toEqual { 'ctrl-c': 'cultivate' } + expect(keymap.bindingsForElement($$ -> @div class: 'brown')).toEqual { 'ctrl-h': 'harvest' } + + keymap.remove('nature') + + expect(keymap.bindingsForElement($$ -> @div class: 'green')).toEqual {} + expect(keymap.bindingsForElement($$ -> @div class: 'brown')).toEqual {} + expect(keymap.bindingSetsByFirstKeystroke['ctrl-c']).toEqual [] + expect(keymap.bindingSetsByFirstKeystroke['ctrl-h']).toEqual [] + describe ".keystrokeStringForEvent(event)", -> describe "when no modifiers are pressed", -> it "returns a string that identifies the key pressed", -> diff --git a/spec/app/language-mode-spec.coffee b/spec/app/language-mode-spec.coffee index 715296e6b..4244d3c72 100644 --- a/spec/app/language-mode-spec.coffee +++ b/spec/app/language-mode-spec.coffee @@ -10,6 +10,7 @@ describe "LanguageMode", -> describe "common behavior", -> beforeEach -> + atom.activatePackage('javascript.tmbundle', sync: true) editSession = project.buildEditSession('sample.js', autoIndent: false) { buffer, languageMode } = editSession @@ -21,6 +22,7 @@ describe "LanguageMode", -> describe "javascript", -> beforeEach -> + atom.activatePackage('javascript.tmbundle', sync: true) editSession = project.buildEditSession('sample.js', autoIndent: false) { buffer, languageMode } = editSession @@ -60,9 +62,9 @@ describe "LanguageMode", -> expect(languageMode.suggestedIndentForBufferRow(2)).toBe 2 expect(languageMode.suggestedIndentForBufferRow(9)).toBe 1 - describe "coffeescript", -> beforeEach -> + atom.activatePackage('coffee-script-tmbundle', sync: true) editSession = project.buildEditSession('coffee.coffee', autoIndent: false) { buffer, languageMode } = editSession @@ -98,6 +100,7 @@ describe "LanguageMode", -> describe "css", -> beforeEach -> + atom.activatePackage('css.tmbundle', sync: true) editSession = project.buildEditSession('css.css', autoIndent: false) { buffer, languageMode } = editSession diff --git a/spec/app/root-view-spec.coffee b/spec/app/root-view-spec.coffee index b1f28e9b5..4935dbd1b 100644 --- a/spec/app/root-view-spec.coffee +++ b/spec/app/root-view-spec.coffee @@ -29,7 +29,7 @@ describe "RootView", -> buffer = editor1.getBuffer() editor1.splitRight() viewState = rootView.serialize() - rootView.deactivate() + rootView.remove() window.rootView = deserialize(viewState) rootView.attachToDom() @@ -54,7 +54,7 @@ describe "RootView", -> pane2.focus() viewState = rootView.serialize() - rootView.deactivate() + rootView.remove() window.rootView = deserialize(viewState) rootView.attachToDom() @@ -91,7 +91,7 @@ describe "RootView", -> expect(rootView.getEditors().length).toBe 0 viewState = rootView.serialize() - rootView.deactivate() + rootView.remove() window.rootView = deserialize(viewState) rootView.attachToDom() diff --git a/spec/app/syntax-spec.coffee b/spec/app/syntax-spec.coffee index 789e44159..ad12bee71 100644 --- a/spec/app/syntax-spec.coffee +++ b/spec/app/syntax-spec.coffee @@ -1,6 +1,12 @@ fs = require 'fs-utils' describe "the `syntax` global", -> + beforeEach -> + atom.activatePackage('text.tmbundle', sync: true) + atom.activatePackage('javascript.tmbundle', sync: true) + atom.activatePackage('coffee-script-tmbundle', sync: true) + atom.activatePackage('ruby.tmbundle', sync: true) + describe "serialization", -> it "remembers grammar overrides by path", -> path = '/foo/bar/file.js' @@ -12,6 +18,8 @@ describe "the `syntax` global", -> describe ".selectGrammar(filePath)", -> it "can use the filePath to load the correct grammar based on the grammar's filetype", -> + atom.activatePackage('git.tmbundle', sync: true) + expect(syntax.selectGrammar("file.js").name).toBe "JavaScript" # based on extension (.js) expect(syntax.selectGrammar("/tmp/.git/config").name).toBe "Git Config" # based on end of the path (.git/config) expect(syntax.selectGrammar("Rakefile").name).toBe "Ruby" # based on the file's basename (Rakefile) @@ -23,6 +31,8 @@ describe "the `syntax` global", -> expect(syntax.selectGrammar(filePath).name).toBe "Ruby" it "uses the number of newlines in the first line regex to determine the number of lines to test against", -> + atom.activatePackage('property-list.tmbundle', sync: true) + fileContent = "first-line\n" expect(syntax.selectGrammar("dummy.coffee", fileContent).name).toBe "CoffeeScript" @@ -47,17 +57,22 @@ describe "the `syntax` global", -> syntax.clearGrammarOverrideForPath(path) expect(syntax.selectGrammar(path).name).not.toBe 'Ruby' + describe ".removeGrammar(grammar)", -> + it "removes the grammar, so it won't be returned by selectGrammar", -> + grammar = syntax.selectGrammar('foo.js') + syntax.removeGrammar(grammar) + expect(syntax.selectGrammar('foo.js').name).not.toBe grammar.name + describe ".getProperty(scopeDescriptor)", -> it "returns the property with the most specific scope selector", -> syntax.addProperties(".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42) syntax.addProperties(".source .string.quoted.double", foo: bar: baz: 22) syntax.addProperties(".source", foo: bar: baz: 11) - syntax.addProperties(foo: bar: baz: 1) expect(syntax.getProperty([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42 expect(syntax.getProperty([".source.js", ".string.quoted.double.js"], "foo.bar.baz")).toBe 22 expect(syntax.getProperty([".source.js", ".variable.assignment.js"], "foo.bar.baz")).toBe 11 - expect(syntax.getProperty([".text"], "foo.bar.baz")).toBe 1 + expect(syntax.getProperty([".text"], "foo.bar.baz")).toBeUndefined() it "favors the most recently added properties in the event of a specificity tie", -> syntax.addProperties(".source.coffee .string.quoted.single", foo: bar: baz: 42) @@ -65,3 +80,12 @@ describe "the `syntax` global", -> expect(syntax.getProperty([".source.coffee", ".string.quoted.single"], "foo.bar.baz")).toBe 42 expect(syntax.getProperty([".source.coffee", ".string.quoted.single.double"], "foo.bar.baz")).toBe 22 + + describe ".removeProperties(name)", -> + it "allows properties to be removed by name", -> + syntax.addProperties("a", ".source.coffee .string.quoted.double.coffee", foo: bar: baz: 42) + syntax.addProperties("b", ".source .string.quoted.double", foo: bar: baz: 22) + + syntax.removeProperties("b") + expect(syntax.getProperty([".source.js", ".string.quoted.double.js"], "foo.bar.baz")).toBeUndefined() + expect(syntax.getProperty([".source.coffee", ".string.quoted.double.coffee"], "foo.bar.baz")).toBe 42 diff --git a/spec/app/text-mate-grammar-spec.coffee b/spec/app/text-mate-grammar-spec.coffee index 80d2a8df1..276a96496 100644 --- a/spec/app/text-mate-grammar-spec.coffee +++ b/spec/app/text-mate-grammar-spec.coffee @@ -8,8 +8,25 @@ describe "TextMateGrammar", -> grammar = null beforeEach -> + atom.activatePackage('text.tmbundle', sync: true) + atom.activatePackage('javascript.tmbundle', sync: true) + atom.activatePackage('coffee-script-tmbundle', sync: true) + atom.activatePackage('ruby.tmbundle', sync: true) grammar = syntax.selectGrammar("hello.coffee") + describe "@loadSync(path)", -> + it "loads grammars from plists", -> + grammar = TextMateGrammar.loadSync(fs.resolveOnLoadPath('packages/text.tmbundle/Syntaxes/Plain text.plist')) + expect(grammar.scopeName).toBe "text.plain" + {tokens} = grammar.tokenizeLine("this text is so plain. i love it.") + expect(tokens[0]).toEqual value: "this text is so plain. i love it.", scopes: ["text.plain", "meta.paragraph.text"] + + it "loads grammars from cson files", -> + grammar = TextMateGrammar.loadSync(fs.resolveOnLoadPath('package-with-grammars/grammars/alot.cson')) + expect(grammar.scopeName).toBe "source.alot" + {tokens} = grammar.tokenizeLine("this is alot of code") + expect(tokens[1]).toEqual value: "alot", scopes: ["source.alot", "keyword.alot"] + describe ".tokenizeLine(line, ruleStack)", -> describe "when the entire line matches a single pattern with no capture groups", -> it "returns a single token with the correct scope", -> @@ -192,6 +209,9 @@ describe "TextMateGrammar", -> describe "when the pattern includes rules from another grammar", -> it "parses tokens inside the begin/end patterns based on the included grammar's rules", -> + atom.activatePackage('html.tmbundle', sync: true) + atom.activatePackage('ruby-on-rails-tmbundle', sync: true) + grammar = syntax.grammarsByFileType["html.erb"] {tokens} = grammar.tokenizeLine("
<%= User.find(2).full_name %>
") @@ -248,6 +268,9 @@ describe "TextMateGrammar", -> {tokens, ruleStack} = grammar.tokenizeLine(" // second line comment with a single leading space", ruleStack) describe "when inside a C block", -> + beforeEach -> + atom.activatePackage('c.tmbundle', sync: true) + it "correctly parses a method. (regression)", -> grammar = syntax.selectGrammar("hello.c") {tokens, ruleStack} = grammar.tokenizeLine("if(1){m()}") @@ -262,7 +285,7 @@ describe "TextMateGrammar", -> describe "when the grammar can infinitely loop over a line", -> it "aborts tokenization", -> spyOn(console, 'error') - window.loadPackage("package-with-infinite-loop-grammar") + atom.activatePackage("package-with-infinite-loop-grammar") grammar = syntax.selectGrammar("something.package-with-infinite-loop-grammar") {tokens} = grammar.tokenizeLine("abc") expect(tokens[0].value).toBe "a" @@ -271,6 +294,7 @@ describe "TextMateGrammar", -> describe "when a grammar has a pattern that has back references in the match value", -> it "does not special handle the back references and instead allows oniguruma to resolve them", -> + atom.activatePackage('sass.tmbundle', sync: true) grammar = syntax.selectGrammar("style.scss") {tokens} = grammar.tokenizeLine("@mixin x() { -moz-selector: whatever; }") expect(tokens[9]).toEqual value: "-moz-selector", scopes: ["source.css.scss", "meta.property-list.scss", "meta.property-name.scss"] diff --git a/spec/app/tokenized-buffer-spec.coffee b/spec/app/tokenized-buffer-spec.coffee index 378534b37..4f7ad41ce 100644 --- a/spec/app/tokenized-buffer-spec.coffee +++ b/spec/app/tokenized-buffer-spec.coffee @@ -8,6 +8,7 @@ describe "TokenizedBuffer", -> [editSession, tokenizedBuffer, buffer, changeHandler] = [] beforeEach -> + atom.activatePackage('javascript.tmbundle', sync: true) # enable async tokenization TokenizedBuffer.prototype.chunkSize = 5 jasmine.unspy(TokenizedBuffer.prototype, 'tokenizeInBackground') @@ -298,6 +299,7 @@ describe "TokenizedBuffer", -> describe "when the buffer contains hard-tabs", -> beforeEach -> + atom.activatePackage('coffee-script-tmbundle', sync: true) tabLength = 2 editSession = project.buildEditSession('sample-with-tabs.coffee', { tabLength }) buffer = editSession.buffer @@ -328,6 +330,7 @@ describe "TokenizedBuffer", -> describe "when a Git commit message file is tokenized", -> beforeEach -> + atom.activatePackage('git.tmbundle', sync: true) editSession = project.buildEditSession('COMMIT_EDITMSG', autoIndent: false) buffer = editSession.buffer tokenizedBuffer = editSession.displayBuffer.tokenizedBuffer @@ -355,6 +358,7 @@ describe "TokenizedBuffer", -> describe "when a C++ source file is tokenized", -> beforeEach -> + atom.activatePackage('c.tmbundle', sync: true) editSession = project.buildEditSession('includes.cc', autoIndent: false) buffer = editSession.buffer tokenizedBuffer = editSession.displayBuffer.tokenizedBuffer @@ -403,6 +407,8 @@ describe "TokenizedBuffer", -> describe "when an Objective-C source file is tokenized", -> beforeEach -> + atom.activatePackage('c.tmbundle', sync: true) + atom.activatePackage('objective-c.tmbundle', sync: true) editSession = project.buildEditSession('function.mm', autoIndent: false) buffer = editSession.buffer tokenizedBuffer = editSession.displayBuffer.tokenizedBuffer diff --git a/spec/fixtures/packages/package-with-activation-events/main.coffee b/spec/fixtures/packages/package-with-activation-events/index.coffee similarity index 86% rename from spec/fixtures/packages/package-with-activation-events/main.coffee rename to spec/fixtures/packages/package-with-activation-events/index.coffee index d57ca7c24..91f2230b1 100644 --- a/spec/fixtures/packages/package-with-activation-events/main.coffee +++ b/spec/fixtures/packages/package-with-activation-events/index.coffee @@ -10,7 +10,5 @@ module.exports = activate: -> @activateCallCount++ rootView.getActiveView()?.command 'activation-event', => + console.log "ACTIVATION EVENT" @activationEventCallCount++ - - serialize: -> - previousData: 'overwritten' diff --git a/spec/fixtures/packages/package-with-activation-events/package.cson b/spec/fixtures/packages/package-with-activation-events/package.cson index 42d3eb78d..45d8dfea1 100644 --- a/spec/fixtures/packages/package-with-activation-events/package.cson +++ b/spec/fixtures/packages/package-with-activation-events/package.cson @@ -1,3 +1,2 @@ 'activationEvents': ['activation-event'] 'deferredDeserializers': ['Foo'] -'main': 'main' diff --git a/spec/fixtures/packages/textmate-package.tmbundle/Snippets/.hidden-file b/spec/fixtures/packages/package-with-broken-snippets.tmbundle/Snippets/.hidden-file similarity index 100% rename from spec/fixtures/packages/textmate-package.tmbundle/Snippets/.hidden-file rename to spec/fixtures/packages/package-with-broken-snippets.tmbundle/Snippets/.hidden-file diff --git a/spec/fixtures/packages/textmate-package.tmbundle/Snippets/invalid.plist b/spec/fixtures/packages/package-with-broken-snippets.tmbundle/Snippets/invalid.plist similarity index 100% rename from spec/fixtures/packages/textmate-package.tmbundle/Snippets/invalid.plist rename to spec/fixtures/packages/package-with-broken-snippets.tmbundle/Snippets/invalid.plist diff --git a/spec/fixtures/packages/package-with-config-defaults/index.coffee b/spec/fixtures/packages/package-with-config-defaults/index.coffee new file mode 100644 index 000000000..5e0b1eed6 --- /dev/null +++ b/spec/fixtures/packages/package-with-config-defaults/index.coffee @@ -0,0 +1,3 @@ +module.exports = + configDefaults: + numbers: { one: 1, two: 2 } diff --git a/spec/fixtures/packages/package-with-deactivate/index.coffee b/spec/fixtures/packages/package-with-deactivate/index.coffee new file mode 100644 index 000000000..65517bacd --- /dev/null +++ b/spec/fixtures/packages/package-with-deactivate/index.coffee @@ -0,0 +1,3 @@ +module.exports = + activate: -> + deactivate: -> diff --git a/spec/fixtures/packages/package-with-grammars/grammars/alittle.cson b/spec/fixtures/packages/package-with-grammars/grammars/alittle.cson new file mode 100644 index 000000000..c72bea137 --- /dev/null +++ b/spec/fixtures/packages/package-with-grammars/grammars/alittle.cson @@ -0,0 +1,12 @@ +'fileTypes': ['alittle'] +'name': 'Alittle' +'scopeName': 'source.alittle' + +'patterns': [ + { + 'captures': + '0': + 'name': 'keyword.alittle' + 'match': 'alittle' + } +] diff --git a/spec/fixtures/packages/package-with-a-cson-grammar/grammars/alot.cson b/spec/fixtures/packages/package-with-grammars/grammars/alot.cson similarity index 100% rename from spec/fixtures/packages/package-with-a-cson-grammar/grammars/alot.cson rename to spec/fixtures/packages/package-with-grammars/grammars/alot.cson diff --git a/spec/fixtures/packages/package-with-index/index.coffee b/spec/fixtures/packages/package-with-index/index.coffee new file mode 100644 index 000000000..825eb4055 --- /dev/null +++ b/spec/fixtures/packages/package-with-index/index.coffee @@ -0,0 +1,2 @@ +module.exports = +activate: -> diff --git a/spec/fixtures/packages/package-with-module/keymaps/keymap-1.cson b/spec/fixtures/packages/package-with-keymaps/keymaps/keymap-1.cson similarity index 100% rename from spec/fixtures/packages/package-with-module/keymaps/keymap-1.cson rename to spec/fixtures/packages/package-with-keymaps/keymaps/keymap-1.cson diff --git a/spec/fixtures/packages/package-with-module/keymaps/keymap-2.cson b/spec/fixtures/packages/package-with-keymaps/keymaps/keymap-2.cson similarity index 100% rename from spec/fixtures/packages/package-with-module/keymaps/keymap-2.cson rename to spec/fixtures/packages/package-with-keymaps/keymaps/keymap-2.cson diff --git a/spec/fixtures/packages/package-with-module/keymaps/keymap-3.cjson b/spec/fixtures/packages/package-with-keymaps/keymaps/keymap-3.cjson similarity index 100% rename from spec/fixtures/packages/package-with-module/keymaps/keymap-3.cjson rename to spec/fixtures/packages/package-with-keymaps/keymaps/keymap-3.cjson diff --git a/spec/fixtures/packages/package-with-main/main-module.coffee b/spec/fixtures/packages/package-with-main/main-module.coffee new file mode 100644 index 000000000..46920ab78 --- /dev/null +++ b/spec/fixtures/packages/package-with-main/main-module.coffee @@ -0,0 +1,2 @@ +module.exports = +activate: -> \ No newline at end of file diff --git a/spec/fixtures/packages/package-with-main/package.cson b/spec/fixtures/packages/package-with-main/package.cson new file mode 100644 index 000000000..a93a109c4 --- /dev/null +++ b/spec/fixtures/packages/package-with-main/package.cson @@ -0,0 +1 @@ +'main': 'main-module.coffee' \ No newline at end of file diff --git a/spec/fixtures/packages/package-with-module/index.coffee b/spec/fixtures/packages/package-with-module/index.coffee deleted file mode 100644 index 84d5c12a1..000000000 --- a/spec/fixtures/packages/package-with-module/index.coffee +++ /dev/null @@ -1,13 +0,0 @@ -module.exports = - configDefaults: - numbers: { one: 1, two: 2 } - - someNumber: 0 - - activate: -> - @someNumber = 1 - - deactivate: -> - - serialize: -> - {@someNumber} diff --git a/spec/fixtures/packages/package-with-module/stylesheets/styles.css b/spec/fixtures/packages/package-with-module/stylesheets/styles.css deleted file mode 100644 index e69de29bb..000000000 diff --git a/spec/fixtures/packages/package-with-serialization/index.coffee b/spec/fixtures/packages/package-with-serialization/index.coffee new file mode 100644 index 000000000..54949113c --- /dev/null +++ b/spec/fixtures/packages/package-with-serialization/index.coffee @@ -0,0 +1,6 @@ +module.exports = + activate: ({@someNumber}) -> + @someNumber ?= 1 + + serialize: -> + {@someNumber} diff --git a/spec/fixtures/packages/package-with-stylesheets-manifest/package.cson b/spec/fixtures/packages/package-with-stylesheets-manifest/package.cson new file mode 100644 index 000000000..45ccf32bc --- /dev/null +++ b/spec/fixtures/packages/package-with-stylesheets-manifest/package.cson @@ -0,0 +1 @@ +stylesheets: ['2', '1'] diff --git a/spec/fixtures/packages/package-with-stylesheets-manifest/stylesheets/1.css b/spec/fixtures/packages/package-with-stylesheets-manifest/stylesheets/1.css new file mode 100644 index 000000000..c16849c0e --- /dev/null +++ b/spec/fixtures/packages/package-with-stylesheets-manifest/stylesheets/1.css @@ -0,0 +1,3 @@ +#jasmine-content { + font-size: 1px; +} diff --git a/spec/fixtures/packages/package-with-stylesheets-manifest/stylesheets/2.less b/spec/fixtures/packages/package-with-stylesheets-manifest/stylesheets/2.less new file mode 100644 index 000000000..c0ffbc897 --- /dev/null +++ b/spec/fixtures/packages/package-with-stylesheets-manifest/stylesheets/2.less @@ -0,0 +1,5 @@ +@size: 2px; + +#jasmine-content { + font-size: @size; +} diff --git a/spec/fixtures/packages/package-with-stylesheets-manifest/stylesheets/3.css b/spec/fixtures/packages/package-with-stylesheets-manifest/stylesheets/3.css new file mode 100644 index 000000000..f43408c63 --- /dev/null +++ b/spec/fixtures/packages/package-with-stylesheets-manifest/stylesheets/3.css @@ -0,0 +1,3 @@ +#jasmine-content { + font-size: 3px; +} diff --git a/spec/fixtures/packages/package-with-stylesheets/stylesheets/1.css b/spec/fixtures/packages/package-with-stylesheets/stylesheets/1.css new file mode 100644 index 000000000..c16849c0e --- /dev/null +++ b/spec/fixtures/packages/package-with-stylesheets/stylesheets/1.css @@ -0,0 +1,3 @@ +#jasmine-content { + font-size: 1px; +} diff --git a/spec/fixtures/packages/package-with-stylesheets/stylesheets/2.less b/spec/fixtures/packages/package-with-stylesheets/stylesheets/2.less new file mode 100644 index 000000000..c0ffbc897 --- /dev/null +++ b/spec/fixtures/packages/package-with-stylesheets/stylesheets/2.less @@ -0,0 +1,5 @@ +@size: 2px; + +#jasmine-content { + font-size: @size; +} diff --git a/spec/fixtures/packages/package-with-stylesheets/stylesheets/3.css b/spec/fixtures/packages/package-with-stylesheets/stylesheets/3.css new file mode 100644 index 000000000..f43408c63 --- /dev/null +++ b/spec/fixtures/packages/package-with-stylesheets/stylesheets/3.css @@ -0,0 +1,3 @@ +#jasmine-content { + font-size: 3px; +} diff --git a/spec/fixtures/packages/package-without-module/package.cson b/spec/fixtures/packages/package-without-module/package.cson new file mode 100644 index 000000000..c6e451934 --- /dev/null +++ b/spec/fixtures/packages/package-without-module/package.cson @@ -0,0 +1 @@ +"name": "perfect" diff --git a/spec/fixtures/test.tmbundle/Preferences/Disable Indention Correction.tmPreferences b/spec/fixtures/test.tmbundle/Preferences/Disable Indention Correction.tmPreferences deleted file mode 100644 index 63c91ab3f..000000000 --- a/spec/fixtures/test.tmbundle/Preferences/Disable Indention Correction.tmPreferences +++ /dev/null @@ -1,19 +0,0 @@ - - - - - name - Disable Indention Correction - scope - text - text source - settings - - disableIndentCorrections - 1 - indentOnPaste - simple - - uuid - 29B976F9-853F-43FF-AA50-7A6A518EDC81 - - diff --git a/spec/fixtures/test.tmbundle/Preferences/Markup style: Bold.plist b/spec/fixtures/test.tmbundle/Preferences/Markup style: Bold.plist deleted file mode 100644 index 5a63bd8b7..000000000 --- a/spec/fixtures/test.tmbundle/Preferences/Markup style: Bold.plist +++ /dev/null @@ -1,17 +0,0 @@ - - - - - name - Style: Bold - scope - markup.bold - settings - - bold - 1 - - uuid - A088E2CA-03E7-4A8C-855C-AC954E739D6D - - diff --git a/spec/fixtures/test.tmbundle/Preferences/Markup style: Italic.plist b/spec/fixtures/test.tmbundle/Preferences/Markup style: Italic.plist deleted file mode 100644 index 9ea3216d3..000000000 --- a/spec/fixtures/test.tmbundle/Preferences/Markup style: Italic.plist +++ /dev/null @@ -1,17 +0,0 @@ - - - - - name - Style: Italic - scope - markup.italic - settings - - italic - 1 - - uuid - 2EE6D01F-50BC-434B-BE45-54D29CACB3E0 - - diff --git a/spec/fixtures/test.tmbundle/Preferences/Markup style: Underline.plist b/spec/fixtures/test.tmbundle/Preferences/Markup style: Underline.plist deleted file mode 100644 index 11a01ca40..000000000 --- a/spec/fixtures/test.tmbundle/Preferences/Markup style: Underline.plist +++ /dev/null @@ -1,17 +0,0 @@ - - - - - name - Style: Underline - scope - markup.underline - settings - - underline - 1 - - uuid - 4CCD8369-40E4-422A-ABE7-C32F3721AFEF - - diff --git a/spec/fixtures/test.tmbundle/Preferences/Miscellaneous.plist b/spec/fixtures/test.tmbundle/Preferences/Miscellaneous.plist deleted file mode 100644 index 04ffaf9d8..000000000 --- a/spec/fixtures/test.tmbundle/Preferences/Miscellaneous.plist +++ /dev/null @@ -1,65 +0,0 @@ - - - - - name - Miscellaneous - settings - - highlightPairs - - - ( - ) - - - { - } - - - [ - ] - - - - - - - - - - - smartTypingPairs - - - " - " - - - ( - ) - - - { - } - - - [ - ] - - - - - - - - - - - unIndentedLinePattern - ^\s*$ - - uuid - 3E8C2307-8175-4A58-BE07-785713D5837A - - diff --git a/spec/fixtures/test.tmbundle/Preferences/Spell checking.plist b/spec/fixtures/test.tmbundle/Preferences/Spell checking.plist deleted file mode 100644 index 3ef7e991a..000000000 --- a/spec/fixtures/test.tmbundle/Preferences/Spell checking.plist +++ /dev/null @@ -1,17 +0,0 @@ - - - - - name - Spell Checking: Disable - scope - markup.underline.link, markup.raw - settings - - spellChecking - 0 - - uuid - 613405D5-67B6-4281-94C9-9148E54C66FB - - diff --git a/spec/fixtures/test.tmbundle/Preferences/Style: Numbered List.tmPreferences b/spec/fixtures/test.tmbundle/Preferences/Style: Numbered List.tmPreferences deleted file mode 100644 index d42f54a77..000000000 --- a/spec/fixtures/test.tmbundle/Preferences/Style: Numbered List.tmPreferences +++ /dev/null @@ -1,22 +0,0 @@ - - - - - name - Style: Numbered List - scope - markup.list.numbered - settings - - indentedSoftWrap - - format - ${0/\S/ /g} - match - \A\s*((\d+\.|[-*]|#+)\s+|(?=\w)) - - - uuid - EEDAA012-BFBD-4FE8-9253-4EDC5F7309F1 - - diff --git a/spec/fixtures/test.tmbundle/Preferences/Style: Raw.tmPreferences b/spec/fixtures/test.tmbundle/Preferences/Style: Raw.tmPreferences deleted file mode 100644 index 0cfde4b08..000000000 --- a/spec/fixtures/test.tmbundle/Preferences/Style: Raw.tmPreferences +++ /dev/null @@ -1,17 +0,0 @@ - - - - - name - Style: Raw - scope - markup.raw.block - settings - - softWrap - - - uuid - 47A88FB7-A084-48F6-9B54-078E9C888BE6 - - diff --git a/spec/fixtures/test.tmbundle/Preferences/Style: Unnumbered List.tmPreferences b/spec/fixtures/test.tmbundle/Preferences/Style: Unnumbered List.tmPreferences deleted file mode 100644 index 1c92881fd..000000000 --- a/spec/fixtures/test.tmbundle/Preferences/Style: Unnumbered List.tmPreferences +++ /dev/null @@ -1,22 +0,0 @@ - - - - - name - Style: Unnumbered List - scope - markup.list.unnumbered - settings - - indentedSoftWrap - - format - ${0/\S/ /g} - match - \A\s*[-*]\s+ - - - uuid - 93CBCCEB-32BE-41F3-9F00-B975831F33EC - - diff --git a/spec/fixtures/test.tmbundle/Preferences/empty.plist b/spec/fixtures/test.tmbundle/Preferences/empty.plist deleted file mode 100644 index e69de29bb..000000000 diff --git a/spec/fixtures/test.tmbundle/Snippets/010 Copyright.plist b/spec/fixtures/test.tmbundle/Snippets/010 Copyright.plist deleted file mode 100644 index 3d43183c1..000000000 --- a/spec/fixtures/test.tmbundle/Snippets/010 Copyright.plist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - content - (c) Copyright `date +%Y` $TM_FULLNAME. All Rights Reserved. - name - Copyright Notice - tabTrigger - c) - uuid - BC8B89E4-5F16-11D9-B9C3-000D93589AF6 - - diff --git a/spec/fixtures/test.tmbundle/Snippets/Conitnue bullet.plist b/spec/fixtures/test.tmbundle/Snippets/Conitnue bullet.plist deleted file mode 100644 index 4704f7906..000000000 --- a/spec/fixtures/test.tmbundle/Snippets/Conitnue bullet.plist +++ /dev/null @@ -1,17 +0,0 @@ - - - - - content - -• - keyEquivalent -  - name - Continue Bullet - scope - meta.bullet-point - uuid - C30BA263-B10E-11D9-9975-000D93589AF6 - - diff --git a/spec/fixtures/test.tmbundle/Snippets/Conitnue light bullet.plist b/spec/fixtures/test.tmbundle/Snippets/Conitnue light bullet.plist deleted file mode 100644 index 2c0cd2a80..000000000 --- a/spec/fixtures/test.tmbundle/Snippets/Conitnue light bullet.plist +++ /dev/null @@ -1,17 +0,0 @@ - - - - - content - - keyEquivalent -  - name - Continue Light Bullet - scope - meta.bullet-point.light - uuid - EA149DAB-B10E-11D9-9975-000D93589AF6 - - diff --git a/spec/fixtures/test.tmbundle/Snippets/Conitnue star bullet.plist b/spec/fixtures/test.tmbundle/Snippets/Conitnue star bullet.plist deleted file mode 100644 index 81dc40471..000000000 --- a/spec/fixtures/test.tmbundle/Snippets/Conitnue star bullet.plist +++ /dev/null @@ -1,17 +0,0 @@ - - - - - content - -* - keyEquivalent -  - name - Continue Star Bullet - scope - meta.bullet-point.star - uuid - B5E22191-B151-11D9-85C9-000D93589AF6 - - diff --git a/spec/fixtures/test.tmbundle/Snippets/Insert ISO date.plist b/spec/fixtures/test.tmbundle/Snippets/Insert ISO date.plist deleted file mode 100644 index c2b2f79b8..000000000 --- a/spec/fixtures/test.tmbundle/Snippets/Insert ISO date.plist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - content - `date +%Y-%m-%d` - name - Current Date — YYYY-MM-DD - tabTrigger - isoD - uuid - C9CAF012-6E50-11D9-AA12-000D93589AF6 - - diff --git a/spec/fixtures/test.tmbundle/Snippets/Lorem ipsum.plist b/spec/fixtures/test.tmbundle/Snippets/Lorem ipsum.plist deleted file mode 100644 index a30cff0d4..000000000 --- a/spec/fixtures/test.tmbundle/Snippets/Lorem ipsum.plist +++ /dev/null @@ -1,14 +0,0 @@ - - - - - content - Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. - name - Lorem ipsum - tabTrigger - lorem - uuid - BA9A2B17-DA89-49A5-809B-AC7510C24625 - - diff --git a/spec/fixtures/test.tmbundle/Syntaxes/Empty.plist b/spec/fixtures/test.tmbundle/Syntaxes/Empty.plist deleted file mode 100644 index e69de29bb..000000000 diff --git a/spec/fixtures/test.tmbundle/Syntaxes/Test.plist b/spec/fixtures/test.tmbundle/Syntaxes/Test.plist deleted file mode 100644 index 315d4ce16..000000000 --- a/spec/fixtures/test.tmbundle/Syntaxes/Test.plist +++ /dev/null @@ -1,35 +0,0 @@ - - - - - fileTypes - - .testo - - keyEquivalent - ^~P - name - Test - patterns - - - captures - - 1 - - name - punctuation.definition.item.text - - - match - ^\s*//.*$\n? - name - comment - - - scopeName - text.test - uuid - 313DD4FA-B10E-11D9-9F75-000D93589AF6 - - diff --git a/spec/spec-bootstrap.coffee b/spec/spec-bootstrap.coffee index 1b0ffcd48..f3a961207 100644 --- a/spec/spec-bootstrap.coffee +++ b/spec/spec-bootstrap.coffee @@ -7,4 +7,4 @@ try runSpecSuite "spec-suite" catch e console.error(e.stack) - atom.exit(1) + atom.exit(1) if window.location.params.exitWhenDone diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index b90303d82..65a1b85dd 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -17,6 +17,7 @@ RootView = require 'root-view' Git = require 'git' requireStylesheet "jasmine" fixturePackagesPath = fs.resolveOnLoadPath('fixtures/packages') +config.packageDirPaths.unshift(fixturePackagesPath) keymap.loadBundledKeymaps() [bindingSetsToRestore, bindingSetsByFirstKeystrokeToRestore] = [] @@ -36,12 +37,12 @@ beforeEach -> window.git = Git.open(window.project.getPath()) window.resetTimeouts() - atom.atomPackageStates = {} - atom.loadedPackages = [] + atom.packageStates = {} spyOn(atom, 'saveWindowState') spyOn(atom, 'getSavedWindowState').andReturn(null) $native.setWindowState('') syntax.clearGrammarOverrides() + syntax.clearProperties() # used to reset keymap after each spec bindingSetsToRestore = _.clone(keymap.bindingSets) @@ -76,8 +77,9 @@ beforeEach -> afterEach -> keymap.bindingSets = bindingSetsToRestore keymap.bindingSetsByFirstKeystrokeToRestore = bindingSetsByFirstKeystrokeToRestore + atom.deactivatePackages() if rootView? - rootView.deactivate?() + rootView.remove?() window.rootView = null if project? project.destroy() @@ -92,26 +94,6 @@ afterEach -> syntax.off() waits(0) # yield to ui thread to make screen update more frequently -window.loadPackage = (name, options={}) -> - Package = require 'package' - packagePath = _.find atom.getPackagePaths(), (packagePath) -> fs.base(packagePath) == name - if pack = Package.build(packagePath) - pack.load(options) - atom.loadedPackages.push(pack) - pack.deferActivation = false if options.activateImmediately - pack.activate() - pack - -# Specs rely on TextMate bundles (but not atom packages) -window.loadTextMatePackages = -> - TextMatePackage = require 'text-mate-package' - config.packageDirPaths.unshift(fixturePackagesPath) - window.textMatePackages = [] - for path in atom.getPackagePaths() when TextMatePackage.testName(path) - window.textMatePackages.push window.loadPackage(fs.base(path), sync: true) - -window.loadTextMatePackages() - ensureNoPathSubscriptions = -> watchedPaths = $native.getWatchedPaths() $native.unwatchAllPaths() diff --git a/spec/stdlib/task-shell-spec.coffee b/spec/stdlib/task-shell-spec.coffee index d1ccb8fc0..f1a539036 100644 --- a/spec/stdlib/task-shell-spec.coffee +++ b/spec/stdlib/task-shell-spec.coffee @@ -2,7 +2,7 @@ Task = require 'task' describe "Task shell", -> describe "populating the window with fake properties", -> - describe "when jQuery is loaded in a web worker", -> + describe "when jQuery is loaded in a child process", -> it "doesn't log to the console", -> spyOn(console, 'log') spyOn(console, 'error') @@ -14,7 +14,7 @@ describe "Task shell", -> task = new JQueryTask() task.start() - waitsFor "web worker to start and jquery to be required", 5000, -> + waitsFor "child process to start and jquery to be required", 5000, -> task.jqueryLoaded runs -> expect(task.jqueryLoaded).toBeTruthy() diff --git a/src/app/atom-package.coffee b/src/app/atom-package.coffee index f063296ad..c7f5cfeb3 100644 --- a/src/app/atom-package.coffee +++ b/src/app/atom-package.coffee @@ -1,6 +1,6 @@ TextMateGrammar = require 'text-mate-grammar' Package = require 'package' -fs = require 'fs-utils' +fsUtils = require 'fs-utils' _ = require 'underscore' $ = require 'jquery' CSON = require 'cson' @@ -8,8 +8,13 @@ CSON = require 'cson' module.exports = class AtomPackage extends Package metadata: null + keymaps: null + stylesheets: null + grammars: null + scopedProperties: null + mainModulePath: null + resolvedMainModulePath: false mainModule: null - deferActivation: false load: -> try @@ -18,7 +23,7 @@ class AtomPackage extends Package @loadStylesheets() @loadGrammars() @loadScopedProperties() - if @deferActivation = @metadata.activationEvents? + if @metadata.activationEvents? @registerDeferredDeserializers() else @requireMainModule() @@ -26,59 +31,91 @@ class AtomPackage extends Package console.warn "Failed to load package named '#{@name}'", e.stack this + activate: ({immediate}={}) -> + keymap.add(path, map) for [path, map] in @keymaps + applyStylesheet(path, content) for [path, content] in @stylesheets + syntax.addGrammar(grammar) for grammar in @grammars + syntax.addProperties(path, selector, properties) for [path, selector, properties] in @scopedProperties + + if @metadata.activationEvents? and not immediate + @subscribeToActivationEvents() + else + @activateNow() + + activateNow: -> + try + if @requireMainModule() + config.setDefaults(@name, @mainModule.configDefaults) + @mainModule.activate(atom.getPackageState(@name) ? {}) + catch e + console.warn "Failed to activate package named '#{@name}'", e.stack + loadMetadata: -> - if metadataPath = fs.resolveExtension(fs.join(@path, 'package'), ['cson', 'json']) + if metadataPath = fsUtils.resolveExtension(fsUtils.join(@path, 'package'), ['cson', 'json']) @metadata = CSON.readObject(metadataPath) @metadata ?= {} loadKeymaps: -> - keymapsDirPath = fs.join(@path, 'keymaps') + @keymaps = @getKeymapPaths().map (path) -> [path, CSON.readObject(path)] + getKeymapPaths: -> + keymapsDirPath = fsUtils.join(@path, 'keymaps') if @metadata.keymaps - for path in @metadata.keymaps - keymapPath = fs.resolve(keymapsDirPath, path, ['cson', 'json', '']) - keymap.load(keymapPath) + @metadata.keymaps.map (name) -> fsUtils.resolve(keymapsDirPath, name, ['cson', 'json', '']) else - keymap.loadDirectory(keymapsDirPath) + fsUtils.list(keymapsDirPath, ['cson', 'json']) ? [] loadStylesheets: -> - stylesheetDirPath = fs.join(@path, 'stylesheets') - for stylesheetPath in fs.list(stylesheetDirPath) ? [] - requireStylesheet(stylesheetPath) + @stylesheets = @getStylesheetPaths().map (path) -> [path, loadStylesheet(path)] + + getStylesheetPaths: -> + stylesheetDirPath = fsUtils.join(@path, 'stylesheets') + if @metadata.stylesheets + @metadata.stylesheets.map (name) -> fsUtils.resolve(stylesheetDirPath, name, ['css', 'less', '']) + else + fsUtils.list(stylesheetDirPath, ['css', 'less']) ? [] loadGrammars: -> - grammarsDirPath = fs.join(@path, 'grammars') - for grammarPath in fs.list(grammarsDirPath, ['.cson', '.json']) ? [] - grammarContent = fs.readObject(grammarPath) - grammar = new TextMateGrammar(grammarContent) - syntax.addGrammar(grammar) + @grammars = [] + grammarsDirPath = fsUtils.join(@path, 'grammars') + for grammarPath in fsUtils.list(grammarsDirPath, ['.cson', '.json']) ? [] + @grammars.push(TextMateGrammar.loadSync(grammarPath)) loadScopedProperties: -> - scopedPropertiessDirPath = fs.join(@path, 'scoped-properties') - for scopedPropertiesPath in fs.list(scopedPropertiessDirPath, ['.cson', '.json']) ? [] - for selector, properties of fs.readObject(scopedPropertiesPath) - syntax.addProperties(selector, properties) + @scopedProperties = [] + scopedPropertiessDirPath = fsUtils.join(@path, 'scoped-properties') + for scopedPropertiesPath in fsUtils.list(scopedPropertiessDirPath, ['.cson', '.json']) ? [] + for selector, properties of fsUtils.readObject(scopedPropertiesPath) + @scopedProperties.push([scopedPropertiesPath, selector, properties]) - activate: -> - if @deferActivation - @subscribeToActivationEvents() - else - try - if @requireMainModule() - config.setDefaults(@name, @mainModule.configDefaults) - atom.activateAtomPackage(this) - catch e - console.warn "Failed to activate package named '#{@name}'", e.stack + serialize: -> + try + @mainModule?.serialize?() + catch e + console.error "Error serializing package '#{@name}'", e.stack + + deactivate: -> + @unsubscribeFromActivationEvents() + syntax.removeGrammar(grammar) for grammar in @grammars + syntax.removeProperties(path) for [path] in @scopedProperties + keymap.remove(path) for [path] in @keymaps + removeStylesheet(path) for [path] in @stylesheets + @mainModule?.deactivate?() requireMainModule: -> return @mainModule if @mainModule - mainPath = + mainModulePath = @getMainModulePath() + @mainModule = require(mainModulePath) if fsUtils.isFile(mainModulePath) + + getMainModulePath: -> + return @mainModulePath if @resolvedMainModulePath + @resolvedMainModulePath = true + mainModulePath = if @metadata.main - fs.join(@path, @metadata.main) + fsUtils.join(@path, @metadata.main) else - fs.join(@path, 'index') - mainPath = fs.resolveExtension(mainPath, ["", _.keys(require.extensions)...]) - @mainModule = require(mainPath) if fs.isFile(mainPath) + fsUtils.join(@path, 'index') + @mainModulePath = fsUtils.resolveExtension(mainModulePath, ["", _.keys(require.extensions)...]) registerDeferredDeserializers: -> for deserializerName in @metadata.deferredDeserializers ? [] @@ -86,25 +123,23 @@ class AtomPackage extends Package subscribeToActivationEvents: () -> return unless @metadata.activationEvents? - - activateHandler = (event) => - bubblePathEventHandlers = @disableEventHandlersOnBubblePath(event) - @deferActivation = false - @activate() - $(event.target).trigger(event) - @restoreEventHandlersOnBubblePath(bubblePathEventHandlers) - @unsubscribeFromActivationEvents(activateHandler) - if _.isArray(@metadata.activationEvents) - rootView.command(event, activateHandler) for event in @metadata.activationEvents + rootView.command(event, @handleActivationEvent) for event in @metadata.activationEvents else - rootView.command(event, selector, activateHandler) for event, selector of @metadata.activationEvents + rootView.command(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents - unsubscribeFromActivationEvents: (activateHandler) -> + handleActivationEvent: (event) => + bubblePathEventHandlers = @disableEventHandlersOnBubblePath(event) + @activateNow() + $(event.target).trigger(event) + @restoreEventHandlersOnBubblePath(bubblePathEventHandlers) + @unsubscribeFromActivationEvents() + + unsubscribeFromActivationEvents: -> if _.isArray(@metadata.activationEvents) - rootView.off(event, activateHandler) for event in @metadata.activationEvents + rootView.off(event, @handleActivationEvent) for event in @metadata.activationEvents else - rootView.off(event, selector, activateHandler) for event, selector of @metadata.activationEvents + rootView.off(event, selector, @handleActivationEvent) for event, selector of @metadata.activationEvents disableEventHandlersOnBubblePath: (event) -> bubblePathEventHandlers = [] diff --git a/src/app/atom.coffee b/src/app/atom.coffee index 5a784ea5f..70c05a952 100644 --- a/src/app/atom.coffee +++ b/src/app/atom.coffee @@ -13,59 +13,91 @@ _.extend atom, loadedThemes: [] pendingBrowserProcessCallbacks: {} loadedPackages: [] - activatedAtomPackages: [] - atomPackageStates: {} + activePackages: [] + packageStates: {} presentingModal: false pendingModals: [[]] getPathToOpen: -> @getWindowState('pathToOpen') ? window.location.params.pathToOpen - activateAtomPackage: (pack) -> - @activatedAtomPackages.push(pack) - pack.mainModule.activate(@atomPackageStates[pack.name] ? {}) + getPackageState: (name) -> + @packageStates[name] - deactivateAtomPackages: -> - pack.mainModule.deactivate?() for pack in @activatedAtomPackages - @activatedAtomPackages = [] - - serializeAtomPackages: -> - packageStates = {} - for pack in @loadedPackages - if pack in @activatedAtomPackages - try - packageStates[pack.name] = pack.mainModule.serialize?() - catch e - console?.error("Exception serializing '#{pack.name}' package's module\n", e.stack) - else - packageStates[pack.name] = @atomPackageStates[pack.name] - packageStates - - loadPackages: -> - textMatePackages = [] - paths = @getPackagePaths().filter (path) -> fs.base(path) isnt 'text.tmbundle' - for path in paths - pack = Package.build(path) - @loadedPackages.push(pack) - pack.load() + setPackageState: (name, state) -> + @packageStates[name] = state activatePackages: -> - pack.activate() for pack in @loadedPackages + @activatePackage(pack.path) for pack in @getLoadedPackages() + + activatePackage: (id, options) -> + if pack = @loadPackage(id, options) + @activePackages.push(pack) + pack.activate(options) + pack + + deactivatePackages: -> + @deactivatePackage(pack.path) for pack in @getActivePackages() + + deactivatePackage: (id) -> + if pack = @getActivePackage(id) + @setPackageState(pack.name, state) if state = pack.serialize?() + pack.deactivate() + _.remove(@activePackages, pack) + else + throw new Error("No active package for id '#{id}'") + + getActivePackage: (id) -> + if path = @resolvePackagePath(id) + _.detect @activePackages, (pack) -> pack.path is path + + isPackageActive: (id) -> + if path = @resolvePackagePath(id) + _.detect @activePackages, (pack) -> pack.path is path + + getActivePackages: -> + _.clone(@activePackages) + + loadPackages: -> + @loadPackage(path) for path in @getPackagePaths() when not @isPackageDisabled(path) + + loadPackage: (id, options) -> + if @isPackageDisabled(id) + return console.warn("Tried to load disabled packaged '#{id}'") + + if path = @resolvePackagePath(id) + return pack if pack = @getLoadedPackage(id) + pack = Package.load(path, options) + @loadedPackages.push(pack) + pack + else + throw new Error("Could not resolve '#{id}' to a package path") + + resolvePackagePath: _.memoize (id) -> + return id if fs.isDirectory(id) + path = fs.resolve(config.packageDirPaths..., id) + path if fs.isDirectory(path) + + getLoadedPackage: (id) -> + if path = @resolvePackagePath(id) + _.detect @loadedPackages, (pack) -> pack.path is path + + isPackageLoaded: (id) -> + @getLoadedPackage(id)? getLoadedPackages: -> _.clone(@loadedPackages) + isPackageDisabled: (id) -> + if path = @resolvePackagePath(id) + _.include(config.get('core.disabledPackages') ? [], fs.base(path)) + getPackagePaths: -> - disabledPackages = config.get("core.disabledPackages") ? [] packagePaths = [] for packageDirPath in config.packageDirPaths for packagePath in fs.list(packageDirPath) - continue if not fs.isDirectory(packagePath) - continue if fs.base(packagePath) in disabledPackages - continue if packagePath in packagePaths - packagePaths.push(packagePath) - - packagePaths + packagePaths.push(packagePath) if fs.isDirectory(packagePath) + _.uniq(packagePaths) loadThemes: -> themeNames = config.get("core.themes") ? ['atom-dark-ui', 'atom-dark-syntax'] diff --git a/src/app/binding-set.coffee b/src/app/binding-set.coffee index 5621ef9cb..3bf87dab8 100644 --- a/src/app/binding-set.coffee +++ b/src/app/binding-set.coffee @@ -14,8 +14,9 @@ class BindingSet commandsByKeystrokes: null commandForEvent: null parser: null + name: null - constructor: (@selector, commandsByKeystrokes, @index) -> + constructor: (@selector, commandsByKeystrokes, @index, @name) -> BindingSet.parser ?= PEG.buildParser(fs.read(require.resolve 'keystroke-pattern.pegjs')) @specificity = Specificity(@selector) @commandsByKeystrokes = @normalizeCommandsByKeystrokes(commandsByKeystrokes) diff --git a/src/app/edit-session.coffee b/src/app/edit-session.coffee index 5b66f2c98..e14baa16a 100644 --- a/src/app/edit-session.coffee +++ b/src/app/edit-session.coffee @@ -584,9 +584,15 @@ class EditSession cursor = @addCursor(marker) selection = new Selection({editSession: this, marker, cursor}) @selections.push(selection) + selectionBufferRange = selection.getBufferRange() @mergeIntersectingSelections() - @trigger 'selection-added', selection - selection + if selection.destroyed + for selection in @getSelections() + if selection.intersectsBufferRange(selectionBufferRange) + return selection + else + @trigger 'selection-added', selection + selection addSelectionForBufferRange: (bufferRange, options={}) -> options = _.defaults({invalidationStrategy: 'never'}, options) @@ -837,12 +843,18 @@ class EditSession getGrammar: -> @languageMode.grammar + setGrammar: (grammar) -> + @languageMode.grammar = grammar + @handleGrammarChange() + reloadGrammar: -> - if @languageMode.reloadGrammar() - @unfoldAll() - @displayBuffer.tokenizedBuffer.resetScreenLines() - @trigger 'grammar-changed' - true + @handleGrammarChange() if @languageMode.reloadGrammar() + + handleGrammarChange: -> + @unfoldAll() + @displayBuffer.tokenizedBuffer.resetScreenLines() + @trigger 'grammar-changed' + true getDebugSnapshot: -> [ diff --git a/src/app/editor.coffee b/src/app/editor.coffee index 21eaf143c..9377de760 100644 --- a/src/app/editor.coffee +++ b/src/app/editor.coffee @@ -150,7 +150,6 @@ class Editor extends View 'editor:toggle-line-comments': @toggleLineCommentsInSelection 'editor:log-cursor-scope': @logCursorScope 'editor:checkout-head-revision': @checkoutHead - 'editor:select-grammar': @selectGrammar 'editor:copy-path': @copyPathToPasteboard 'editor:move-line-up': @moveLineUp 'editor:move-line-down': @moveLineDown @@ -374,7 +373,7 @@ class Editor extends View else if clickCount == 3 @activeEditSession.selectLine() unless e.shiftKey - @selectOnMousemoveUntilMouseup() + @selectOnMousemoveUntilMouseup() unless e.originalEvent.which > 1 @renderedLines.on 'mousedown', onMouseDown @@ -590,6 +589,7 @@ class Editor extends View @setSoftWrapColumn(softWrapColumn) if @attached if @activeEditSession.getSoftWrap() @addClass 'soft-wrap' + @scrollView.scrollLeft(0) @_setSoftWrapColumn = => @setSoftWrapColumn() $(window).on "resize.editor-#{@id}", @_setSoftWrapColumn else @@ -659,7 +659,7 @@ class Editor extends View super rootView?.focus() - afterRemove: -> + beforeRemove: -> @removed = true @activeEditSession?.destroy() $(window).off(".editor-#{@id}") @@ -1143,19 +1143,23 @@ class Editor extends View else @highlightedLine = null - getGrammar: -> @activeEditSession.getGrammar() + getGrammar: -> + @activeEditSession.getGrammar() - selectGrammar: -> - GrammarView = require 'grammar-view' - new GrammarView(this) + setGrammar: (grammar) -> + throw new Error("Only mini-editors can explicity set their grammar") unless @mini + @activeEditSession.setGrammar(grammar) + @handleGrammarChange() reloadGrammar: -> grammarChanged = @activeEditSession.reloadGrammar() - if grammarChanged - @clearRenderedLines() - @updateDisplay() + @handleGrammarChange() if grammarChanged grammarChanged + handleGrammarChange: -> + @clearRenderedLines() + @updateDisplay() + bindToKeyedEvent: (key, event, callback) -> binding = {} binding[key] = event diff --git a/src/app/keymap.coffee b/src/app/keymap.coffee index ec260f417..71be30cec 100644 --- a/src/app/keymap.coffee +++ b/src/app/keymap.coffee @@ -8,6 +8,7 @@ BindingSet = require 'binding-set' module.exports = class Keymap bindingSets: null + nextBindingSetIndex: 0 bindingSetsByFirstKeystroke: null queuedKeystrokes: null @@ -40,20 +41,39 @@ class Keymap @load(filePath) for filePath in fs.list(directoryPath, ['.cson', '.json']) ? [] load: (path) -> - @add(CSON.readObject(path)) + @add(path, CSON.readObject(path)) - add: (keymap) -> + add: (args...) -> + name = args.shift() if args.length > 1 + keymap = args.shift() for selector, bindings of keymap - @bindKeys(selector, bindings) + @bindKeys(name, selector, bindings) - bindKeys: (selector, bindings) -> - bindingSet = new BindingSet(selector, bindings, @bindingSets.length) + remove: (name) -> + for bindingSet in @bindingSets.filter((bindingSet) -> bindingSet.name is name) + _.remove(@bindingSets, bindingSet) + for keystrokes of bindingSet.commandsByKeystrokes + keystroke = keystrokes.split(' ')[0] + _.remove(@bindingSetsByFirstKeystroke[keystroke], bindingSet) + + bindKeys: (args...) -> + name = args.shift() if args.length > 2 + [selector, bindings] = args + bindingSet = new BindingSet(selector, bindings, @nextBindingSetIndex++, name) @bindingSets.unshift(bindingSet) for keystrokes of bindingSet.commandsByKeystrokes keystroke = keystrokes.split(' ')[0] # only index by first keystroke @bindingSetsByFirstKeystroke[keystroke] ?= [] @bindingSetsByFirstKeystroke[keystroke].push(bindingSet) + unbindKeys: (selector, bindings) -> + bindingSet = _.detect @bindingSets, (bindingSet) -> + bindingSet.selector is selector and bindingSet.bindings is bindings + + if bindingSet + console.log "binding set", bindingSet + _.remove(@bindingSets, bindingSet) + bindingsForElement: (element) -> keystrokeMap = {} currentNode = $(element) diff --git a/src/app/keymaps/editor.cson b/src/app/keymaps/editor.cson index 8b192fece..f9675e6c1 100644 --- a/src/app/keymaps/editor.cson +++ b/src/app/keymaps/editor.cson @@ -20,7 +20,6 @@ 'meta-U': 'editor:lower-case' 'alt-meta-w': 'editor:close-other-edit-sessions' 'meta-P': 'editor:close-all-edit-sessions' - 'meta-L': 'editor:select-grammar' 'ctrl-C': 'editor:copy-path' 'ctrl-meta-up': 'editor:move-line-up' 'ctrl-meta-down': 'editor:move-line-down' diff --git a/src/app/package.coffee b/src/app/package.coffee index 57c4dc8f5..c03e082d5 100644 --- a/src/app/package.coffee +++ b/src/app/package.coffee @@ -11,6 +11,11 @@ class Package else new AtomPackage(path) + @load: (path, options) -> + pack = @build(path) + pack.load(options) + pack + name: null path: null diff --git a/src/app/pane.coffee b/src/app/pane.coffee index 3bd9c9f4c..e9132cc11 100644 --- a/src/app/pane.coffee +++ b/src/app/pane.coffee @@ -324,5 +324,5 @@ class Pane extends View container.adjustPaneDimensions() container.trigger 'pane:removed', [this] - afterRemove: -> + beforeRemove: -> item.destroy?() for item in @getItems() diff --git a/src/app/point.coffee b/src/app/point.coffee index e471ad6c6..6dd3eca27 100644 --- a/src/app/point.coffee +++ b/src/app/point.coffee @@ -1,10 +1,12 @@ +_ = require 'underscore' + module.exports = class Point @fromObject: (object) -> if object instanceof Point object else - if object instanceof Array + if _.isArray(object) [row, column] = object else { row, column } = object diff --git a/src/app/range.coffee b/src/app/range.coffee index a3870ea66..d72ef6514 100644 --- a/src/app/range.coffee +++ b/src/app/range.coffee @@ -31,7 +31,7 @@ class Range new Range(@start.copy(), @end.copy()) isEqual: (other) -> - if other instanceof Array and other.length == 2 + if _.isArray(other) and other.length == 2 other = new Range(other...) other.start.isEqual(@start) and other.end.isEqual(@end) diff --git a/src/app/root-view.coffee b/src/app/root-view.coffee index 5a9f35a3f..2c299c92c 100644 --- a/src/app/root-view.coffee +++ b/src/app/root-view.coffee @@ -29,8 +29,7 @@ class RootView extends View @div id: 'vertical', outlet: 'vertical', => @subview 'panes', panes ? new PaneContainer - @deserialize: ({ panes, packages, projectPath }) -> - atom.atomPackageStates = packages ? {} + @deserialize: ({ panes }) -> panes = deserialize(panes) if panes?.deserializer is 'PaneContainer' new RootView({panes}) @@ -73,7 +72,6 @@ class RootView extends View version: RootView.version deserializer: 'RootView' panes: @panes.serialize() - packages: atom.serializeAtomPackages() confirmClose: -> @panes.confirmClose() @@ -94,10 +92,6 @@ class RootView extends View afterAttach: (onDom) -> @focus() if onDom - deactivate: -> - atom.deactivateAtomPackages() - @remove() - open: (path, options = {}) -> changeFocus = options.changeFocus ? true path = project.resolve(path) if path? diff --git a/src/app/syntax.coffee b/src/app/syntax.coffee index f37949370..7ab711a14 100644 --- a/src/app/syntax.coffee +++ b/src/app/syntax.coffee @@ -22,7 +22,6 @@ class Syntax @grammarsByFileType = {} @grammarsByScopeName = {} @grammarOverridesByPath = {} - @globalProperties = {} @scopedPropertiesIndex = 0 @scopedProperties = [] @nullGrammar = new NullGrammar @@ -32,9 +31,14 @@ class Syntax addGrammar: (grammar) -> @grammars.push(grammar) - for fileType in grammar.fileTypes - @grammarsByFileType[fileType] = grammar - @grammarsByScopeName[grammar.scopeName] = grammar + @grammarsByFileType[fileType] = grammar for fileType in grammar.fileTypes + @grammarsByScopeName[grammar.scopeName] = grammar + + removeGrammar: (grammar) -> + if _.include(@grammars, grammar) + _.remove(@grammars, grammar) + delete @grammarsByFileType[fileType] for fileType in grammar.fileTypes + delete @grammarsByScopeName[grammar.scopeName] setGrammarOverrideForPath: (path, scopeName) -> @grammarOverridesByPath[path] = scopeName @@ -46,7 +50,9 @@ class Syntax @grammarOverridesByPath = {} selectGrammar: (filePath, fileContents) -> + return @grammarsByFileType["txt"] ? @nullGrammar unless filePath + @grammarOverrideForPath(filePath) ? @grammarByFirstLineRegex(filePath, fileContents) ? @grammarByPath(filePath) ? @@ -94,18 +100,24 @@ class Syntax @grammarsByScopeName[scopeName] addProperties: (args...) -> - selector = args.shift() if args.length > 1 - properties = args.shift() + name = args.shift() if args.length > 2 + [selector, properties] = args - if selector - @scopedProperties.unshift( - selector: selector, - properties: properties, - specificity: Specificity(selector), - index: @scopedPropertiesIndex++ - ) - else - _.extend(@globalProperties, properties) + @scopedProperties.unshift( + name: name + selector: selector, + properties: properties, + specificity: Specificity(selector), + index: @scopedPropertiesIndex++ + ) + + removeProperties: (name) -> + for properties in @scopedProperties.filter((properties) -> properties.name is name) + _.remove(@scopedProperties, properties) + + clearProperties: -> + @scopedProperties = [] + @scopedPropertiesIndex = 0 getProperty: (scope, keyPath) -> for object in @propertiesForScope(scope, keyPath) @@ -121,7 +133,7 @@ class Syntax while element matchingProperties.push(@matchingPropertiesForElement(element, candidates)...) element = element.parentNode - matchingProperties.concat([@globalProperties]) + matchingProperties matchingPropertiesForElement: (element, candidates) -> matchingScopedProperties = candidates.filter ({selector}) -> diff --git a/src/app/text-mate-package.coffee b/src/app/text-mate-package.coffee index 4d2af792f..a6ef9b8fd 100644 --- a/src/app/text-mate-package.coffee +++ b/src/app/text-mate-package.coffee @@ -1,10 +1,8 @@ Package = require 'package' -fs = require 'fs' fsUtils = require 'fs-utils' plist = require 'plist' _ = require 'underscore' TextMateGrammar = require 'text-mate-grammar' -CSON = require 'cson' async = require 'async' module.exports = @@ -31,7 +29,16 @@ class TextMatePackage extends Package TextMatePackage.getLoadQueue().push(this) @loadScopedProperties() - legalGrammarExtensions: ['plist', 'tmLanguage', 'tmlanguage', 'cson', 'json'] + activate: -> + syntax.addGrammar(grammar) for grammar in @grammars + for { selector, properties } in @scopedProperties + syntax.addProperties(@path, selector, properties) + + deactivate: -> + syntax.removeGrammar(grammar) for grammar in @grammars + syntax.removeProperties(@path) + + legalGrammarExtensions: ['plist', 'tmLanguage', 'tmlanguage'] loadGrammars: (done) -> fsUtils.isDirectoryAsync @syntaxesPath, (isDirectory) => @@ -52,30 +59,22 @@ class TextMatePackage extends Package addGrammar: (grammar) -> @grammars.push(grammar) - syntax.addGrammar(grammar) - - activate: -> # no-op + syntax.addGrammar(grammar) if atom.isPackageActive(@path) getGrammars: -> @grammars loadScopedProperties: -> - for { selector, properties } in @getScopedProperties() - syntax.addProperties(selector, properties) - - getScopedProperties: -> - scopedProperties = [] + @scopedProperties = [] for grammar in @getGrammars() if properties = @propertiesFromTextMateSettings(grammar) selector = syntax.cssSelectorFromScopeSelector(grammar.scopeName) - scopedProperties.push({selector, properties}) + @scopedProperties.push({selector, properties}) for {scope, settings} in @getTextMatePreferenceObjects() if properties = @propertiesFromTextMateSettings(settings) selector = syntax.cssSelectorFromScopeSelector(scope) if scope? - scopedProperties.push({selector, properties}) - - scopedProperties + @scopedProperties.push({selector, properties}) getTextMatePreferenceObjects: -> preferenceObjects = [] diff --git a/src/app/window.coffee b/src/app/window.coffee index a2056a672..f6c513c2d 100644 --- a/src/app/window.coffee +++ b/src/app/window.coffee @@ -61,10 +61,12 @@ window.shutdown = -> return if not project and not rootView atom.setWindowState('pathToOpen', project.getPath()) atom.setWindowState('project', project.serialize()) - atom.setWindowState('rootView', rootView.serialize()) atom.setWindowState('syntax', syntax.serialize()) + atom.setWindowState('rootView', rootView.serialize()) + atom.deactivatePackages() + atom.setWindowState('packageStates', atom.packageStates) + rootView.remove() atom.saveWindowState() - rootView.deactivate() project.destroy() git?.destroy() $(window).off('focus blur before') @@ -78,7 +80,7 @@ window.installAtomCommand = (commandPath) -> bundledCommandPath = fs.resolve(window.resourcePath, 'atom.sh') if bundledCommandPath? fs.write(commandPath, fs.read(bundledCommandPath)) - spawn("chmod u+x '#{commandPath}'") + spawn('chmod', ['u+x', commandPath]) window.handleWindowEvents = -> $(window).command 'window:toggle-full-screen', => atom.toggleFullScreen() @@ -95,6 +97,7 @@ window.deserializeWindowState = -> windowState = atom.getWindowState() + atom.packageStates = windowState.packageStates ? {} window.project = deserialize(windowState.project) ? new Project(pathToOpen) window.rootView = deserialize(windowState.rootView) ? new RootView diff --git a/src/packages/autocomplete/spec/autocomplete-spec.coffee b/src/packages/autocomplete/spec/autocomplete-spec.coffee index c210b2173..b525a8fe7 100644 --- a/src/packages/autocomplete/spec/autocomplete-spec.coffee +++ b/src/packages/autocomplete/spec/autocomplete-spec.coffee @@ -14,7 +14,7 @@ describe "Autocomplete", -> describe "@activate()", -> it "activates autocomplete on all existing and future editors (but not on autocomplete's own mini editor)", -> spyOn(AutocompleteView.prototype, 'initialize').andCallThrough() - autocompletePackage = window.loadPackage("autocomplete") + autocompletePackage = atom.activatePackage("autocomplete") expect(AutocompleteView.prototype.initialize).not.toHaveBeenCalled() leftEditor = rootView.getActiveView() @@ -41,7 +41,7 @@ describe "AutocompleteView", -> beforeEach -> window.rootView = new RootView editor = new Editor(editSession: project.buildEditSession('sample.js')) - window.loadPackage('autocomplete') + atom.activatePackage('autocomplete') autocomplete = new AutocompleteView(editor) miniEditor = autocomplete.miniEditor diff --git a/src/packages/autoflow/spec/autoflow-spec.coffee b/src/packages/autoflow/spec/autoflow-spec.coffee index 190ac238c..c497f6292 100644 --- a/src/packages/autoflow/spec/autoflow-spec.coffee +++ b/src/packages/autoflow/spec/autoflow-spec.coffee @@ -6,7 +6,7 @@ describe "Autoflow package", -> beforeEach -> window.rootView = new RootView rootView.open() - window.loadPackage 'autoflow' + atom.activatePackage('autoflow') editor = rootView.getActiveView() config.set('editor.preferredLineLength', 30) diff --git a/src/packages/bracket-matcher/spec/bracket-matcher-spec.coffee b/src/packages/bracket-matcher/spec/bracket-matcher-spec.coffee index 099be0926..9a44ba3a7 100644 --- a/src/packages/bracket-matcher/spec/bracket-matcher-spec.coffee +++ b/src/packages/bracket-matcher/spec/bracket-matcher-spec.coffee @@ -6,7 +6,7 @@ describe "bracket matching", -> beforeEach -> window.rootView = new RootView rootView.open('sample.js') - window.loadPackage('bracket-matcher') + atom.activatePackage('bracket-matcher') rootView.attachToDom() editor = rootView.getActiveView() editSession = editor.activeEditSession diff --git a/src/packages/command-logger/spec/command-logger-spec.coffee b/src/packages/command-logger/spec/command-logger-spec.coffee index 8feec78bd..96365b469 100644 --- a/src/packages/command-logger/spec/command-logger-spec.coffee +++ b/src/packages/command-logger/spec/command-logger-spec.coffee @@ -7,7 +7,7 @@ describe "CommandLogger", -> beforeEach -> window.rootView = new RootView rootView.open('sample.js') - commandLogger = window.loadPackage('command-logger').mainModule + commandLogger = atom.activatePackage('command-logger').mainModule commandLogger.eventLog = {} editor = rootView.getActiveView() diff --git a/src/packages/command-palette/lib/command-palette-view.coffee b/src/packages/command-palette/lib/command-palette-view.coffee index 2acb80753..34988e751 100644 --- a/src/packages/command-palette/lib/command-palette-view.coffee +++ b/src/packages/command-palette/lib/command-palette-view.coffee @@ -13,7 +13,6 @@ class CommandPaletteView extends SelectList filterKey: 'eventDescription' - previouslyFocusedElement: null keyBindings: null initialize: -> @@ -30,10 +29,14 @@ class CommandPaletteView extends SelectList attach: -> super - @keyBindings = _.losslessInvert(keymap.bindingsForElement(@previouslyFocusedElement)) + if @previouslyFocusedElement[0] + @eventElement = @previouslyFocusedElement + else + @eventElement = rootView + @keyBindings = _.losslessInvert(keymap.bindingsForElement(@eventElement)) events = [] - for eventName, eventDescription of _.extend($(window).events(), @previouslyFocusedElement.events()) + for eventName, eventDescription of _.extend($(window).events(), @eventElement.events()) events.push({eventName, eventDescription}) if eventDescription events = _.sortBy events, (e) -> e.eventDescription @@ -53,4 +56,4 @@ class CommandPaletteView extends SelectList confirmed: ({eventName}) -> @cancel() - @previouslyFocusedElement.trigger(eventName) + @eventElement.trigger(eventName) diff --git a/src/packages/command-palette/spec/command-palette-spec.coffee b/src/packages/command-palette/spec/command-palette-spec.coffee index 414c53996..a0de19c61 100644 --- a/src/packages/command-palette/spec/command-palette-spec.coffee +++ b/src/packages/command-palette/spec/command-palette-spec.coffee @@ -9,7 +9,7 @@ describe "CommandPalette", -> beforeEach -> window.rootView = new RootView rootView.open('sample.js') - window.loadPackage("command-palette") + atom.activatePackage("command-palette") rootView.attachToDom().focus() rootView.trigger 'command-palette:toggle' palette = rootView.find('.command-palette').view() @@ -81,3 +81,20 @@ describe "CommandPalette", -> expect(activeEditor.isFocused).toBeTruthy() expect(eventHandler).toHaveBeenCalled() expect(palette.hasParent()).toBeFalsy() + + describe "when no element has focus", -> + it "uses the root view as the element to display and trigger events for", -> + rootView.trigger 'command-palette:toggle' + $(':focus').blur() + rootView.trigger 'command-palette:toggle' + keyBindings = _.losslessInvert(keymap.bindingsForElement(rootView.getActiveView())) + for eventName, description of rootView.events() + eventLi = palette.list.children("[data-event-name='#{eventName}']") + if description + expect(eventLi).toExist() + expect(eventLi.find('.label')).toHaveText(description) + expect(eventLi.find('.label').attr('title')).toBe(eventName) + for binding in keyBindings[eventName] ? [] + expect(eventLi.find(".key-binding:contains(#{binding})")).toExist() + else + expect(eventLi).not.toExist() diff --git a/src/packages/command-panel/lib/command-interpreter.coffee b/src/packages/command-panel/lib/command-interpreter.coffee index 8caa07b20..07f53672e 100644 --- a/src/packages/command-panel/lib/command-interpreter.coffee +++ b/src/packages/command-panel/lib/command-interpreter.coffee @@ -11,8 +11,12 @@ class CommandInterpreter @lastRelativeAddress = compositeCommand if compositeCommand.isRelativeAddress() compositeCommand.execute(@project, activeEditSession) - repeatRelativeAddress: (activeEditSession) -> - @lastRelativeAddress?.execute(@project, activeEditSession) + repeatRelativeAddress: (activeEditSession, {reverse}={}) -> + return unless @lastRelativeAddress + reverse ?= false + previousSelectionRange = activeEditSession.getSelection().getBufferRange() + address = if reverse then @lastRelativeAddress.reverse() else @lastRelativeAddress - repeatRelativeAddressInReverse: (activeEditSession) -> - @lastRelativeAddress?.reverse().execute(@project, activeEditSession) + address.execute(@project, activeEditSession).done -> + currentSelectionRange = activeEditSession.getSelection().getBufferRange() + $native.beep() if previousSelectionRange.isEqual(currentSelectionRange) diff --git a/src/packages/command-panel/lib/command-panel-view.coffee b/src/packages/command-panel/lib/command-panel-view.coffee index bd72f93ee..9ed6533d5 100644 --- a/src/packages/command-panel/lib/command-panel-view.coffee +++ b/src/packages/command-panel/lib/command-panel-view.coffee @@ -44,7 +44,7 @@ class CommandPanelView extends View rootView.command 'command-panel:find-in-file', => @attach('/') rootView.command 'command-panel:find-in-project', => @attach('Xx/') rootView.command 'command-panel:repeat-relative-address', => @repeatRelativeAddress() - rootView.command 'command-panel:repeat-relative-address-in-reverse', => @repeatRelativeAddressInReverse() + rootView.command 'command-panel:repeat-relative-address-in-reverse', => @repeatRelativeAddress(reverse: true) rootView.command 'command-panel:set-selection-as-regex-address', => @setSelectionAsLastRelativeAddress() @on 'click', '.expand', @onExpandAll @@ -65,6 +65,9 @@ class CommandPanelView extends View destroy: -> @previewList.destroy() + rootView.off "command-panel:toggle-preview command-panel:find-in-file command-panel:find-in-project \ + command-panel:repeat-relative-address command-panel:repeat-relative-address-in-reverse command-panel:set-selection-as-regex-address" + @remove() toggle: -> if @miniEditor.isFocused @@ -138,6 +141,7 @@ class CommandPanelView extends View else @detach() catch error + @loadingMessage.hide() if error.name is "SyntaxError" @flashError() return @@ -154,11 +158,8 @@ class CommandPanelView extends View @historyIndex++ @miniEditor.setText(@history[@historyIndex] or '') - repeatRelativeAddress: -> - @commandInterpreter.repeatRelativeAddress(rootView.getActivePaneItem()) - - repeatRelativeAddressInReverse: -> - @commandInterpreter.repeatRelativeAddressInReverse(rootView.getActivePaneItem()) + repeatRelativeAddress: (options) -> + @commandInterpreter.repeatRelativeAddress(rootView.getActivePaneItem(), options) setSelectionAsLastRelativeAddress: -> selection = rootView.getActiveView().getSelectedText() diff --git a/src/packages/command-panel/lib/commands/address.coffee b/src/packages/command-panel/lib/commands/address.coffee index 7d0f88214..ffa89f8ff 100644 --- a/src/packages/command-panel/lib/commands/address.coffee +++ b/src/packages/command-panel/lib/commands/address.coffee @@ -6,7 +6,7 @@ module.exports = class Address extends Command compile: (project, buffer, ranges) -> deferred = $.Deferred() - deferred.resolve ranges.map (range) => + operations = ranges.map (range) => newRange = @getRange(buffer, range) new Operation @@ -15,6 +15,7 @@ class Address extends Command bufferRange: newRange errorMessage: @errorMessage + deferred.resolve(operations) deferred.promise() isAddress: -> true diff --git a/src/packages/command-panel/spec/command-panel-spec.coffee b/src/packages/command-panel/spec/command-panel-spec.coffee index c6d9f7997..782399da3 100644 --- a/src/packages/command-panel/spec/command-panel-spec.coffee +++ b/src/packages/command-panel/spec/command-panel-spec.coffee @@ -11,7 +11,7 @@ describe "CommandPanel", -> rootView.enableKeymap() editSession = rootView.getActivePaneItem() buffer = editSession.buffer - commandPanelMain = window.loadPackage('command-panel', activateImmediately: true).mainModule + commandPanelMain = atom.activatePackage('command-panel', immediate: true).mainModule commandPanel = commandPanelMain.commandPanelView commandPanel.history = [] commandPanel.historyIndex = 0 @@ -28,11 +28,8 @@ describe "CommandPanel", -> rootView.trigger 'command-panel:toggle' expect(commandPanel.miniEditor.isFocused).toBeTruthy() - rootViewState = rootView.serialize() - rootView.deactivate() - window.rootView = RootView.deserialize(rootViewState) - rootView.attachToDom() - window.loadPackage('command-panel') + atom.deactivatePackage('command-panel') + atom.activatePackage('command-panel') expect(rootView.find('.command-panel')).not.toExist() rootView.trigger 'command-panel:toggle' @@ -55,13 +52,12 @@ describe "CommandPanel", -> expect(commandPanel.history[2]).toBe('/test3') expect(commandPanel.historyIndex).toBe(3) - rootViewState = rootView.serialize() - rootView.deactivate() - RootView.deserialize(rootViewState).attachToDom() - window.loadPackage('command-panel') - rootView.trigger 'command-panel:toggle' + atom.deactivatePackage('command-panel') + atom.activatePackage('command-panel') + rootView.trigger 'command-panel:toggle' commandPanel = rootView.find('.command-panel').view() + expect(commandPanel.history.length).toBe(2) expect(commandPanel.history[0]).toBe('/test2') expect(commandPanel.history[1]).toBe('/test3') @@ -216,39 +212,69 @@ describe "CommandPanel", -> expect(commandPanel.miniEditor.hiddenInput).not.toMatchSelector ':focus' describe "when command-panel:repeat-relative-address is triggered on the root view", -> - it "repeats the last search command if there is one", -> - rootView.trigger 'command-panel:repeat-relative-address' + describe "when there is more than one match", -> + it "repeats the last search command if there is one", -> + rootView.trigger 'command-panel:repeat-relative-address' - editSession.setCursorScreenPosition([4, 0]) + editSession.setCursorScreenPosition([4, 0]) - commandPanel.execute("/current") - expect(editSession.getSelectedBufferRange()).toEqual [[5,6], [5,13]] + commandPanel.execute("/current") + expect(editSession.getSelectedBufferRange()).toEqual [[5,6], [5,13]] - rootView.trigger 'command-panel:repeat-relative-address' - expect(editSession.getSelectedBufferRange()).toEqual [[6,6], [6,13]] + rootView.trigger 'command-panel:repeat-relative-address' + expect(editSession.getSelectedBufferRange()).toEqual [[6,6], [6,13]] - commandPanel.execute('s/r/R/g') + commandPanel.execute('s/r/R/g') - rootView.trigger 'command-panel:repeat-relative-address' - expect(editSession.getSelectedBufferRange()).toEqual [[6,34], [6,41]] + rootView.trigger 'command-panel:repeat-relative-address' + expect(editSession.getSelectedBufferRange()).toEqual [[6,34], [6,41]] - commandPanel.execute('0') - commandPanel.execute('/sort/ s/r/R/') # this contains a substitution... won't be repeated + commandPanel.execute('0') + commandPanel.execute('/sort/ s/r/R/') # this contains a substitution... won't be repeated - rootView.trigger 'command-panel:repeat-relative-address' - expect(editSession.getSelectedBufferRange()).toEqual [[3,31], [3,38]] + rootView.trigger 'command-panel:repeat-relative-address' + expect(editSession.getSelectedBufferRange()).toEqual [[3,31], [3,38]] + + describe "when there is only one match and it is selected", -> + it "maintains the current selection and plays a beep", -> + editSession.setCursorScreenPosition([0, 0]) + waitsForPromise -> + commandPanel.execute("/Array") + runs -> + expect(editSession.getSelectedBufferRange()).toEqual [[11,14], [11,19]] + spyOn($native, 'beep') + rootView.trigger 'command-panel:repeat-relative-address' + waitsFor -> + $native.beep.callCount > 0 + runs -> + expect(editSession.getSelectedBufferRange()).toEqual [[11,14], [11,19]] describe "when command-panel:repeat-relative-address-in-reverse is triggered on the root view", -> - it "it repeats the last relative address in the reverse direction", -> - rootView.trigger 'command-panel:repeat-relative-address-in-reverse' + describe "when there is more than one match", -> + it "it repeats the last relative address in the reverse direction", -> + rootView.trigger 'command-panel:repeat-relative-address-in-reverse' - editSession.setCursorScreenPosition([6, 0]) + editSession.setCursorScreenPosition([6, 0]) - commandPanel.execute("/current") - expect(editSession.getSelectedBufferRange()).toEqual [[6,6], [6,13]] + commandPanel.execute("/current") + expect(editSession.getSelectedBufferRange()).toEqual [[6,6], [6,13]] - rootView.trigger 'command-panel:repeat-relative-address-in-reverse' - expect(editSession.getSelectedBufferRange()).toEqual [[5,6], [5,13]] + rootView.trigger 'command-panel:repeat-relative-address-in-reverse' + expect(editSession.getSelectedBufferRange()).toEqual [[5,6], [5,13]] + + describe "when there is only one match and it is selected", -> + it "maintains the current selection and plays a beep", -> + editSession.setCursorScreenPosition([0, 0]) + waitsForPromise -> + commandPanel.execute("/Array") + runs -> + expect(editSession.getSelectedBufferRange()).toEqual [[11,14], [11,19]] + spyOn($native, 'beep') + rootView.trigger 'command-panel:repeat-relative-address-in-reverse' + waitsFor -> + $native.beep.callCount > 0 + runs -> + expect(editSession.getSelectedBufferRange()).toEqual [[11,14], [11,19]] describe "when command-panel:set-selection-as-regex-address is triggered on the root view", -> it "sets the @lastRelativeAddress to a RegexAddress of the current selection", -> @@ -319,13 +345,15 @@ describe "CommandPanel", -> # there shouldn't be any dangling operations after this describe "if the command is malformed", -> - it "adds and removes an error class to the command panel and does not close it", -> + it "adds and removes an error class to the command panel and does not close it or display a loading message", -> + rootView.attachToDom() rootView.trigger 'command-panel:toggle' commandPanel.miniEditor.insertText 'garbage-command!!' commandPanel.miniEditor.hiddenInput.trigger keydownEvent('enter') expect(commandPanel.parent()).toExist() expect(commandPanel).toHaveClass 'error' + expect(commandPanel.loadingMessage).toBeHidden() advanceClock 400 diff --git a/src/packages/command-panel/spec/preview-list-spec.coffee b/src/packages/command-panel/spec/preview-list-spec.coffee index 4fecb0f2d..ce91de176 100644 --- a/src/packages/command-panel/spec/preview-list-spec.coffee +++ b/src/packages/command-panel/spec/preview-list-spec.coffee @@ -8,7 +8,7 @@ describe "Preview List", -> beforeEach -> window.rootView = new RootView() rootView.attachToDom() - commandPanelMain = window.loadPackage('command-panel', activateImmediately: true).mainModule + commandPanelMain = atom.activatePackage('command-panel', immediate: true).mainModule commandPanelView = commandPanelMain.commandPanelView previewList = commandPanelView.previewList rootView.trigger 'command-panel:toggle' diff --git a/src/packages/editor-stats/spec/editor-stats-spec.coffee b/src/packages/editor-stats/spec/editor-stats-spec.coffee index 9ce2360a2..582f99cfe 100644 --- a/src/packages/editor-stats/spec/editor-stats-spec.coffee +++ b/src/packages/editor-stats/spec/editor-stats-spec.coffee @@ -17,7 +17,7 @@ describe "EditorStats", -> beforeEach -> window.rootView = new RootView rootView.open('sample.js') - editorStats = window.loadPackage('editor-stats').mainModule.stats + editorStats = atom.activatePackage('editor-stats').mainModule.stats describe "when a keyup event is triggered", -> beforeEach -> diff --git a/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee b/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee index 330e50fb3..b49f096da 100644 --- a/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee +++ b/src/packages/fuzzy-finder/lib/fuzzy-finder-view.coffee @@ -163,8 +163,10 @@ class FuzzyFinderView extends SelectList @loadPathsTask.start() populateOpenBufferPaths: -> - editSessions = project.getEditSessions().filter (editSession)-> + editSessions = project.getEditSessions().filter (editSession) -> editSession.getPath()? + editSessions = _.uniq editSessions, (editSession) -> + editSession.getPath() editSessions = _.sortBy editSessions, (editSession) => if editSession is rootView.getActivePaneItem() diff --git a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee index 92b91d792..15c89721e 100644 --- a/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee +++ b/src/packages/fuzzy-finder/spec/fuzzy-finder-spec.coffee @@ -13,7 +13,7 @@ describe 'FuzzyFinder', -> window.rootView = new RootView rootView.open('sample.js') rootView.enableKeymap() - finderView = window.loadPackage("fuzzy-finder").mainModule.createView() + finderView = atom.activatePackage("fuzzy-finder").mainModule.createView() describe "file-finder behavior", -> describe "toggling", -> @@ -143,8 +143,8 @@ describe 'FuzzyFinder', -> rootView.trigger 'fuzzy-finder:toggle-buffer-finder' rootView.open() - states = rootView.serialize().packages - states = _.map states['fuzzy-finder'], (path, time) -> [ path, time ] + atom.deactivatePackage('fuzzy-finder') + states = _.map atom.getPackageState('fuzzy-finder'), (path, time) -> [ path, time ] states = _.sortBy states, (path, time) -> -time paths = [ 'sample-with-tabs.coffee', 'sample.txt', 'sample.js' ] @@ -166,6 +166,13 @@ describe 'FuzzyFinder', -> rootView.trigger 'fuzzy-finder:toggle-buffer-finder' expect(rootView.find('.fuzzy-finder')).not.toExist() + describe "when multiple sessions are opened on the same path", -> + it "does not display duplicates for that path in the list", -> + rootView.open 'sample.js' + rootView.getActivePane().splitRight() + rootView.trigger 'fuzzy-finder:toggle-buffer-finder' + expect(_.pluck(finderView.list.children('li'), 'outerText')).toEqual ['sample.js'] + describe "when a path selection is confirmed", -> [editor1, editor2] = [] diff --git a/src/packages/gfm/spec/gfm-spec.coffee b/src/packages/gfm/spec/gfm-spec.coffee index 484835ae9..ea579187e 100644 --- a/src/packages/gfm/spec/gfm-spec.coffee +++ b/src/packages/gfm/spec/gfm-spec.coffee @@ -5,7 +5,7 @@ describe "GitHub Flavored Markdown grammar", -> beforeEach -> spyOn(syntax, "addGrammar").andCallThrough() - window.loadPackage("gfm") + atom.activatePackage("gfm") expect(syntax.addGrammar).toHaveBeenCalled() grammar = syntax.addGrammar.argsForCall[0][0] diff --git a/src/packages/gists/lib/gists.coffee b/src/packages/gists/lib/gists.coffee index 6df96a01d..cada1d582 100644 --- a/src/packages/gists/lib/gists.coffee +++ b/src/packages/gists/lib/gists.coffee @@ -8,7 +8,7 @@ class Gists constructor: -> rootView.command 'gist:create', '.editor', => @createGist() - createGist: (editor) -> + createGist: -> editor = rootView.getActiveView() return unless editor? diff --git a/src/packages/gists/spec/gists-spec.coffee b/src/packages/gists/spec/gists-spec.coffee index 3091f8ed6..58866b892 100644 --- a/src/packages/gists/spec/gists-spec.coffee +++ b/src/packages/gists/spec/gists-spec.coffee @@ -7,7 +7,7 @@ describe "Gists package", -> beforeEach -> window.rootView = new RootView rootView.open('sample.js') - window.loadPackage('gists') + atom.activatePackage('gists') editor = rootView.getActiveView() spyOn($, 'ajax') diff --git a/src/packages/grammar-selector/keymaps/grammar-selector.cson b/src/packages/grammar-selector/keymaps/grammar-selector.cson new file mode 100644 index 000000000..07e5050bd --- /dev/null +++ b/src/packages/grammar-selector/keymaps/grammar-selector.cson @@ -0,0 +1,3 @@ +'.editor': + 'meta-L': 'grammar-selector:show' + diff --git a/src/app/grammar-view.coffee b/src/packages/grammar-selector/lib/grammar-selector.coffee similarity index 74% rename from src/app/grammar-view.coffee rename to src/packages/grammar-selector/lib/grammar-selector.coffee index 5c1638057..ca0aa828b 100644 --- a/src/app/grammar-view.coffee +++ b/src/packages/grammar-selector/lib/grammar-selector.coffee @@ -1,18 +1,23 @@ SelectList = require 'select-list' +Editor = require 'editor' {$$} = require 'space-pen' module.exports = -class GrammarView extends SelectList +class GrammarSelector extends SelectList + @viewClass: -> "#{super} grammar-selector from-top overlay mini" - @viewClass: -> "#{super} grammar-view from-top overlay mini" + @activate: -> + rootView.command 'grammar-selector:show', '.editor', => new GrammarSelector() filterKey: 'name' - initialize: (@editor) -> + initialize: -> + @editor = rootView.getActiveView() + return unless @editor instanceof Editor @currentGrammar = @editor.getGrammar() @path = @editor.getPath() @autoDetect = name: 'Auto Detect' - @command 'editor:select-grammar', => + @command 'grammar-selector:show', => @cancel() false super @@ -55,6 +60,5 @@ class GrammarView extends SelectList attach: -> super - rootView.append(this) - @miniEditor.focus() + @miniEditor.focus() \ No newline at end of file diff --git a/src/packages/grammar-selector/package.cson b/src/packages/grammar-selector/package.cson new file mode 100644 index 000000000..99390e3bc --- /dev/null +++ b/src/packages/grammar-selector/package.cson @@ -0,0 +1,2 @@ +'main': 'lib/grammar-selector' +'activationEvents': ['grammar-selector:show'] diff --git a/spec/app/grammar-view-spec.coffee b/src/packages/grammar-selector/spec/grammar-selector-spec.coffee similarity index 63% rename from spec/app/grammar-view-spec.coffee rename to src/packages/grammar-selector/spec/grammar-selector-spec.coffee index 45157aab0..db4fa262c 100644 --- a/spec/app/grammar-view-spec.coffee +++ b/src/packages/grammar-selector/spec/grammar-selector-spec.coffee @@ -1,12 +1,15 @@ +GrammarSelector = require '../lib/grammar-selector' RootView = require 'root-view' -GrammarView = require 'grammar-view' _ = require 'underscore' -describe "GrammarView", -> +describe "GrammarSelector", -> [editor, textGrammar, jsGrammar] = [] beforeEach -> window.rootView = new RootView + atom.activatePackage('grammar-selector') + atom.activatePackage('text.tmbundle', sync: true) + atom.activatePackage('javascript.tmbundle', sync: true) rootView.open('sample.js') editor = rootView.getActiveView() textGrammar = _.find syntax.grammars, (grammar) -> grammar.name is 'Plain Text' @@ -15,10 +18,10 @@ describe "GrammarView", -> expect(jsGrammar).toBeTruthy() expect(editor.getGrammar()).toBe jsGrammar - describe "when editor:select-grammar is toggled", -> + describe "when grammar-selector:show is triggered", -> it "displays a list of all the available grammars", -> - editor.trigger 'editor:select-grammar' - grammarView = rootView.find('.grammar-view').view() + editor.trigger 'grammar-selector:show' + grammarView = rootView.find('.grammar-selector').view() expect(grammarView).toExist() grammars = syntax.grammars expect(grammarView.list.children('li').length).toBe grammars.length + 1 @@ -26,19 +29,19 @@ describe "GrammarView", -> describe "when a grammar is selected", -> it "sets the new grammar on the editor", -> - editor.trigger 'editor:select-grammar' - grammarView = rootView.find('.grammar-view').view() + editor.trigger 'grammar-selector:show' + grammarView = rootView.find('.grammar-selector').view() grammarView.confirmed(textGrammar) expect(editor.getGrammar()).toBe textGrammar describe "when auto-detect is selected", -> it "restores the auto-detected grammar on the editor", -> - editor.trigger 'editor:select-grammar' - grammarView = rootView.find('.grammar-view').view() + editor.trigger 'grammar-selector:show' + grammarView = rootView.find('.grammar-selector').view() grammarView.confirmed(textGrammar) expect(editor.getGrammar()).toBe textGrammar - editor.trigger 'editor:select-grammar' - grammarView = rootView.find('.grammar-view').view() + editor.trigger 'grammar-selector:show' + grammarView = rootView.find('.grammar-selector').view() grammarView.confirmed(grammarView.array[0]) expect(editor.getGrammar()).toBe jsGrammar diff --git a/src/packages/markdown-preview/lib/markdown-preview.coffee b/src/packages/markdown-preview/lib/markdown-preview.coffee index b30bbb621..457984f1e 100644 --- a/src/packages/markdown-preview/lib/markdown-preview.coffee +++ b/src/packages/markdown-preview/lib/markdown-preview.coffee @@ -22,4 +22,4 @@ module.exports = nextPane.showItem(new MarkdownPreviewView(editSession.buffer)) else activePane.splitRight(new MarkdownPreviewView(editSession.buffer)) - activePane.focus() \ No newline at end of file + activePane.focus() diff --git a/src/packages/markdown-preview/spec/markdown-preview-spec.coffee b/src/packages/markdown-preview/spec/markdown-preview-spec.coffee index 9a7ed6e33..1bcf984ad 100644 --- a/src/packages/markdown-preview/spec/markdown-preview-spec.coffee +++ b/src/packages/markdown-preview/spec/markdown-preview-spec.coffee @@ -6,7 +6,7 @@ describe "MarkdownPreview package", -> beforeEach -> project.setPath(project.resolve('markdown')) window.rootView = new RootView - window.loadPackage("markdown-preview", activateImmediately: true) + atom.activatePackage("markdown-preview", immediate: true) spyOn(MarkdownPreviewView.prototype, 'fetchRenderedMarkdown') describe "markdown-preview:show", -> diff --git a/src/packages/package-generator/spec/package-generator-spec.coffee b/src/packages/package-generator/spec/package-generator-spec.coffee index 5df9d71c6..87d57bbdd 100644 --- a/src/packages/package-generator/spec/package-generator-spec.coffee +++ b/src/packages/package-generator/spec/package-generator-spec.coffee @@ -7,7 +7,7 @@ describe 'Package Generator', -> beforeEach -> window.rootView = new RootView rootView.open('sample.js') - window.loadPackage("package-generator") + atom.activatePackage("package-generator") describe "when package-generator:generate is triggered", -> it "displays a miniEditor", -> diff --git a/src/packages/package-generator/template/spec/__package-name__-view-spec.coffee.template b/src/packages/package-generator/template/spec/__package-name__-view-spec.coffee.template index a8b62caf7..dc342839d 100644 --- a/src/packages/package-generator/template/spec/__package-name__-view-spec.coffee.template +++ b/src/packages/package-generator/template/spec/__package-name__-view-spec.coffee.template @@ -10,7 +10,7 @@ fdescribe "__PackageName__View", -> beforeEach -> window.rootView = new RootView - __packageName__ = window.loadPackage('__packageName__', activateImmediately: true) + __packageName__ = atom.activatePackage('__packageName__', immediate: true) describe "when the __package-name__:toggle event is triggered", -> it "attaches and then detaches the view", -> diff --git a/src/packages/snippets/spec/snippets-spec.coffee b/src/packages/snippets/spec/snippets-spec.coffee index c02a85f74..2236757e7 100644 --- a/src/packages/snippets/spec/snippets-spec.coffee +++ b/src/packages/snippets/spec/snippets-spec.coffee @@ -9,16 +9,14 @@ Package = require 'package' describe "Snippets extension", -> [buffer, editor, editSession] = [] beforeEach -> + atom.activatePackage('javascript.tmbundle', sync: true) window.rootView = new RootView rootView.open('sample.js') - packageWithSnippets = window.loadPackage("package-with-snippets") - spyOn(atom, "getLoadedPackages").andCallFake -> - textMatePackages = window.textMatePackages.filter (pack) -> /package-with-a-cson-grammar|test|textmate-package|javascript/.test(pack.name) - textMatePackages.concat([packageWithSnippets]) + packageWithSnippets = atom.loadPackage("package-with-snippets") spyOn(require("snippets/lib/snippets"), 'loadAll') - window.loadPackage("snippets") + atom.activatePackage("snippets") editor = rootView.getActiveView() editSession = rootView.getActivePaneItem() @@ -238,6 +236,9 @@ describe "Snippets extension", -> describe "snippet loading", -> beforeEach -> + atom.loadPackage('package-with-broken-snippets.tmbundle', sync: true) + atom.loadPackage('package-with-snippets') + jasmine.unspy(window, "setTimeout") jasmine.unspy(snippets, 'loadAll') spyOn(snippets, 'loadAtomSnippets').andCallFake (path, done) -> done() diff --git a/src/packages/spell-check/lib/misspelling-view.coffee b/src/packages/spell-check/lib/misspelling-view.coffee index ba57e4164..0940ce0f7 100644 --- a/src/packages/spell-check/lib/misspelling-view.coffee +++ b/src/packages/spell-check/lib/misspelling-view.coffee @@ -1,6 +1,7 @@ {View} = require 'space-pen' Range = require 'range' CorrectionsView = require './corrections-view' +SpellChecker = require 'spellchecker' module.exports = class MisspellingView extends View @@ -30,7 +31,7 @@ class MisspellingView extends View screenRange = @getScreenRange() misspelling = @editor.getTextInRange(@editor.bufferRangeForScreenRange(screenRange)) - corrections = $native.getCorrectionsForMisspelling(misspelling) + corrections = SpellChecker.getCorrectionsForMisspelling(misspelling) @correctionsView?.remove() @correctionsView = new CorrectionsView(@editor, corrections, screenRange) diff --git a/src/packages/spell-check/lib/spell-check-handler.coffee b/src/packages/spell-check/lib/spell-check-handler.coffee index 8040f4991..0b21f73ab 100644 --- a/src/packages/spell-check/lib/spell-check-handler.coffee +++ b/src/packages/spell-check/lib/spell-check-handler.coffee @@ -1,3 +1,5 @@ +SpellChecker = require 'spellchecker' + module.exports = findMisspellings: (text) -> wordRegex = /(?:^|[\s\[\]])([a-zA-Z']+)(?=[\s\.\[\]]|$)/g @@ -6,7 +8,7 @@ module.exports = for line in text.split('\n') while matches = wordRegex.exec(line) word = matches[1] - continue unless $native.isMisspelled(word) + continue unless SpellChecker.isMisspelled(word) startColumn = matches.index + matches[0].length - word.length endColumn = startColumn + word.length misspellings.push([[row, startColumn], [row, endColumn]]) diff --git a/src/packages/spell-check/spec/spell-check-spec.coffee b/src/packages/spell-check/spec/spell-check-spec.coffee index 6ccf41df5..5ac927d96 100644 --- a/src/packages/spell-check/spec/spell-check-spec.coffee +++ b/src/packages/spell-check/spec/spell-check-spec.coffee @@ -4,10 +4,12 @@ describe "Spell check", -> [editor] = [] beforeEach -> + atom.activatePackage('text.tmbundle', sync: true) + atom.activatePackage('javascript.tmbundle', sync: true) window.rootView = new RootView rootView.open('sample.js') config.set('spell-check.grammars', []) - window.loadPackage('spell-check', activateImmediately: true) + atom.activatePackage('spell-check', immediate: true) syntax.trigger 'grammars-loaded' rootView.attachToDom() editor = rootView.getActiveView() diff --git a/src/packages/status-bar/lib/status-bar-view.coffee b/src/packages/status-bar/lib/status-bar-view.coffee index 152136a0d..ecae8af28 100644 --- a/src/packages/status-bar/lib/status-bar-view.coffee +++ b/src/packages/status-bar/lib/status-bar-view.coffee @@ -34,7 +34,7 @@ class StatusBarView extends View @updateCursorPositionText() @subscribe @editor, 'cursor:moved', => @updateCursorPositionText() - @subscribe @grammarName, 'click', => @editor.trigger 'editor:select-grammar' + @subscribe @grammarName, 'click', => @editor.trigger 'grammar-selector:show' @subscribe @editor, 'editor:grammar-changed', => @updateGrammarText() if git? @subscribe git, 'status-changed', (path, status) => diff --git a/src/packages/status-bar/spec/status-bar-spec.coffee b/src/packages/status-bar/spec/status-bar-spec.coffee index 2e03eec13..497a4143d 100644 --- a/src/packages/status-bar/spec/status-bar-spec.coffee +++ b/src/packages/status-bar/spec/status-bar-spec.coffee @@ -32,7 +32,7 @@ describe "StatusBar", -> describe "when associated with an unsaved buffer", -> it "displays 'untitled' instead of the buffer's path, but still displays the buffer position", -> - rootView.deactivate() + rootView.remove() window.rootView = new RootView rootView.open() rootView.simulateDomAttachment() @@ -180,6 +180,11 @@ describe "StatusBar", -> expect(statusBar.gitStatusIcon).toHaveText('+1') describe "grammar label", -> + beforeEach -> + atom.activatePackage('text.tmbundle', sync: true) + atom.activatePackage('javascript.tmbundle', sync: true) + syntax.trigger 'grammars-loaded' + it "displays the name of the current grammar", -> expect(statusBar.find('.grammar-name').text()).toBe 'JavaScript' @@ -200,8 +205,8 @@ describe "StatusBar", -> expect(statusBar.find('.grammar-name').text()).toBe 'Plain Text' describe "when clicked", -> - it "toggles the editor:select-grammar event", -> + it "toggles the grammar-selector:show event", -> eventHandler = jasmine.createSpy('eventHandler') - editor.on 'editor:select-grammar', eventHandler + editor.on 'grammar-selector:show', eventHandler statusBar.find('.grammar-name').click() expect(eventHandler).toHaveBeenCalled() diff --git a/src/packages/strip-trailing-whitespace/spec/strip-trailing-whitespace-spec.coffee b/src/packages/strip-trailing-whitespace/spec/strip-trailing-whitespace-spec.coffee index 9a405ddd2..e41d2d3ab 100644 --- a/src/packages/strip-trailing-whitespace/spec/strip-trailing-whitespace-spec.coffee +++ b/src/packages/strip-trailing-whitespace/spec/strip-trailing-whitespace-spec.coffee @@ -10,7 +10,7 @@ describe "StripTrailingWhitespace", -> window.rootView = new RootView rootView.open(path) - window.loadPackage('strip-trailing-whitespace') + atom.activatePackage('strip-trailing-whitespace') rootView.focus() editor = rootView.getActiveView() diff --git a/src/packages/symbols-view/spec/symbols-view-spec.coffee b/src/packages/symbols-view/spec/symbols-view-spec.coffee index db8876855..7401a4448 100644 --- a/src/packages/symbols-view/spec/symbols-view-spec.coffee +++ b/src/packages/symbols-view/spec/symbols-view-spec.coffee @@ -8,7 +8,7 @@ describe "SymbolsView", -> beforeEach -> window.rootView = new RootView - window.loadPackage("symbols-view") + atom.activatePackage("symbols-view") rootView.attachToDom() setArraySpy = spyOn(SymbolsView.prototype, 'setArray').andCallThrough() diff --git a/src/packages/tabs/spec/tabs-spec.coffee b/src/packages/tabs/spec/tabs-spec.coffee index 0a31a02b0..820f3f9eb 100644 --- a/src/packages/tabs/spec/tabs-spec.coffee +++ b/src/packages/tabs/spec/tabs-spec.coffee @@ -11,7 +11,7 @@ describe "Tabs package main", -> beforeEach -> window.rootView = new RootView rootView.open('sample.js') - window.loadPackage("tabs") + atom.activatePackage("tabs") describe ".activate()", -> it "appends a tab bar all existing and new panes", -> diff --git a/src/packages/toml/spec/toml-spec.coffee b/src/packages/toml/spec/toml-spec.coffee index c9d07a4f6..656b99673 100644 --- a/src/packages/toml/spec/toml-spec.coffee +++ b/src/packages/toml/spec/toml-spec.coffee @@ -5,7 +5,7 @@ describe "TOML grammar", -> beforeEach -> spyOn(syntax, "addGrammar") - window.loadPackage("toml") + atom.activatePackage("toml") expect(syntax.addGrammar).toHaveBeenCalled() grammar = syntax.addGrammar.argsForCall[0][0] diff --git a/src/packages/tree-view/lib/tree-view.coffee b/src/packages/tree-view/lib/tree-view.coffee index e6421679f..18475fa06 100644 --- a/src/packages/tree-view/lib/tree-view.coffee +++ b/src/packages/tree-view/lib/tree-view.coffee @@ -40,8 +40,8 @@ class TreeView extends ScrollView else @selectActiveFile() - rootView.on 'pane:active-item-changed pane:became-active', => @selectActiveFile() - project.on 'path-changed', => @updateRoot() + rootView.on 'pane:active-item-changed.tree-view pane:became-active.tree-view', => @selectActiveFile() + project.on 'path-changed.tree-view', => @updateRoot() @observeConfig 'core.hideGitIgnoredFiles', => @updateRoot() if @root @@ -67,6 +67,9 @@ class TreeView extends ScrollView deactivate: -> @root?.unwatchEntries() + rootView.off('.tree-view') + project.off('.tree-view') + @remove() toggle: -> if @hasFocus() diff --git a/src/packages/tree-view/spec/tree-view-spec.coffee b/src/packages/tree-view/spec/tree-view-spec.coffee index dc7645ed2..f5f01aace 100644 --- a/src/packages/tree-view/spec/tree-view-spec.coffee +++ b/src/packages/tree-view/spec/tree-view-spec.coffee @@ -13,7 +13,7 @@ describe "TreeView", -> project.setPath(project.resolve('tree-view')) window.rootView = new RootView - window.loadPackage("tree-view") + atom.activatePackage("tree-view") rootView.trigger 'tree-view:toggle' treeView = rootView.find(".tree-view").view() treeView.root = treeView.find('ol > li:first').view() @@ -47,10 +47,8 @@ describe "TreeView", -> describe "when the project has no path", -> beforeEach -> project.setPath(undefined) - rootView.deactivate() - window.rootView = new RootView() - rootView.open() - treeView = window.loadPackage("tree-view").mainModule.createView() + atom.deactivatePackage("tree-view") + treeView = atom.activatePackage("tree-view").mainModule.createView() it "does not attach to the root view or create a root node when initialized", -> expect(treeView.hasParent()).toBeFalsy() @@ -66,6 +64,7 @@ describe "TreeView", -> describe "when the project is assigned a path because a new buffer is saved", -> it "creates a root directory view but does not attach to the root view", -> + rootView.open() rootView.getActivePaneItem().saveAs("/tmp/test.txt") expect(treeView.hasParent()).toBeFalsy() expect(treeView.root.getPath()).toBe '/tmp' @@ -73,16 +72,16 @@ describe "TreeView", -> describe "when the root view is opened to a file path", -> it "does not attach to the root view but does create a root node when initialized", -> - rootView.deactivate() - window.rootView = new RootView + atom.deactivatePackage("tree-view") + atom.packageStates = {} rootView.open('tree-view.js') - treeView = window.loadPackage("tree-view").mainModule.createView() + treeView = atom.activatePackage("tree-view").mainModule.createView() expect(treeView.hasParent()).toBeFalsy() expect(treeView.root).toExist() describe "when the root view is opened to a directory", -> it "attaches to the root view", -> - treeView = window.loadPackage("tree-view").mainModule.createView() + treeView = atom.activatePackage("tree-view").mainModule.createView() expect(treeView.hasParent()).toBeTruthy() expect(treeView.root).toExist() @@ -91,10 +90,8 @@ describe "TreeView", -> treeView.find('.directory:contains(dir1)').click() sampleJs.click() - rootViewState = rootView.serialize() - rootView.deactivate() - window.rootView = RootView.deserialize(rootViewState) - window.loadPackage("tree-view") + atom.deactivatePackage("tree-view") + atom.activatePackage("tree-view") treeView = rootView.find(".tree-view").view() expect(treeView).toExist() @@ -105,13 +102,8 @@ describe "TreeView", -> rootView.attachToDom() treeView.focus() expect(treeView.find(".tree-view")).toMatchSelector ':focus' - - rootViewState = rootView.serialize() - rootView.deactivate() - window.rootView = RootView.deserialize(rootViewState) - - rootView.attachToDom() - window.loadPackage("tree-view") + atom.deactivatePackage("tree-view") + atom.activatePackage("tree-view") treeView = rootView.find(".tree-view").view() expect(treeView.find(".tree-view")).toMatchSelector ':focus' @@ -599,7 +591,7 @@ describe "TreeView", -> [dirView, fileView, rootDirPath, dirPath, filePath] = [] beforeEach -> - rootView.deactivate() + atom.deactivatePackage('tree-view') rootDirPath = fs.join(fs.absolute("/tmp"), "atom-tests") fs.remove(rootDirPath) if fs.exists(rootDirPath) @@ -611,8 +603,8 @@ describe "TreeView", -> fs.write(filePath, "doesn't matter") project.setPath(rootDirPath) - window.rootView = new RootView(rootDirPath) - window.loadPackage('tree-view') + + atom.activatePackage('tree-view') rootView.trigger 'tree-view:toggle' treeView = rootView.find(".tree-view").view() dirView = treeView.root.entries.find('.directory:contains(test-dir)').view() diff --git a/src/packages/wrap-guide/spec/wrap-guide-spec.coffee b/src/packages/wrap-guide/spec/wrap-guide-spec.coffee index ff14f7c27..cd0761881 100644 --- a/src/packages/wrap-guide/spec/wrap-guide-spec.coffee +++ b/src/packages/wrap-guide/spec/wrap-guide-spec.coffee @@ -6,7 +6,7 @@ describe "WrapGuide", -> beforeEach -> window.rootView = new RootView rootView.open('sample.js') - window.loadPackage('wrap-guide') + atom.activatePackage('wrap-guide') rootView.attachToDom() editor = rootView.getActiveView() wrapGuide = rootView.find('.wrap-guide').view() diff --git a/src/stdlib/task-shell.coffee b/src/stdlib/task-shell.coffee index bab99dc7c..6ac2d5f6e 100644 --- a/src/stdlib/task-shell.coffee +++ b/src/stdlib/task-shell.coffee @@ -1,17 +1,17 @@ -# This file is loaded within Task's worker thread. It will attempt to invoke +# This file is loaded within Task's worker process. It will attempt to invoke # any message with a 'method' and 'args' key on the global `handler` object. The # initial `handler` object contains the `start` method, which is called by the # task itself to relay information from the window thread and bootstrap the # worker's environment. The `start` method then replaces the handler with an # object required from the given `handlerPath`. -self.window = {} -self.attachEvent = -> +global.window = {} +global.attachEvent = -> console = warn: -> callTaskMethod 'warn', arguments... log: -> callTaskMethod 'log', arguments... error: -> callTaskMethod 'error', arguments... -self.__defineGetter__ 'console', -> console +global.__defineGetter__ 'console', -> console window.document = createElement: -> @@ -24,21 +24,21 @@ window.document = getElementById: -> {} createComment: -> {} createDocumentFragment: -> {} -self.document = window.document +global.document = window.document # `callTaskMethod` can be used to invoke method's on the parent `Task` object # back in the window thread. -self.callTaskMethod = (method, args...) -> - postMessage(method: method, args: args) +global.callTaskMethod = (method, args...) -> + process.send(method: method, args: args) -# The worker's initial handler replaces itself when `start` is invoked -self.handler = +# The worker's initial handler replaces itglobal when `start` is invoked +global.handler = start: ({globals, handlerPath}) -> for key, value of globals - self[key] = value + global[key] = value window[key] = value - self.handler = require(handlerPath) + global.handler = require(handlerPath) callTaskMethod 'started' -self.addEventListener 'message', ({data}) -> +process.on 'message', (data) -> handler[data.method]?(data.args...) if data.method diff --git a/src/stdlib/task.coffee b/src/stdlib/task.coffee index 636be9e6f..8216a4f15 100644 --- a/src/stdlib/task.coffee +++ b/src/stdlib/task.coffee @@ -1,4 +1,5 @@ _ = require 'underscore' +child_process = require 'child_process' EventEmitter = require 'event-emitter' fs = require 'fs-utils' @@ -11,9 +12,11 @@ class Task start: -> throw new Error("Task already started") if @worker? - blob = new Blob(["require('coffee-script'); require('task-shell');"], type: 'text/javascript') - @worker = new Worker(URL.createObjectURL(blob)) - @worker.onmessage = ({data}) => + # Equivalent with node --eval "...". + blob = "require('coffee-script'); require('task-shell');" + @worker = child_process.fork '--eval', [ blob ], cwd: __dirname + + @worker.on 'message', (data) => if @aborted @done() return @@ -22,6 +25,7 @@ class Task this[data.method](data.args...) else @onMessage(data) + @startWorker() log: -> console.log(arguments...) @@ -43,14 +47,14 @@ class Task @postMessage({method, args}) postMessage: (data) -> - @worker.postMessage(data) + @worker.send(data) abort: -> @aborted = true done: -> @abort() - @worker?.terminate() + @worker?.kill() @worker = null @trigger 'task-completed' diff --git a/vendor/space-pen.coffee b/vendor/space-pen.coffee deleted file mode 100644 index f734350e0..000000000 --- a/vendor/space-pen.coffee +++ /dev/null @@ -1,208 +0,0 @@ -# Modified from d9b6b4a2fe3fdeca07bb82e14e412718540f89f3 -$ = jQuery = require('jquery') - -elements = - 'a abbr address article aside audio b bdi bdo blockquote body button - canvas caption cite code colgroup datalist dd del details dfn div dl dt em - fieldset figcaption figure footer form h1 h2 h3 h4 h5 h6 head header hgroup - html i iframe ins kbd label legend li map mark menu meter nav noscript object - ol optgroup option output p pre progress q rp rt ruby s samp script section - select small span strong style sub summary sup table tbody td textarea tfoot - th thead time title tr u ul video area base br col command embed hr img input - keygen link meta param source track wbrk'.split /\s+/ - -voidElements = - 'area base br col command embed hr img input keygen link meta param - source track wbr'.split /\s+/ - -events = - 'blur change click dblclick error focus input keydown - keypress keyup load mousedown mousemove mouseout mouseover - mouseup resize scroll select submit unload'.split /\s+/ - -idCounter = 0 - -class View extends jQuery - @builderStack: [] - - elements.forEach (tagName) -> - View[tagName] = (args...) -> @currentBuilder.tag(tagName, args...) - - @subview: (name, view) -> - @currentBuilder.subview(name, view) - - @text: (string) -> @currentBuilder.text(string) - - @raw: (string) -> @currentBuilder.raw(string) - - @pushBuilder: -> - builder = new Builder - @builderStack.push(builder) - @currentBuilder = builder - - @popBuilder: -> - @currentBuilder = @builderStack[@builderStack.length - 2] - @builderStack.pop() - - @buildHtml: (fn) -> - @pushBuilder() - fn.call(this) - [html, postProcessingSteps] = @popBuilder().buildHtml() - - @render: (fn) -> - [html, postProcessingSteps] = @buildHtml(fn) - div = document.createElement('div') - div.innerHTML = html - fragment = $(div.childNodes) - step(fragment) for step in postProcessingSteps - fragment - - constructor: (args...) -> - [html, postProcessingSteps] = @constructor.buildHtml -> @content(args...) - jQuery.fn.init.call(this, html) - @constructor = jQuery # sadly, jQuery assumes this.constructor == jQuery in pushStack - throw new Error("View markup must have a single root element") if this.length != 1 - @wireOutlets(this) - @bindEventHandlers(this) - @find('*').andSelf().data('view', this) - @attr('callAttachHooks', true) - step(this) for step in postProcessingSteps - @initialize?(args...) - - buildHtml: (params) -> - @constructor.builder = new Builder - @constructor.content(params) - [html, postProcessingSteps] = @constructor.builder.buildHtml() - @constructor.builder = null - postProcessingSteps - - wireOutlets: (view) -> - @find('[outlet]').each -> - element = $(this) - outlet = element.attr('outlet') - view[outlet] = element - element.attr('outlet', null) - - bindEventHandlers: (view) -> - for eventName in events - selector = "[#{eventName}]" - elements = view.find(selector).add(view.filter(selector)) - elements.each -> - element = $(this) - methodName = element.attr(eventName) - element.on eventName, (event) -> view[methodName](event, element) - -class Builder - constructor: -> - @document = [] - @postProcessingSteps = [] - - buildHtml: -> - [@document.join(''), @postProcessingSteps] - - tag: (name, args...) -> - options = @extractOptions(args) - - @openTag(name, options.attributes) - - if name in voidElements - if (options.text? or options.content?) - throw new Error("Self-closing tag #{name} cannot have text or content") - else - options.content?() - @text(options.text) if options.text - @closeTag(name) - - openTag: (name, attributes) -> - attributePairs = - for attributeName, value of attributes - "#{attributeName}=\"#{value}\"" - - attributesString = - if attributePairs.length - " " + attributePairs.join(" ") - else - "" - - @document.push "<#{name}#{attributesString}>" - - closeTag: (name) -> - @document.push "" - - text: (string) -> - escapedString = string - .replace(/&/g, '&') - .replace(/"/g, '"') - .replace(/'/g, ''') - .replace(//g, '>') - - @document.push escapedString - - raw: (string) -> - @document.push string - - subview: (outletName, subview) -> - subviewId = "subview-#{++idCounter}" - @tag 'div', id: subviewId - @postProcessingSteps.push (view) -> - view[outletName] = subview - subview.parentView = view - view.find("div##{subviewId}").replaceWith(subview) - - extractOptions: (args) -> - options = {} - for arg in args - type = typeof(arg) - if type is "function" - options.content = arg - else if type is "string" or type is "number" - options.text = arg.toString() - else - options.attributes = arg - options - -jQuery.fn.view = -> @data('view') -jQuery.fn.views = -> @toArray().map (elt) -> jQuery(elt).view() - -# Trigger attach event when views are added to the DOM -callAttachHook = (element) -> - return unless element - onDom = element.parents?('html').length > 0 - - elementsWithHooks = [] - elementsWithHooks.push(element[0]) if element.attr?('callAttachHooks') - elementsWithHooks = elementsWithHooks.concat(element.find?('[callAttachHooks]').toArray() ? []) if onDom - - parent = element - for element in elementsWithHooks - view = $(element).view() - $(element).view()?.afterAttach?(onDom) - -for methodName in ['append', 'prepend', 'after', 'before'] - do (methodName) -> - originalMethod = $.fn[methodName] - jQuery.fn[methodName] = (args...) -> - flatArgs = [].concat args... - result = originalMethod.apply(this, flatArgs) - callAttachHook arg for arg in flatArgs - result - -for methodName in ['prependTo', 'appendTo', 'insertAfter', 'insertBefore'] - do (methodName) -> - originalMethod = jQuery.fn[methodName] - jQuery.fn[methodName] = (args...) -> - result = originalMethod.apply(this, args) - callAttachHook(this) - result - -originalCleanData = jQuery.cleanData -jQuery.cleanData = (elements) -> - for element in elements - view = $(element).view() - view.afterRemove?() if view and view?[0] == element - originalCleanData(elements) - -(exports ? this).View = View -(exports ? this).$$ = (fn) -> View.render.call(View, fn) -(exports ? this).$$$ = (fn) -> View.buildHtml.call(View, fn)[0]