diff --git a/atom.gyp b/atom.gyp deleted file mode 100644 index 46eff41c9..000000000 --- a/atom.gyp +++ /dev/null @@ -1,14 +0,0 @@ -{ - 'targets': [ - { - 'target_name': 'Atom', - 'type': 'none', - 'postbuilds': [ - { - 'postbuild_name': 'Create Atom, basically do everything', - 'action': ['script/constructicon/build'], - }, - ], - }, - ], -} diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index cdca7392a..9e0f1b4eb 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -151,8 +151,10 @@ module.exports = (grunt) -> 'dot-atom/**/*.coffee' 'exports/**/*.coffee' 'src/**/*.coffee' - 'tasks/**/*.coffee' - 'Gruntfile.coffee' + ] + build: [ + 'build/tasks/**/*.coffee' + 'build/Gruntfile.coffee' ] test: [ 'spec/*.coffee' @@ -225,7 +227,6 @@ module.exports = (grunt) -> grunt.registerTask('compile', ['coffee', 'prebuild-less', 'cson', 'peg']) grunt.registerTask('lint', ['coffeelint', 'csslint', 'lesslint']) grunt.registerTask('test', ['shell:kill-atom', 'run-specs']) - grunt.registerTask('ci', ['output-disk-space', 'download-atom-shell', 'build', 'set-development-version', 'lint', 'test', 'publish-build']) - grunt.registerTask('deploy', ['partial-clean', 'download-atom-shell', 'build', 'codesign']) + grunt.registerTask('ci', ['output-disk-space', 'download-atom-shell', 'build', 'set-version', 'lint', 'test', 'codesign', 'publish-build']) grunt.registerTask('docs', ['markdown:guides', 'build-docs']) - grunt.registerTask('default', ['download-atom-shell', 'build', 'set-development-version', 'install']) + grunt.registerTask('default', ['download-atom-shell', 'build', 'set-version', 'install']) diff --git a/build/tasks/build-task.coffee b/build/tasks/build-task.coffee index 1f95e76c4..0e1d47e61 100644 --- a/build/tasks/build-task.coffee +++ b/build/tasks/build-task.coffee @@ -39,19 +39,27 @@ module.exports = (grunt) -> else nonPackageDirectories.push(directory) + # Put any paths here that shouldn't end up in the built Atom.app + # so that it doesn't becomes larger than it needs to be. ignoredPaths = [ path.join('git-utils', 'deps') path.join('oniguruma', 'deps') + path.join('less', 'dist') + path.join('less', 'test') + path.join('bootstrap', 'docs') + path.join('spellchecker', 'vendor') + path.join('xmldom', 'test') path.join('vendor', 'apm') path.join('resources', 'mac') path.join('resources', 'win') ] ignoredPaths = ignoredPaths.map (ignoredPath) -> "(#{ignoredPath})" nodeModulesFilter = new RegExp(ignoredPaths.join('|')) + packageFilter = new RegExp("(#{ignoredPaths.join('|')})|(.+\\.(cson|coffee)$)") for directory in nonPackageDirectories cp directory, path.join(appDir, directory), filter: nodeModulesFilter for directory in packageDirectories - cp directory, path.join(appDir, directory), filter: /.+\.(cson|coffee)$/ + cp directory, path.join(appDir, directory), filter: packageFilter cp 'spec', path.join(appDir, 'spec') cp 'src', path.join(appDir, 'src'), filter: /.+\.(cson|coffee)$/ diff --git a/build/tasks/codesign-task.coffee b/build/tasks/codesign-task.coffee index fd4592eba..93920d7a7 100644 --- a/build/tasks/codesign-task.coffee +++ b/build/tasks/codesign-task.coffee @@ -3,6 +3,23 @@ module.exports = (grunt) -> grunt.registerTask 'codesign', 'Codesign the app', -> done = @async() + + if process.env.XCODE_KEYCHAIN + unlockKeychain (error) -> + if error? + done(error) + else + signApp(done) + else + signApp(done) + + unlockKeychain = (callback) -> + cmd = 'security' + {XCODE_KEYCHAIN_PASSWORD, XCODE_KEYCHAIN} = process.env + args = ['unlock-keychain', '-p', XCODE_KEYCHAIN_PASSWORD, XCODE_KEYCHAIN] + spawn {cmd, args}, (error) -> callback(error) + + signApp = (callback) -> cmd = 'codesign' args = ['-f', '-v', '-s', 'Developer ID Application: GitHub', grunt.config.get('atom.shellAppDir')] - spawn {cmd, args}, (error) -> done(error) + spawn {cmd, args}, (error) -> callback(error) diff --git a/build/tasks/publish-build-task.coffee b/build/tasks/publish-build-task.coffee index 3f8dc909a..19f0aff90 100644 --- a/build/tasks/publish-build-task.coffee +++ b/build/tasks/publish-build-task.coffee @@ -24,13 +24,19 @@ module.exports = (gruntObject) -> done = @async() - createRelease (error, release) -> + createBuildRelease (error, release) -> return done(error) if error? zipApp (error) -> return done(error) if error? uploadAsset release, (error) -> return done(error) if error? - publishRelease(release, done) + publishRelease release, (error) -> + return done(error) if error? + getAtomDraftRelease (error, release) -> + return done(error) if error? + deleteExistingAsset release, (error) -> + return done(error) if error? + uploadAsset(release, done) logError = (message, error, details) -> grunt.log.error(message) @@ -65,6 +71,18 @@ getRelease = (callback) -> return callback() +getAtomDraftRelease = (callback) -> + atomRepo = new GitHub({repo: 'atom/atom', token}) + atomRepo.getReleases (error, releases=[]) -> + if error? + logError('Fetching atom/atom releases failed', error, releases) + callback(error) + else + for release in releases when release.draft + callback(null, release) + return + callback(new Error('No draft release in atom/atom repo')) + deleteRelease = (release) -> options = uri: release.url @@ -92,7 +110,7 @@ deleteExistingAsset = (release, callback) -> callback() -createRelease = (callback) -> +createBuildRelease = (callback) -> getRelease (error, release) -> if error? callback(error) @@ -123,7 +141,7 @@ createRelease = (callback) -> uploadAsset = (release, callback) -> options = - uri: "https://uploads.github.com/repos/atom/atom-master-builds/releases/#{release.id}/assets?name=#{assetName}" + uri: release.upload_url.replace(/\{.*$/, "?name=#{assetName}") method: 'POST' headers: _.extend({ 'Content-Type': 'application/zip' diff --git a/build/tasks/set-development-version-task.coffee b/build/tasks/set-version-task.coffee similarity index 64% rename from build/tasks/set-development-version-task.coffee rename to build/tasks/set-version-task.coffee index 66bdd1089..be41866a8 100644 --- a/build/tasks/set-development-version-task.coffee +++ b/build/tasks/set-version-task.coffee @@ -4,15 +4,24 @@ path = require 'path' module.exports = (grunt) -> {spawn} = require('./task-helpers')(grunt) - grunt.registerTask 'set-development-version', 'Sets version to current SHA-1', -> + getVersion = (callback) -> + if process.env.JANKY_SHA1 and process.env.JANKY_BRANCH is 'master' + {version} = require(path.join(grunt.config.get('atom.appDir'), 'package.json')) + callback(null, version) + else + cmd = 'git' + args = ['rev-parse', '--short', 'HEAD'] + spawn {cmd, args}, (error, {stdout}={}, code) -> + callback(error, stdout?.trim?()) + + grunt.registerTask 'set-version', 'Set the version in the plist and package.json', -> done = @async() - cmd = 'git' - args = ['rev-parse', '--short', 'HEAD'] - spawn {cmd, args}, (error, result, code) -> - return done(error) if error? + getVersion (error, version) -> + if error? + done(error) + return - version = result.stdout.trim() appDir = grunt.config.get('atom.appDir') # Replace version field of package.json. @@ -32,7 +41,7 @@ module.exports = (grunt) -> strings = CompanyName: 'GitHub, Inc.' - FileDescription: 'The hackable, collaborative editor of tomorrow!' + FileDescription: 'The hackable, collaborative editor' LegalCopyright: 'Copyright (C) 2013 GitHub, Inc. All rights reserved' ProductName: 'Atom' ProductVersion: version diff --git a/package.json b/package.json index c8a891c1c..fa883f73a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "atom", "productName": "Atom", - "version": "0.45.0", + "version": "0.46.0", "main": "./src/browser/main.js", "repository": { "type": "git", @@ -19,7 +19,7 @@ "atomShellVersion": "0.8.5", "dependencies": { "async": "0.2.6", - "bootstrap": "git://github.com/benogle/bootstrap.git", + "bootstrap": "git://github.com/atom/bootstrap.git#6af81906189f1747fd6c93479e3d998ebe041372", "clear-cut": "0.2.0", "coffee-script": "1.6.3", "coffeestack": "0.6.0", @@ -28,11 +28,9 @@ "fs-plus": "0.14.0", "fstream": "0.1.24", "fuzzaldrin": "0.6.0", - "git-utils": "0.30.0", + "git-utils": "0.33.0", "guid": "0.0.10", - "jasmine-focused": "~0.15.0", - "jasmine-node": "git://github.com/kevinsawicki/jasmine-node.git#short-stacks", - "jasmine-tagged": "0.2.0", + "jasmine-tagged": "0.3.0", "mkdirp": "0.3.5", "keytar": "0.15.0", "less-cache": "0.10.0", @@ -43,17 +41,18 @@ "pathwatcher": "0.14.0", "pegjs": "0.8.0", "q": "0.9.7", - "scandal": "0.11.0", + "scandal": "0.12.0", "season": "0.14.0", "semver": "1.1.4", "space-pen": "3.1.0", "temp": "0.5.0", - "text-buffer": "0.12.0", + "text-buffer": "0.13.0", "underscore-plus": "0.6.1", "vm-compatibility-layer": "0.1.0", "theorist": "~0.13.0", "delegato": "~0.4.0", - "mixto": "~0.4.0" + "mixto": "~0.4.0", + "property-accessors": "~0.1.0" }, "packageDependencies": { "atom-dark-syntax": "0.10.0", @@ -64,46 +63,46 @@ "solarized-dark-syntax": "0.6.0", "solarized-light-syntax": "0.2.0", "archive-view": "0.19.0", - "autocomplete": "0.19.0", - "autoflow": "0.11.0", + "autocomplete": "0.20.0", + "autoflow": "0.12.0", "autosave": "0.10.0", "background-tips": "0.4.0", - "bookmarks": "0.15.0", - "bracket-matcher": "0.16.0", + "bookmarks": "0.16.0", + "bracket-matcher": "0.19.0", "command-logger": "0.9.0", "command-palette": "0.14.0", "dev-live-reload": "0.22.0", "editor-stats": "0.12.0", "exception-reporting": "0.11.0", "feedback": "0.22.0", - "find-and-replace": "0.74.0", - "fuzzy-finder": "0.30.0", - "gists": "0.14.0", - "git-diff": "0.21.0", + "find-and-replace": "0.75.0", + "fuzzy-finder": "0.31.0", + "gists": "0.15.0", + "git-diff": "0.22.0", "github-sign-in": "0.16.0", - "go-to-line": "0.14.0", - "grammar-selector": "0.16.0", + "go-to-line": "0.15.0", + "grammar-selector": "0.17.0", "image-view": "0.15.0", "keybinding-resolver": "0.8.0", "markdown-preview": "0.24.0", "metrics": "0.21.0", - "package-generator": "0.23.0", + "package-generator": "0.24.0", "release-notes": "0.15.0", - "settings-view": "0.55.0", - "snippets": "0.18.0", - "spell-check": "0.19.0", - "status-bar": "0.31.0", + "settings-view": "0.56.0", + "snippets": "0.19.0", + "spell-check": "0.20.0", + "status-bar": "0.32.0", "styleguide": "0.19.0", - "symbols-view": "0.28.0", + "symbols-view": "0.29.0", "tabs": "0.17.0", "terminal": "0.24.0", "timecop": "0.13.0", "to-the-hubs": "0.17.0", - "tree-view": "0.59.0", + "tree-view": "0.61.0", "visual-bell": "0.6.0", "welcome": "0.4.0", "whitespace": "0.10.0", - "wrap-guide": "0.11.0", + "wrap-guide": "0.12.0", "language-c": "0.2.0", "language-clojure": "0.1.0", "language-coffee-script": "0.4.0", @@ -122,12 +121,12 @@ "language-objective-c": "0.2.0", "language-pegjs": "0.1.0", "language-perl": "0.2.0", - "language-php": "0.2.0", + "language-php": "0.3.0", "language-property-list": "0.2.0", "language-puppet": "0.2.0", "language-python": "0.2.0", - "language-ruby": "0.6.0", - "language-ruby-on-rails": "0.3.0", + "language-ruby": "0.7.0", + "language-ruby-on-rails": "0.4.0", "language-sass": "0.3.0", "language-shellscript": "0.2.0", "language-source": "0.2.0", diff --git a/script/bootstrap b/script/bootstrap index 2154a1ef4..d0c9919e0 100755 --- a/script/bootstrap +++ b/script/bootstrap @@ -31,6 +31,7 @@ if (!fs.existsSync(path.join(apmInstallPath, 'node_modules'))) fs.mkdirSync(path.join(apmInstallPath, 'node_modules')); var apmFlags = process.env.JANKY_SHA1 || process.argv.indexOf('--no-color') !== -1 ? '--no-color' : ''; +var packagesToDedupe = ['nan', 'oniguruma', 'roaster']; var echoNewLine = process.platform == 'win32' ? 'echo.' : 'echo'; var commands = [ 'git submodule --quiet sync', @@ -43,6 +44,7 @@ var commands = [ echoNewLine, 'node apm/node_modules/atom-package-manager/bin/apm clean ' + apmFlags, 'node apm/node_modules/atom-package-manager/bin/apm install --quiet ' + apmFlags, + 'node apm/node_modules/atom-package-manager/bin/apm dedupe --quiet ' + apmFlags + ' ' + packagesToDedupe.join(' '), ]; process.chdir(path.dirname(__dirname)); diff --git a/script/cibuild b/script/cibuild index c5b1a0423..6c7cb8ef5 100755 --- a/script/cibuild +++ b/script/cibuild @@ -10,11 +10,9 @@ if (process.platform == 'linux') var homeDir = process.platform == 'win32' ? process.env.USERPROFILE : process.env.HOME; -function readEnvironmentVariables() { - var credentialsPath = '/var/lib/jenkins/config/atomcredentials'; +function loadEnvironmentVariables(filePath) { try { - var credentials = fs.readFileSync(credentialsPath, 'utf8'); - var lines = credentials.trim().split('\n'); + var lines = fs.readFileSync(filePath, 'utf8').trim().split('\n'); for (i in lines) { var parts = lines[i].split('='); var key = parts[0].trim(); @@ -24,6 +22,11 @@ function readEnvironmentVariables() { } catch(error) { } } +function readEnvironmentVariables() { + loadEnvironmentVariables('/var/lib/jenkins/config/atomcredentials') + loadEnvironmentVariables('/var/lib/jenkins/config/xcodekeychain') +} + readEnvironmentVariables(); cp.safeExec.bind(global, 'node script/bootstrap', function(error) { if (error) diff --git a/script/constructicon/build b/script/constructicon/build deleted file mode 100755 index 647108d85..000000000 --- a/script/constructicon/build +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -set -ex - -# This entire file is a hack so that constructicon can build Atom via -# xcode - -cd "$(dirname "$0")/../.." -rm -fr node_modules -rm -fr vendor/apm/node_modules -./script/bootstrap --no-color -./build/node_modules/.bin/grunt --no-color --build-dir="$BUILT_PRODUCTS_DIR" deploy - -echo "TARGET_BUILD_DIR=$BUILT_PRODUCTS_DIR" -echo "FULL_PRODUCT_NAME=Atom.app" -echo "PRODUCT_NAME=Atom" diff --git a/script/constructicon/prebuild b/script/constructicon/prebuild deleted file mode 100755 index 600a56afa..000000000 --- a/script/constructicon/prebuild +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh - -set -ex -cd "$(dirname "$0")/../.." -export PATH="atom-shell/Atom.app/Contents/Resources/:${PATH}" - -rm -rf atom.xcodeproj -gyp --depth=. atom.gyp diff --git a/spec/editor-view-spec.coffee b/spec/editor-view-spec.coffee index 6ab18ccdf..bae094249 100644 --- a/spec/editor-view-spec.coffee +++ b/spec/editor-view-spec.coffee @@ -21,7 +21,7 @@ describe "EditorView", -> editorView.calculateHeightInLines = -> Math.ceil(@height() / @lineHeight) editorView.attachToDom = ({ heightInLines, widthInChars } = {}) -> - heightInLines ?= @getBuffer().getLineCount() + heightInLines ?= @getEditor().getBuffer().getLineCount() @height(getLineHeight() * heightInLines) @width(getCharWidth() * widthInChars) if widthInChars $('#jasmine-content').append(this) @@ -51,7 +51,7 @@ describe "EditorView", -> it "calculates line height and char width and updates the pixel position of the cursor", -> expect(editorView.lineHeight).toBeNull() expect(editorView.charWidth).toBeNull() - editorView.setCursorScreenPosition(row: 2, column: 2) + editor.setCursorScreenPosition(row: 2, column: 2) editorView.attachToDom() @@ -72,7 +72,7 @@ describe "EditorView", -> it "does not scroll the editor view (regression)", -> editorView.attachToDom(heightInLines: 2) - editorView.selectAll() + editor.selectAll() editorView.hiddenInput.blur() editorView.focus() @@ -80,7 +80,7 @@ describe "EditorView", -> expect($(editorView[0]).scrollTop()).toBe 0 expect($(editorView.scrollView[0]).scrollTop()).toBe 0 - editorView.moveCursorToBottom() + editor.moveCursorToBottom() editorView.hiddenInput.blur() editorView.scrollTop(0) editorView.focus() @@ -105,7 +105,7 @@ describe "EditorView", -> fs.writeFileSync(filePath, "") editor = atom.project.openSync(filePath) editorView.edit(editor) - editorView.insertText("now the buffer is modified") + editor.insertText("now the buffer is modified") fileChangeHandler = jasmine.createSpy('fileChange') editor.buffer.file.on 'contents-changed', fileChangeHandler @@ -134,7 +134,7 @@ describe "EditorView", -> it "updates the rendered lines, cursors, selections, scroll position, and event subscriptions to match the given edit session", -> editorView.attachToDom(heightInLines: 5, widthInChars: 30) - editorView.setCursorBufferPosition([6, 13]) + editor.setCursorBufferPosition([6, 13]) editorView.scrollToBottom() editorView.scrollLeft(150) previousScrollHeight = editorView.verticalScrollbar.prop('scrollHeight') @@ -151,7 +151,7 @@ describe "EditorView", -> expect(editorView.scrollTop()).toBe 900 expect(editorView.scrollLeft()).toBe 0 expect(editorView.getSelectionView().regions[0].position().top).toBe 40 * editorView.lineHeight - editorView.insertText("hello") + newEditor.insertText("hello") expect(editorView.lineElementForScreenRow(40).text()).toBe "hello3" editorView.edit(editor) @@ -162,7 +162,7 @@ describe "EditorView", -> expect(editorView.scrollTop()).toBe previousScrollTop expect(editorView.scrollLeft()).toBe previousScrollLeft expect(editorView.getCursorView().position()).toEqual { top: 6 * editorView.lineHeight, left: 13 * editorView.charWidth } - editorView.insertText("goodbye") + editor.insertText("goodbye") expect(editorView.lineElementForScreenRow(6).text()).toMatch /^ currentgoodbye/ it "triggers alert if edit session's buffer goes into conflict with changes on disk", -> @@ -271,7 +271,7 @@ describe "EditorView", -> it "emits event when buffer's path is changed", -> eventHandler = jasmine.createSpy('eventHandler') editorView.on 'editor:path-changed', eventHandler - editorView.getBuffer().saveAs(filePath) + editor.saveAs(filePath) expect(eventHandler).toHaveBeenCalled() it "emits event when editor view view receives a new buffer", -> @@ -282,10 +282,12 @@ describe "EditorView", -> it "stops listening to events on previously set buffers", -> eventHandler = jasmine.createSpy('eventHandler') - oldBuffer = editorView.getBuffer() + oldBuffer = editor.getBuffer() + newEditor = atom.project.openSync(filePath) editorView.on 'editor:path-changed', eventHandler - editorView.edit(atom.project.openSync(filePath)) + + editorView.edit(newEditor) expect(eventHandler).toHaveBeenCalled() eventHandler.reset() @@ -293,13 +295,13 @@ describe "EditorView", -> expect(eventHandler).not.toHaveBeenCalled() eventHandler.reset() - editorView.getBuffer().saveAs(path.join(temp.dir, 'atom-new.txt')) + newEditor.getBuffer().saveAs(path.join(temp.dir, 'atom-new.txt')) expect(eventHandler).toHaveBeenCalled() it "loads the grammar for the new path", -> - expect(editorView.getGrammar().name).toBe 'JavaScript' - editorView.getBuffer().saveAs(filePath) - expect(editorView.getGrammar().name).toBe 'Plain Text' + expect(editor.getGrammar().name).toBe 'JavaScript' + editor.getBuffer().saveAs(filePath) + expect(editor.getGrammar().name).toBe 'Plain Text' describe "font family", -> beforeEach -> @@ -322,7 +324,7 @@ describe "EditorView", -> editorView.attachToDom(12) lineHeightBefore = editorView.lineHeight charWidthBefore = editorView.charWidth - editorView.setCursorScreenPosition [5, 6] + editor.setCursorScreenPosition [5, 6] atom.config.set("editor.fontFamily", fontFamily) expect(editorView.css('font-family')).toBe fontFamily @@ -347,7 +349,7 @@ describe "EditorView", -> editorView.attachToDom() lineHeightBefore = editorView.lineHeight charWidthBefore = editorView.charWidth - editorView.setCursorScreenPosition [5, 6] + editor.setCursorScreenPosition [5, 6] atom.config.set("editor.fontSize", 30) expect(editorView.css('font-size')).toBe '30px' @@ -364,7 +366,7 @@ describe "EditorView", -> it "updates the position and size of selection regions", -> atom.config.set("editor.fontSize", 10) - editorView.setSelectedBufferRange([[5, 2], [5, 7]]) + editor.setSelectedBufferRange([[5, 2], [5, 7]]) editorView.attachToDom() atom.config.set("editor.fontSize", 30) @@ -384,7 +386,7 @@ describe "EditorView", -> describe "when the font size changes while editor view view is detached", -> it "redraws the editor view view according to the new font size when it is reattached", -> - editorView.setCursorScreenPosition([4, 2]) + editor.setCursorScreenPosition([4, 2]) editorView.attachToDom() initialLineHeight = editorView.lineHeight initialCharWidth = editorView.charWidth @@ -409,17 +411,17 @@ describe "EditorView", -> describe "single-click", -> it "re-positions the cursor to the clicked row / column", -> - expect(editorView.getCursorScreenPosition()).toEqual(row: 0, column: 0) + expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0) editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [3, 10]) - expect(editorView.getCursorScreenPosition()).toEqual(row: 3, column: 10) + expect(editor.getCursorScreenPosition()).toEqual(row: 3, column: 10) describe "when the lines are scrolled to the right", -> it "re-positions the cursor on the clicked location", -> setEditorWidthInChars(editorView, 30) - expect(editorView.getCursorScreenPosition()).toEqual(row: 0, column: 0) + expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0) editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [3, 30]) # scrolls lines to the right editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [3, 50]) - expect(editorView.getCursorBufferPosition()).toEqual(row: 3, column: 50) + expect(editor.getCursorBufferPosition()).toEqual(row: 3, column: 50) describe "when the editor view view is using a variable-width font", -> beforeEach -> @@ -428,62 +430,62 @@ describe "EditorView", -> it "positions the cursor to the clicked row and column", -> {top, left} = editorView.pixelOffsetForScreenPosition([3, 30]) editorView.renderedLines.trigger mousedownEvent(pageX: left, pageY: top) - expect(editorView.getCursorScreenPosition()).toEqual [3, 30] + expect(editor.getCursorScreenPosition()).toEqual [3, 30] describe "double-click", -> it "selects the word under the cursor, and expands the selection wordwise in either direction on a subsequent shift-click", -> - expect(editorView.getCursorScreenPosition()).toEqual(row: 0, column: 0) + expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0) editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [8, 24], originalEvent: {detail: 1}) editorView.renderedLines.trigger 'mouseup' editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [8, 24], originalEvent: {detail: 2}) editorView.renderedLines.trigger 'mouseup' - expect(editorView.getSelectedText()).toBe "concat" + expect(editor.getSelectedText()).toBe "concat" editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [8, 7], shiftKey: true) editorView.renderedLines.trigger 'mouseup' - expect(editorView.getSelectedText()).toBe "return sort(left).concat" + expect(editor.getSelectedText()).toBe "return sort(left).concat" it "stops selecting by word when the selection is emptied", -> - expect(editorView.getCursorScreenPosition()).toEqual(row: 0, column: 0) + expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0) editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 8], originalEvent: {detail: 1}) editorView.renderedLines.trigger 'mouseup' editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 8], originalEvent: {detail: 2}) editorView.renderedLines.trigger 'mouseup' - expect(editorView.getSelectedText()).toBe "quicksort" + expect(editor.getSelectedText()).toBe "quicksort" editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [3, 10]) editorView.renderedLines.trigger 'mouseup' editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [3, 12], originalEvent: {detail: 1}, shiftKey: true) - expect(editorView.getSelectedBufferRange()).toEqual [[3, 10], [3, 12]] + expect(editor.getSelectedBufferRange()).toEqual [[3, 10], [3, 12]] describe "when clicking between a word and a non-word", -> it "selects the word", -> - expect(editorView.getCursorScreenPosition()).toEqual(row: 0, column: 0) + expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0) editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [1, 21], originalEvent: {detail: 1}) editorView.renderedLines.trigger 'mouseup' editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [1, 21], originalEvent: {detail: 2}) editorView.renderedLines.trigger 'mouseup' - expect(editorView.getSelectedText()).toBe "function" + expect(editor.getSelectedText()).toBe "function" - editorView.setCursorBufferPosition([0, 0]) + editor.setCursorBufferPosition([0, 0]) editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [1, 22], originalEvent: {detail: 1}) editorView.renderedLines.trigger 'mouseup' editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [1, 22], originalEvent: {detail: 2}) editorView.renderedLines.trigger 'mouseup' - expect(editorView.getSelectedText()).toBe "items" + expect(editor.getSelectedText()).toBe "items" - editorView.setCursorBufferPosition([0, 0]) + editor.setCursorBufferPosition([0, 0]) editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 28], originalEvent: {detail: 1}) editorView.renderedLines.trigger 'mouseup' editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 28], originalEvent: {detail: 2}) editorView.renderedLines.trigger 'mouseup' - expect(editorView.getSelectedText()).toBe "{" + expect(editor.getSelectedText()).toBe "{" describe "triple/quardruple/etc-click", -> it "selects the line under the cursor", -> - expect(editorView.getCursorScreenPosition()).toEqual(row: 0, column: 0) + expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0) # Triple click editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [1, 8], originalEvent: {detail: 1}) @@ -492,7 +494,7 @@ describe "EditorView", -> editorView.renderedLines.trigger 'mouseup' editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [1, 8], originalEvent: {detail: 3}) editorView.renderedLines.trigger 'mouseup' - expect(editorView.getSelectedText()).toBe " var sort = function(items) {\n" + expect(editor.getSelectedText()).toBe " var sort = function(items) {\n" # Quad click editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [2, 3], originalEvent: {detail: 1}) @@ -503,7 +505,7 @@ describe "EditorView", -> editorView.renderedLines.trigger 'mouseup' editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [2, 3], originalEvent: {detail: 4}) editorView.renderedLines.trigger 'mouseup' - expect(editorView.getSelectedText()).toBe " if (items.length <= 1) return items;\n" + expect(editor.getSelectedText()).toBe " if (items.length <= 1) return items;\n" it "expands the selection linewise in either direction on a subsequent shift-click, but stops selecting linewise once the selection is emptied", -> editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [4, 8], originalEvent: {detail: 1}) @@ -512,55 +514,55 @@ describe "EditorView", -> editorView.renderedLines.trigger 'mouseup' editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [4, 8], originalEvent: {detail: 3}) editorView.renderedLines.trigger 'mouseup' - expect(editorView.getSelectedBufferRange()).toEqual [[4, 0], [5, 0]] + expect(editor.getSelectedBufferRange()).toEqual [[4, 0], [5, 0]] editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [1, 8], originalEvent: {detail: 1}, shiftKey: true) editorView.renderedLines.trigger 'mouseup' - expect(editorView.getSelectedBufferRange()).toEqual [[1, 0], [5, 0]] + expect(editor.getSelectedBufferRange()).toEqual [[1, 0], [5, 0]] editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [2, 8], originalEvent: {detail: 1}) editorView.renderedLines.trigger 'mouseup' - expect(editorView.getSelection().isEmpty()).toBeTruthy() + expect(editor.getSelection().isEmpty()).toBeTruthy() editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [3, 8], originalEvent: {detail: 1}, shiftKey: true) editorView.renderedLines.trigger 'mouseup' - expect(editorView.getSelectedBufferRange()).toEqual [[2, 8], [3, 8]] + expect(editor.getSelectedBufferRange()).toEqual [[2, 8], [3, 8]] describe "shift-click", -> it "selects from the cursor's current location to the clicked location", -> - editorView.setCursorScreenPosition([4, 7]) + editor.setCursorScreenPosition([4, 7]) editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [5, 24], shiftKey: true) - expect(editorView.getSelection().getScreenRange()).toEqual [[4, 7], [5, 24]] + expect(editor.getSelection().getScreenRange()).toEqual [[4, 7], [5, 24]] describe "shift-double-click", -> it "expands the selection on the first click and ignores the second click", -> - editorView.setCursorScreenPosition([4, 7]) + editor.setCursorScreenPosition([4, 7]) editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [5, 24], shiftKey: true, originalEvent: { detail: 1 }) editorView.renderedLines.trigger 'mouseup' - expect(editorView.getSelection().getScreenRange()).toEqual [[4, 7], [5, 24]] + expect(editor.getSelection().getScreenRange()).toEqual [[4, 7], [5, 24]] editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [5, 24], shiftKey: true, originalEvent: { detail: 2 }) editorView.renderedLines.trigger 'mouseup' - expect(editorView.getSelection().getScreenRange()).toEqual [[4, 7], [5, 24]] + expect(editor.getSelection().getScreenRange()).toEqual [[4, 7], [5, 24]] describe "shift-triple-click", -> it "expands the selection on the first click and ignores the second click", -> - editorView.setCursorScreenPosition([4, 7]) + editor.setCursorScreenPosition([4, 7]) editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [5, 24], shiftKey: true, originalEvent: { detail: 1 }) editorView.renderedLines.trigger 'mouseup' - expect(editorView.getSelection().getScreenRange()).toEqual [[4, 7], [5, 24]] + expect(editor.getSelection().getScreenRange()).toEqual [[4, 7], [5, 24]] editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [5, 24], shiftKey: true, originalEvent: { detail: 2 }) editorView.renderedLines.trigger 'mouseup' editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [5, 24], shiftKey: true, originalEvent: { detail: 3 }) editorView.renderedLines.trigger 'mouseup' - expect(editorView.getSelection().getScreenRange()).toEqual [[4, 7], [5, 24]] + expect(editor.getSelection().getScreenRange()).toEqual [[4, 7], [5, 24]] describe "meta-click", -> it "places an additional cursor", -> editorView.attachToDom() setEditorHeightInLines(editorView, 5) - editorView.setCursorBufferPosition([3, 0]) + editor.setCursorBufferPosition([3, 0]) editorView.scrollTop(editorView.lineHeight * 6) editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [6, 0], metaKey: true) @@ -583,10 +585,10 @@ describe "EditorView", -> # moving changes selection $(document).trigger mousemoveEvent(editorView: editorView, point: [5, 27]) - range = editorView.getSelection().getScreenRange() + range = editor.getSelection().getScreenRange() expect(range.start).toEqual({row: 4, column: 10}) expect(range.end).toEqual({row: 5, column: 27}) - expect(editorView.getCursorScreenPosition()).toEqual(row: 5, column: 27) + expect(editor.getCursorScreenPosition()).toEqual(row: 5, column: 27) # mouse up may occur outside of editorView, but still need to halt selection $(document).trigger 'mouseup' @@ -594,10 +596,10 @@ describe "EditorView", -> # moving after mouse up should not change selection editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [8, 8]) - range = editorView.getSelection().getScreenRange() + range = editor.getSelection().getScreenRange() expect(range.start).toEqual({row: 4, column: 10}) expect(range.end).toEqual({row: 5, column: 27}) - expect(editorView.getCursorScreenPosition()).toEqual(row: 5, column: 27) + expect(editor.getCursorScreenPosition()).toEqual(row: 5, column: 27) it "selects and scrolls if the mouse is dragged outside of the editor view view itself", -> editorView.vScrollMargin = 0 @@ -630,7 +632,7 @@ describe "EditorView", -> $(document).trigger mousemoveEvent(editorView: editorView, point: [5, 27]) $(document).trigger 'mouseup' - range = editorView.getSelection().getScreenRange() + range = editor.getSelection().getScreenRange() expect(range.start).toEqual({row: 4, column: 10}) expect(range.end).toEqual({row: 4, column: 10}) @@ -644,33 +646,33 @@ describe "EditorView", -> $(document).trigger mousemoveEvent(editorView: editorView, point: [5, 27]) $(document).trigger 'mouseup' - range = editorView.getSelection().getScreenRange() + 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(editorView.getCursorScreenPosition()).toEqual(row: 0, column: 0) + expect(editor.getCursorScreenPosition()).toEqual(row: 0, column: 0) editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 8], originalEvent: {detail: 1}) editorView.renderedLines.trigger 'mouseup' editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [0, 8], originalEvent: {detail: 2}) - expect(editorView.getSelectedText()).toBe "quicksort" + expect(editor.getSelectedText()).toBe "quicksort" editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [1, 8]) - expect(editorView.getSelectedBufferRange()).toEqual [[0, 4], [1, 10]] - expect(editorView.getCursorBufferPosition()).toEqual [1, 10] + expect(editor.getSelectedBufferRange()).toEqual [[0, 4], [1, 10]] + expect(editor.getCursorBufferPosition()).toEqual [1, 10] editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [0, 1]) - expect(editorView.getSelectedBufferRange()).toEqual [[0, 0], [0, 13]] - expect(editorView.getCursorBufferPosition()).toEqual [0, 0] + expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 13]] + expect(editor.getCursorBufferPosition()).toEqual [0, 0] editorView.renderedLines.trigger 'mouseup' - expect(editorView.getSelectedBufferRange()).toEqual [[0, 0], [0, 13]] + expect(editor.getSelectedBufferRange()).toEqual [[0, 0], [0, 13]] # shift-clicking still selects by word, but does not preserve the initial range editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [5, 25], originalEvent: {detail: 1}, shiftKey: true) editorView.renderedLines.trigger 'mouseup' - expect(editorView.getSelectedBufferRange()).toEqual [[0, 13], [5, 27]] + expect(editor.getSelectedBufferRange()).toEqual [[0, 13], [5, 27]] describe "triple-click and drag", -> it "expands the initial selection linewise in either direction", -> @@ -682,17 +684,17 @@ describe "EditorView", -> editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [4, 7], originalEvent: {detail: 2}) $(document).trigger 'mouseup' editorView.renderedLines.trigger mousedownEvent(editorView: editorView, point: [4, 7], originalEvent: {detail: 3}) - expect(editorView.getSelectedBufferRange()).toEqual [[4, 0], [5, 0]] + expect(editor.getSelectedBufferRange()).toEqual [[4, 0], [5, 0]] # moving changes selection linewise editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [5, 27]) - expect(editorView.getSelectedBufferRange()).toEqual [[4, 0], [6, 0]] - expect(editorView.getCursorBufferPosition()).toEqual [6, 0] + expect(editor.getSelectedBufferRange()).toEqual [[4, 0], [6, 0]] + expect(editor.getCursorBufferPosition()).toEqual [6, 0] # moving changes selection linewise editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [2, 27]) - expect(editorView.getSelectedBufferRange()).toEqual [[2, 0], [5, 0]] - expect(editorView.getCursorBufferPosition()).toEqual [2, 0] + expect(editor.getSelectedBufferRange()).toEqual [[2, 0], [5, 0]] + expect(editor.getCursorBufferPosition()).toEqual [2, 0] # mouse up may occur outside of editorView, but still need to halt selection $(document).trigger 'mouseup' @@ -707,7 +709,7 @@ describe "EditorView", -> editorView.renderedLines.trigger mousemoveEvent(editorView: editorView, point: [8, 27], metaKey: true) editorView.renderedLines.trigger 'mouseup' - selections = editorView.getSelections() + selections = editor.getSelections() expect(selections.length).toBe 2 [selection1, selection2] = selections expect(selection1.getScreenRange()).toEqual [[4, 10], [5, 27]] @@ -716,14 +718,14 @@ describe "EditorView", -> describe "when text input events are triggered on the hidden input element", -> it "inserts the typed character at the cursor position, both in the buffer and the pre element", -> editorView.attachToDom() - editorView.setCursorScreenPosition(row: 1, column: 6) + editor.setCursorScreenPosition(row: 1, column: 6) expect(buffer.lineForRow(1).charAt(6)).not.toBe 'q' editorView.hiddenInput.textInput 'q' expect(buffer.lineForRow(1).charAt(6)).toBe 'q' - expect(editorView.getCursorScreenPosition()).toEqual(row: 1, column: 7) + expect(editor.getCursorScreenPosition()).toEqual(row: 1, column: 7) expect(editorView.renderedLines.find('.line:eq(1)')).toHaveText buffer.lineForRow(1) describe "selection rendering", -> @@ -733,7 +735,7 @@ describe "EditorView", -> editorView.attachToDom() editorView.width(500) { charWidth, lineHeight } = editorView - selection = editorView.getSelection() + selection = editor.getSelection() selectionView = editorView.getSelectionView() describe "when a selection is added", -> @@ -845,7 +847,7 @@ describe "EditorView", -> describe "when the selection is created with the selectAll event", -> it "does not scroll to the end of the buffer", -> editorView.height(150) - editorView.selectAll() + editor.selectAll() expect(editorView.scrollTop()).toBe 0 # regression: does not scroll the scroll view when the editorView is refocused @@ -855,7 +857,7 @@ describe "EditorView", -> expect(editorView.scrollView.scrollTop()).toBe 0 # does autoscroll when the selection is cleared - editorView.moveCursorDown() + editor.moveCursorDown() expect(editorView.scrollTop()).toBeGreaterThan(0) describe "selection autoscrolling and highlighting when setting selected buffer range", -> @@ -864,24 +866,24 @@ describe "EditorView", -> describe "if autoscroll is true", -> it "centers the viewport on the selection if its vertical center is currently offscreen", -> - editorView.setSelectedBufferRange([[2, 0], [4, 0]], autoscroll: true) + editor.setSelectedBufferRange([[2, 0], [4, 0]], autoscroll: true) expect(editorView.scrollTop()).toBe 0 - editorView.setSelectedBufferRange([[6, 0], [8, 0]], autoscroll: true) + editor.setSelectedBufferRange([[6, 0], [8, 0]], autoscroll: true) expect(editorView.scrollTop()).toBe 5 * editorView.lineHeight it "highlights the selection if autoscroll is true", -> - editorView.setSelectedBufferRange([[2, 0], [4, 0]], autoscroll: true) + editor.setSelectedBufferRange([[2, 0], [4, 0]], autoscroll: true) expect(editorView.getSelectionView()).toHaveClass 'highlighted' advanceClock(1000) expect(editorView.getSelectionView()).not.toHaveClass 'highlighted' - editorView.setSelectedBufferRange([[3, 0], [5, 0]], autoscroll: true) + editor.setSelectedBufferRange([[3, 0], [5, 0]], autoscroll: true) expect(editorView.getSelectionView()).toHaveClass 'highlighted' advanceClock(500) spyOn(editorView.getSelectionView(), 'removeClass').andCallThrough() - editorView.setSelectedBufferRange([[2, 0], [4, 0]], autoscroll: true) + editor.setSelectedBufferRange([[2, 0], [4, 0]], autoscroll: true) expect(editorView.getSelectionView().removeClass).toHaveBeenCalledWith('highlighted') expect(editorView.getSelectionView()).toHaveClass 'highlighted' @@ -892,13 +894,13 @@ describe "EditorView", -> it "does not scroll to the selection or the cursor", -> editorView.scrollToBottom() scrollTopBefore = editorView.scrollTop() - editorView.setSelectedBufferRange([[0, 0], [1, 0]], autoscroll: false) + editor.setSelectedBufferRange([[0, 0], [1, 0]], autoscroll: false) expect(editorView.scrollTop()).toBe scrollTopBefore describe "if autoscroll is not specified", -> it "autoscrolls to the cursor as normal", -> editorView.scrollToBottom() - editorView.setSelectedBufferRange([[0, 0], [1, 0]]) + editor.setSelectedBufferRange([[0, 0], [1, 0]]) expect(editorView.scrollTop()).toBe 0 describe "cursor rendering", -> @@ -912,24 +914,24 @@ describe "EditorView", -> {charWidth} = editorView it "repositions the cursor's view on screen", -> - editorView.setCursorScreenPosition(row: 2, column: 2) + editor.setCursorScreenPosition(row: 2, column: 2) expect(editorView.getCursorView().position()).toEqual(top: 2 * editorView.lineHeight, left: 2 * editorView.charWidth) it "hides the cursor when the selection is non-empty, and shows it otherwise", -> cursorView = editorView.getCursorView() - expect(editorView.getSelection().isEmpty()).toBeTruthy() + expect(editor.getSelection().isEmpty()).toBeTruthy() expect(cursorView).toBeVisible() - editorView.setSelectedBufferRange([[0, 0], [3, 0]]) - expect(editorView.getSelection().isEmpty()).toBeFalsy() + editor.setSelectedBufferRange([[0, 0], [3, 0]]) + expect(editor.getSelection().isEmpty()).toBeFalsy() expect(cursorView).toBeHidden() - editorView.setCursorBufferPosition([1, 3]) - expect(editorView.getSelection().isEmpty()).toBeTruthy() + editor.setCursorBufferPosition([1, 3]) + expect(editor.getSelection().isEmpty()).toBeTruthy() expect(cursorView).toBeVisible() it "moves the hiddenInput to the same position with cursor's view", -> - editorView.setCursorScreenPosition(row: 2, column: 2) + editor.setCursorScreenPosition(row: 2, column: 2) expect(editorView.getCursorView().offset()).toEqual(editorView.hiddenInput.offset()) describe "when the editor view is using a variable-width font", -> @@ -938,23 +940,23 @@ describe "EditorView", -> describe "on #darwin or #linux", -> it "correctly positions the cursor", -> - editorView.setCursorBufferPosition([3, 30]) + editor.setCursorBufferPosition([3, 30]) expect(editorView.getCursorView().position()).toEqual {top: 3 * editorView.lineHeight, left: 178} - editorView.setCursorBufferPosition([3, Infinity]) + editor.setCursorBufferPosition([3, Infinity]) expect(editorView.getCursorView().position()).toEqual {top: 3 * editorView.lineHeight, left: 353} describe "on #win32", -> it "correctly positions the cursor", -> - editorView.setCursorBufferPosition([3, 30]) + editor.setCursorBufferPosition([3, 30]) expect(editorView.getCursorView().position()).toEqual {top: 3 * editorView.lineHeight, left: 175} - editorView.setCursorBufferPosition([3, Infinity]) + editor.setCursorBufferPosition([3, Infinity]) expect(editorView.getCursorView().position()).toEqual {top: 3 * editorView.lineHeight, left: 346} describe "autoscrolling", -> it "only autoscrolls when the last cursor is moved", -> - editorView.setCursorBufferPosition([11,0]) - editorView.addCursorAtBufferPosition([6,50]) - [cursor1, cursor2] = editorView.getCursors() + editor.setCursorBufferPosition([11,0]) + editor.addCursorAtBufferPosition([6,50]) + [cursor1, cursor2] = editor.getCursors() spyOn(editorView, 'scrollToPixelPosition') cursor1.setScreenPosition([10, 10]) @@ -964,37 +966,37 @@ describe "EditorView", -> expect(editorView.scrollToPixelPosition).toHaveBeenCalled() it "does not autoscroll if the 'autoscroll' option is false", -> - editorView.setCursorBufferPosition([11,0]) + editor.setCursorBufferPosition([11,0]) spyOn(editorView, 'scrollToPixelPosition') - editorView.setCursorScreenPosition([10, 10], autoscroll: false) + editor.setCursorScreenPosition([10, 10], autoscroll: false) expect(editorView.scrollToPixelPosition).not.toHaveBeenCalled() it "autoscrolls to cursor if autoscroll is true, even if the position does not change", -> spyOn(editorView, 'scrollToPixelPosition') - editorView.setCursorScreenPosition([4, 10], autoscroll: false) - editorView.setCursorScreenPosition([4, 10]) + editor.setCursorScreenPosition([4, 10], autoscroll: false) + editor.setCursorScreenPosition([4, 10]) expect(editorView.scrollToPixelPosition).toHaveBeenCalled() editorView.scrollToPixelPosition.reset() - editorView.setCursorBufferPosition([4, 10]) + editor.setCursorBufferPosition([4, 10]) expect(editorView.scrollToPixelPosition).toHaveBeenCalled() it "does not autoscroll the cursor based on a buffer change, unless the buffer change was initiated by the cursor", -> lastVisibleRow = editorView.getLastVisibleScreenRow() - editorView.addCursorAtBufferPosition([lastVisibleRow, 0]) + editor.addCursorAtBufferPosition([lastVisibleRow, 0]) spyOn(editorView, 'scrollToPixelPosition') buffer.insert([lastVisibleRow, 0], "\n\n") expect(editorView.scrollToPixelPosition).not.toHaveBeenCalled() - editorView.insertText('\n\n') + editor.insertText('\n\n') expect(editorView.scrollToPixelPosition.callCount).toBe 1 it "autoscrolls on undo/redo", -> spyOn(editorView, 'scrollToPixelPosition') - editorView.insertText('\n\n') + editor.insertText('\n\n') expect(editorView.scrollToPixelPosition.callCount).toBe 1 - editorView.undo() + editor.undo() expect(editorView.scrollToPixelPosition.callCount).toBe 2 - editorView.redo() + editor.redo() expect(editorView.scrollToPixelPosition.callCount).toBe 3 describe "when the last cursor exceeds the upper or lower scroll margins", -> @@ -1002,32 +1004,32 @@ describe "EditorView", -> it "sets the scrollTop so the cursor remains within the scroll margin", -> setEditorHeightInLines(editorView, 10) - _.times 6, -> editorView.moveCursorDown() + _.times 6, -> editor.moveCursorDown() expect(editorView.scrollTop()).toBe(0) - editorView.moveCursorDown() + editor.moveCursorDown() expect(editorView.scrollTop()).toBe(editorView.lineHeight) - editorView.moveCursorDown() + editor.moveCursorDown() expect(editorView.scrollTop()).toBe(editorView.lineHeight * 2) - _.times 3, -> editorView.moveCursorUp() + _.times 3, -> editor.moveCursorUp() - editorView.moveCursorUp() + editor.moveCursorUp() expect(editorView.scrollTop()).toBe(editorView.lineHeight) - editorView.moveCursorUp() + editor.moveCursorUp() expect(editorView.scrollTop()).toBe(0) describe "when the editor view is shorter than twice the vertical scroll margin", -> it "sets the scrollTop based on a reduced scroll margin, which prevents a jerky tug-of-war between upper and lower scroll margins", -> setEditorHeightInLines(editorView, 5) - _.times 3, -> editorView.moveCursorDown() + _.times 3, -> editor.moveCursorDown() expect(editorView.scrollTop()).toBe(editorView.lineHeight) - editorView.moveCursorUp() + editor.moveCursorUp() expect(editorView.renderedLines.css('top')).toBe "0px" describe "when the last cursor exceeds the right or left scroll margins", -> @@ -1037,23 +1039,23 @@ describe "EditorView", -> setEditorWidthInChars(editorView, 30) # moving right - editorView.setCursorScreenPosition([2, 24]) + editor.setCursorScreenPosition([2, 24]) expect(editorView.scrollLeft()).toBe 0 - editorView.setCursorScreenPosition([2, 25]) + editor.setCursorScreenPosition([2, 25]) expect(editorView.scrollLeft()).toBe charWidth - editorView.setCursorScreenPosition([2, 28]) + editor.setCursorScreenPosition([2, 28]) expect(editorView.scrollLeft()).toBe charWidth * 4 # moving left - editorView.setCursorScreenPosition([2, 9]) + editor.setCursorScreenPosition([2, 9]) expect(editorView.scrollLeft()).toBe charWidth * 4 - editorView.setCursorScreenPosition([2, 8]) + editor.setCursorScreenPosition([2, 8]) expect(editorView.scrollLeft()).toBe charWidth * 3 - editorView.setCursorScreenPosition([2, 5]) + editor.setCursorScreenPosition([2, 5]) expect(editorView.scrollLeft()).toBe 0 describe "when the editor view is narrower than twice the horizontal scroll margin", -> @@ -1061,15 +1063,15 @@ describe "EditorView", -> editorView.hScrollMargin = 6 setEditorWidthInChars(editorView, 7) - editorView.setCursorScreenPosition([2, 3]) + editor.setCursorScreenPosition([2, 3]) window.advanceClock() expect(editorView.scrollLeft()).toBe(0) - editorView.setCursorScreenPosition([2, 4]) + editor.setCursorScreenPosition([2, 4]) window.advanceClock() expect(editorView.scrollLeft()).toBe(charWidth) - editorView.setCursorScreenPosition([2, 3]) + editor.setCursorScreenPosition([2, 3]) window.advanceClock() expect(editorView.scrollLeft()).toBe(0) @@ -1081,23 +1083,23 @@ describe "EditorView", -> editorView.width(charWidth * 30) # moving right - editorView.setCursorScreenPosition([2, 24]) + editor.setCursorScreenPosition([2, 24]) expect(editorView.scrollLeft()).toBe 0 - editorView.setCursorScreenPosition([2, 25]) + editor.setCursorScreenPosition([2, 25]) expect(editorView.scrollLeft()).toBe 0 - editorView.setCursorScreenPosition([2, 28]) + editor.setCursorScreenPosition([2, 28]) expect(editorView.scrollLeft()).toBe 0 # moving left - editorView.setCursorScreenPosition([2, 9]) + editor.setCursorScreenPosition([2, 9]) expect(editorView.scrollLeft()).toBe 0 - editorView.setCursorScreenPosition([2, 8]) + editor.setCursorScreenPosition([2, 8]) expect(editorView.scrollLeft()).toBe 0 - editorView.setCursorScreenPosition([2, 5]) + editor.setCursorScreenPosition([2, 5]) expect(editorView.scrollLeft()).toBe 0 describe "when editor:toggle-soft-wrap is toggled", -> @@ -1105,7 +1107,7 @@ describe "EditorView", -> it "wraps the text and renders properly", -> editorView.attachToDom(heightInLines: 30, widthInChars: 30) editorView.setWidthInChars(100) - editorView.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.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.") editorView.scrollLeft(editorView.charWidth * 30) editorView.trigger "editor:toggle-soft-wrap" expect(editorView.scrollLeft()).toBe 0 @@ -1154,7 +1156,7 @@ describe "EditorView", -> expect(line12.children('span:eq(1)')).toMatchSelector '.keyword' it "wraps hard tabs in a span", -> - editorView.setText('\t<- hard tab') + editor.setText('\t<- hard tab') line0 = editorView.renderedLines.find('.line:first') span0_0 = line0.children('span:eq(0)').children('span:eq(0)') expect(span0_0).toMatchSelector '.hard-tab' @@ -1169,7 +1171,7 @@ describe "EditorView", -> describe "when the line has trailing whitespace", -> it "wraps trailing whitespace in a span", -> - editorView.setText('trailing whitespace -> ') + editor.setText('trailing whitespace -> ') line0 = editorView.renderedLines.find('.line:first') span0_last = line0.children('span:eq(0)').children('span:last') expect(span0_last).toMatchSelector '.trailing-whitespace' @@ -1206,25 +1208,25 @@ describe "EditorView", -> expect(editorView.renderedLines.find('.line:last').text()).toBe buffer.lineForRow(11) it "renders correctly when scrolling after text is added to the buffer", -> - editorView.insertText("1\n") - _.times 4, -> editorView.moveCursorDown() - expect(editorView.renderedLines.find('.line:eq(2)').text()).toBe editorView.lineForBufferRow(2) - expect(editorView.renderedLines.find('.line:eq(7)').text()).toBe editorView.lineForBufferRow(7) + editor.insertText("1\n") + _.times 4, -> editor.moveCursorDown() + expect(editorView.renderedLines.find('.line:eq(2)').text()).toBe editor.lineForBufferRow(2) + expect(editorView.renderedLines.find('.line:eq(7)').text()).toBe editor.lineForBufferRow(7) it "renders correctly when scrolling after text is removed from buffer", -> - editorView.getBuffer().delete([[0,0],[1,0]]) - expect(editorView.renderedLines.find('.line:eq(0)').text()).toBe editorView.lineForBufferRow(0) - expect(editorView.renderedLines.find('.line:eq(5)').text()).toBe editorView.lineForBufferRow(5) + editor.getBuffer().delete([[0,0],[1,0]]) + expect(editorView.renderedLines.find('.line:eq(0)').text()).toBe editor.lineForBufferRow(0) + expect(editorView.renderedLines.find('.line:eq(5)').text()).toBe editor.lineForBufferRow(5) editorView.scrollTop(3 * editorView.lineHeight) - expect(editorView.renderedLines.find('.line:first').text()).toBe editorView.lineForBufferRow(1) - expect(editorView.renderedLines.find('.line:last').text()).toBe editorView.lineForBufferRow(10) + expect(editorView.renderedLines.find('.line:first').text()).toBe editor.lineForBufferRow(1) + expect(editorView.renderedLines.find('.line:last').text()).toBe editor.lineForBufferRow(10) describe "when creating and destroying folds that are longer than the visible lines", -> describe "when the cursor precedes the fold when it is destroyed", -> it "renders lines and line numbers correctly", -> scrollHeightBeforeFold = editorView.scrollView.prop('scrollHeight') - fold = editorView.createFold(1, 9) + fold = editor.createFold(1, 9) fold.destroy() expect(editorView.scrollView.prop('scrollHeight')).toBe scrollHeightBeforeFold @@ -1240,8 +1242,8 @@ describe "EditorView", -> describe "when the cursor follows the fold when it is destroyed", -> it "renders lines and line numbers correctly", -> - fold = editorView.createFold(1, 9) - editorView.setCursorBufferPosition([10, 0]) + fold = editor.createFold(1, 9) + editor.setCursorBufferPosition([10, 0]) fold.destroy() expect(editorView.renderedLines.find('.line').length).toBe 8 @@ -1371,7 +1373,7 @@ describe "EditorView", -> expect(editorView.renderedLines.find(".line:last").text()).toBe buffer.lineForRow(6) it "increases the width of the rendered lines element to be either the width of the longest line or the width of the scrollView (whichever is longer)", -> - maxLineLength = editorView.getMaxScreenLineLength() + maxLineLength = editor.getMaxScreenLineLength() setEditorWidthInChars(editorView, maxLineLength) widthBefore = editorView.renderedLines.width() expect(widthBefore).toBe editorView.scrollView.width() + 20 @@ -1383,7 +1385,7 @@ describe "EditorView", -> editorView.attachToDom(heightInLines: 5) it "sets the rendered screen line's width to either the max line length or the scollView's width (whichever is greater)", -> - maxLineLength = editorView.getMaxScreenLineLength() + maxLineLength = editor.getMaxScreenLineLength() setEditorWidthInChars(editorView, maxLineLength) buffer.change([[12,0], [12,0]], [1..maxLineLength*2].join('')) expect(editorView.renderedLines.width()).toBeGreaterThan editorView.scrollView.width() @@ -1431,8 +1433,8 @@ describe "EditorView", -> describe "when the last line is removed when the editor view is scrolled to the bottom", -> it "reduces the editor view's scrollTop (due to the reduced total scroll height) and renders the correct screen lines", -> - editorView.setCursorScreenPosition([Infinity, Infinity]) - editorView.insertText('\n\n\n') + editor.setCursorScreenPosition([Infinity, Infinity]) + editor.insertText('\n\n\n') editorView.scrollToBottom() expect(buffer.getLineCount()).toBe 16 @@ -1441,7 +1443,7 @@ describe "EditorView", -> expect(editorView.firstRenderedScreenRow).toBe 9 expect(editorView.lastRenderedScreenRow).toBe 15 - editorView.backspace() + editor.backspace() expect(editorView.scrollTop()).toBeLessThan initialScrollTop expect(editorView.firstRenderedScreenRow).toBe 9 @@ -1449,13 +1451,13 @@ describe "EditorView", -> expect(editorView.find('.line').length).toBe 6 - editorView.backspace() + editor.backspace() expect(editorView.firstRenderedScreenRow).toBe 9 expect(editorView.lastRenderedScreenRow).toBe 13 expect(editorView.find('.line').length).toBe 5 - editorView.backspace() + editor.backspace() expect(editorView.firstRenderedScreenRow).toBe 6 expect(editorView.lastRenderedScreenRow).toBe 12 @@ -1487,7 +1489,7 @@ describe "EditorView", -> expect(editorView.renderedLines.find('.line').length).toBe 8 - editorView.moveCursorToBottom() + editor.moveCursorToBottom() expect(editorView.renderedLines.find('.line').length).toBe 8 @@ -1499,7 +1501,7 @@ describe "EditorView", -> describe "when editor.showInvisibles config is set to true", -> it "displays spaces, tabs, and newlines using visible non-empty values", -> - editorView.setText " a line with tabs\tand spaces " + editor.setText " a line with tabs\tand spaces " editorView.attachToDom() expect(atom.config.get("editor.showInvisibles")).toBeFalsy() @@ -1520,18 +1522,18 @@ describe "EditorView", -> it "displays newlines as their own token outside of the other tokens scope", -> editorView.setShowInvisibles(true) editorView.attachToDom() - editorView.setText "var" + editor.setText "var" expect(editorView.find('.line').html()).toBe 'var¬' it "allows invisible glyphs to be customized via the editor.invisibles config", -> - editorView.setText(" \t ") + editor.setText(" \t ") editorView.attachToDom() atom.config.set("editor.showInvisibles", true) atom.config.set("editor.invisibles", eol: ";", space: "_", tab: "tab") expect(editorView.find(".line:first").text()).toBe "_tab _;" it "displays trailing carriage return using a visible non-empty value", -> - editorView.setText "a line that ends with a carriage return\r\n" + editor.setText "a line that ends with a carriage return\r\n" editorView.attachToDom() expect(atom.config.get("editor.showInvisibles")).toBeFalsy() @@ -1549,7 +1551,7 @@ describe "EditorView", -> editor.setSoftWrap(true) it "doesn't show the end of line invisible at the end of lines broken due to wrapping", -> - editorView.setText "a line that wraps" + editor.setText "a line that wraps" editorView.attachToDom() editorView.setWidthInChars(6) atom.config.set "editor.showInvisibles", true @@ -1561,7 +1563,7 @@ describe "EditorView", -> expect(editorView.renderedLines.find('.line:last').text()).toBe "wraps#{eol}" it "displays trailing carriage return using a visible non-empty value", -> - editorView.setText "a line that\r\n" + editor.setText "a line that\r\n" editorView.attachToDom() editorView.setWidthInChars(6) atom.config.set "editor.showInvisibles", true @@ -1628,8 +1630,8 @@ describe "EditorView", -> expect(editorView.renderedLines.find('.line:eq(10) .indent-guide').length).toBe 1 expect(editorView.renderedLines.find('.line:eq(10) .indent-guide').text()).toBe ' ' - editorView.setCursorBufferPosition([9]) - editorView.indentSelectedRows() + editor.setCursorBufferPosition([9]) + editor.indentSelectedRows() expect(editorView.renderedLines.find('.line:eq(10) .indent-guide').length).toBe 2 expect(editorView.renderedLines.find('.line:eq(10) .indent-guide').text()).toBe ' ' @@ -1642,8 +1644,8 @@ describe "EditorView", -> expect(editorView.renderedLines.find('.line:eq(10) .indent-guide').length).toBe 1 expect(editorView.renderedLines.find('.line:eq(10) .indent-guide').text()).toBe ' ' - editorView.setCursorBufferPosition([11]) - editorView.indentSelectedRows() + editor.setCursorBufferPosition([11]) + editor.indentSelectedRows() expect(editorView.renderedLines.find('.line:eq(10) .indent-guide').length).toBe 2 expect(editorView.renderedLines.find('.line:eq(10) .indent-guide').text()).toBe ' ' @@ -1653,10 +1655,10 @@ describe "EditorView", -> editorView.attachToDom() atom.config.set("editor.showIndentGuide", true) - editorView.setCursorBufferPosition([10]) - editorView.indent() - editorView.indent() - expect(editorView.getCursorBufferPosition()).toEqual [10, 4] + editor.setCursorBufferPosition([10]) + editor.indent() + editor.indent() + expect(editor.getCursorBufferPosition()).toEqual [10, 4] expect(editorView.renderedLines.find('.line:eq(10) .indent-guide').length).toBe 2 expect(editorView.renderedLines.find('.line:eq(10) .indent-guide').text()).toBe ' ' @@ -1664,9 +1666,9 @@ describe "EditorView", -> editorView.attachToDom() atom.config.set("editor.showIndentGuide", true) - editorView.setCursorBufferPosition([1, Infinity]) - editorView.insertNewline() - expect(editorView.getCursorBufferPosition()).toEqual [2, 0] + editor.setCursorBufferPosition([1, Infinity]) + editor.insertNewline() + expect(editor.getCursorBufferPosition()).toEqual [2, 0] expect(editorView.renderedLines.find('.line:eq(2) .indent-guide').length).toBe 2 expect(editorView.renderedLines.find('.line:eq(2) .indent-guide').text()).toBe ' ' @@ -1675,7 +1677,7 @@ describe "EditorView", -> editorView.attachToDom() atom.config.set("editor.showIndentGuide", true) - editorView.insertText("/*\n * \n*/") + editor.insertText("/*\n * \n*/") expect(editorView.renderedLines.find('.line:eq(1) .indent-guide').length).toBe 1 expect(editorView.renderedLines.find('.line:eq(1) .indent-guide')).toHaveClass('leading-whitespace') @@ -1690,8 +1692,8 @@ describe "EditorView", -> expect(editorView.renderedLines.find('.line:eq(10) .indent-guide').text()).toBe "#{eol} " expect(editorView.renderedLines.find('.line:eq(10) .invisible-character').text()).toBe eol - editorView.setCursorBufferPosition([9]) - editorView.indent() + editor.setCursorBufferPosition([9]) + editor.indent() expect(editorView.renderedLines.find('.line:eq(10) .indent-guide').length).toBe 2 expect(editorView.renderedLines.find('.line:eq(10) .indent-guide').text()).toBe "#{eol} " @@ -1711,13 +1713,13 @@ describe "EditorView", -> expect(editorView.renderedLines.find('.line:eq(3)').text()).toBe " var pivot = items.shift(), current, left = [], " expect(editorView.renderedLines.find('.line:eq(4)').text()).toBe "right = [];" - editorView.setCursorBufferPosition([3, 51], wrapAtSoftNewlines: true) + editor.setCursorBufferPosition([3, 51], wrapAtSoftNewlines: true) expect(editorView.find('.cursor').offset()).toEqual(editorView.renderedLines.find('.line:eq(4)').offset()) - editorView.setCursorBufferPosition([4, 0]) + editor.setCursorBufferPosition([4, 0]) expect(editorView.find('.cursor').offset()).toEqual(editorView.renderedLines.find('.line:eq(5)').offset()) - editorView.getSelection().setBufferRange([[6, 30], [6, 55]]) + editor.getSelection().setBufferRange([[6, 30], [6, 55]]) [region1, region2] = editorView.getSelectionView().regions expect(region1.offset().top).toBeCloseTo(editorView.renderedLines.find('.line:eq(7)').offset().top) expect(region2.offset().top).toBeCloseTo(editorView.renderedLines.find('.line:eq(8)').offset().top) @@ -1729,12 +1731,12 @@ describe "EditorView", -> expect(editorView.renderedLines.find('.line:eq(9)').text()).toBe ' }' it "changes the max line length and repositions the cursor when the window size changes", -> - editorView.setCursorBufferPosition([3, 60]) + editor.setCursorBufferPosition([3, 60]) setEditorWidthInChars(editorView, 40) expect(editorView.renderedLines.find('.line').length).toBe 19 expect(editorView.renderedLines.find('.line:eq(4)').text()).toBe "left = [], right = [];" expect(editorView.renderedLines.find('.line:eq(5)').text()).toBe " while(items.length > 0) {" - expect(editorView.bufferPositionForScreenPosition(editorView.getCursorScreenPosition())).toEqual [3, 60] + expect(editor.bufferPositionForScreenPosition(editor.getCursorScreenPosition())).toEqual [3, 60] it "does not wrap the lines of any newly assigned buffers", -> otherEditor = atom.project.openSync() @@ -1747,26 +1749,26 @@ describe "EditorView", -> expect(editorView.renderedLines.find('.line:eq(3)').text()).toBe ' var pivot = items.shift(), current, left = [], right = [];' it "allows the cursor to move down to the last line", -> - _.times editorView.getLastScreenRow(), -> editorView.moveCursorDown() - expect(editorView.getCursorScreenPosition()).toEqual [editorView.getLastScreenRow(), 0] - editorView.moveCursorDown() - expect(editorView.getCursorScreenPosition()).toEqual [editorView.getLastScreenRow(), 2] + _.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", -> - editorView.setCursorScreenPosition([11, 15]) - editorView.moveCursorUp() - expect(editorView.getCursorScreenPosition()).toEqual [10, 10] - editorView.moveCursorUp() - editorView.moveCursorUp() - expect(editorView.getCursorScreenPosition()).toEqual [8, 15] + 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", -> - editorView.setCursorScreenPosition([11, 0]) - editorView.moveCursorLeft() - expect(editorView.getCursorScreenPosition()).toEqual [10, 10] + editor.setCursorScreenPosition([11, 0]) + editor.moveCursorLeft() + expect(editor.getCursorScreenPosition()).toEqual [10, 10] - editorView.moveCursorRight() - expect(editorView.getCursorScreenPosition()).toEqual [11, 0] + editor.moveCursorRight() + expect(editor.getCursorScreenPosition()).toEqual [11, 0] it "calls .setWidthInChars() when the editor view is attached because now its dimensions are available to calculate it", -> otherEditor = new EditorView(editor: atom.project.openSync('sample.js')) @@ -1842,7 +1844,7 @@ describe "EditorView", -> describe "when there are folds", -> it "skips line numbers covered by the fold and updates them when the fold changes", -> - editorView.createFold(3, 5) + editor.createFold(3, 5) expect(editorView.gutter.find('.line-number:eq(3)').intValue()).toBe 4 expect(editorView.gutter.find('.line-number:eq(4)').intValue()).toBe 7 @@ -1856,14 +1858,14 @@ describe "EditorView", -> it "redraws gutter numbers when lines are unfolded", -> setEditorHeightInLines(editorView, 20) - fold = editorView.createFold(2, 12) + fold = editor.createFold(2, 12) expect(editorView.gutter.find('.line-number').length).toBe 3 fold.destroy() expect(editorView.gutter.find('.line-number').length).toBe 13 it "styles folded line numbers", -> - editorView.createFold(3, 5) + editor.createFold(3, 5) expect(editorView.gutter.find('.line-number.fold').length).toBe 1 expect(editorView.gutter.find('.line-number.fold:eq(0)').intValue()).toBe 4 @@ -1914,7 +1916,7 @@ describe "EditorView", -> it "doesn't highlight the only line", -> miniEditor = new EditorView(mini: true) miniEditor.attachToDom() - expect(miniEditor.getCursorBufferPosition().row).toBe 0 + expect(miniEditor.getEditor().getCursorBufferPosition().row).toBe 0 expect(miniEditor.find('.line.cursor-line').length).toBe 0 it "doesn't show the end of line invisible", -> @@ -1925,26 +1927,26 @@ describe "EditorView", -> expect(space).toBeTruthy() tab = miniEditor.invisibles?.tab expect(tab).toBeTruthy() - miniEditor.setText(" a line with tabs\tand spaces ") + miniEditor.getEditor().setText(" a line with tabs\tand spaces ") expect(miniEditor.renderedLines.find('.line').text()).toBe "#{space}a line with tabs#{tab} and spaces#{space}" it "doesn't show the indent guide", -> atom.config.set "editor.showIndentGuide", true miniEditor = new EditorView(mini: true) miniEditor.attachToDom() - miniEditor.setText(" and indented line") + miniEditor.getEditor().setText(" and indented line") expect(miniEditor.renderedLines.find('.indent-guide').length).toBe 0 it "lets you set the grammar", -> miniEditor = new EditorView(mini: true) - miniEditor.setText("var something") - previousTokens = miniEditor.lineForScreenRow(0).tokens - miniEditor.setGrammar(atom.syntax.selectGrammar('something.js')) - expect(miniEditor.getGrammar().name).toBe "JavaScript" - expect(previousTokens).not.toEqual miniEditor.lineForScreenRow(0).tokens + miniEditor.getEditor().setText("var something") + previousTokens = miniEditor.getEditor().lineForScreenRow(0).tokens + miniEditor.getEditor().setGrammar(atom.syntax.selectGrammar('something.js')) + expect(miniEditor.getEditor().getGrammar().name).toBe "JavaScript" + expect(previousTokens).not.toEqual miniEditor.getEditor().lineForScreenRow(0).tokens # doesn't allow regular editors to set grammars - expect(-> editorView.setGrammar()).toThrow() + expect(-> editor.setGrammar()).toThrow() describe "placeholderText", -> it "is hidden and shown when appropriate", -> @@ -1953,10 +1955,10 @@ describe "EditorView", -> expect(miniEditor.underlayer.find('.placeholder-text')).toExist() - miniEditor.setText("var something") + miniEditor.getEditor().setText("var something") expect(miniEditor.underlayer.find('.placeholder-text')).not.toExist() - miniEditor.setText("") + miniEditor.getEditor().setText("") expect(miniEditor.underlayer.find('.placeholder-text')).toExist() it "can be set", -> @@ -2026,13 +2028,13 @@ describe "EditorView", -> describe "when there is no wrapping", -> it "highlights the line where the initial cursor position is", -> - expect(editorView.getCursorBufferPosition().row).toBe 0 + expect(editor.getCursorBufferPosition().row).toBe 0 expect(editorView.find('.line-number.cursor-line.cursor-line-no-selection').length).toBe 1 expect(editorView.find('.line-number.cursor-line.cursor-line-no-selection').intValue()).toBe 1 it "updates the highlighted line when the cursor position changes", -> - editorView.setCursorBufferPosition([1,0]) - expect(editorView.getCursorBufferPosition().row).toBe 1 + editor.setCursorBufferPosition([1,0]) + expect(editor.getCursorBufferPosition().row).toBe 1 expect(editorView.find('.line-number.cursor-line.cursor-line-no-selection').length).toBe 1 expect(editorView.find('.line-number.cursor-line.cursor-line-no-selection').intValue()).toBe 2 @@ -2043,13 +2045,13 @@ describe "EditorView", -> setEditorWidthInChars(editorView, 20) it "highlights the line where the initial cursor position is", -> - expect(editorView.getCursorBufferPosition().row).toBe 0 + expect(editor.getCursorBufferPosition().row).toBe 0 expect(editorView.find('.line-number.cursor-line.cursor-line-no-selection').length).toBe 1 expect(editorView.find('.line-number.cursor-line.cursor-line-no-selection').intValue()).toBe 1 it "updates the highlighted line when the cursor position changes", -> - editorView.setCursorBufferPosition([1,0]) - expect(editorView.getCursorBufferPosition().row).toBe 1 + editor.setCursorBufferPosition([1,0]) + expect(editor.getCursorBufferPosition().row).toBe 1 expect(editorView.find('.line-number.cursor-line.cursor-line-no-selection').length).toBe 1 expect(editorView.find('.line-number.cursor-line.cursor-line-no-selection').intValue()).toBe 2 @@ -2058,24 +2060,24 @@ describe "EditorView", -> editorView.attachToDom(30) it "highlights the foreground of the gutter", -> - editorView.getSelection().setBufferRange([[0,0],[2,2]]) - expect(editorView.getSelection().isSingleScreenLine()).toBe false + editor.getSelection().setBufferRange([[0,0],[2,2]]) + expect(editor.getSelection().isSingleScreenLine()).toBe false expect(editorView.find('.line-number.cursor-line').length).toBe 3 it "doesn't highlight the background of the gutter", -> - editorView.getSelection().setBufferRange([[0,0],[2,0]]) - expect(editorView.getSelection().isSingleScreenLine()).toBe false + editor.getSelection().setBufferRange([[0,0],[2,0]]) + expect(editor.getSelection().isSingleScreenLine()).toBe false expect(editorView.find('.line-number.cursor-line.cursor-line-no-selection').length).toBe 0 it "doesn't highlight the last line if it ends at the beginning of a line", -> - editorView.getSelection().setBufferRange([[0,0],[1,0]]) - expect(editorView.getSelection().isSingleScreenLine()).toBe false + editor.getSelection().setBufferRange([[0,0],[1,0]]) + expect(editor.getSelection().isSingleScreenLine()).toBe false expect(editorView.find('.line-number.cursor-line').length).toBe 1 expect(editorView.find('.line-number.cursor-line').intValue()).toBe 1 it "when a newline is deleted with backspace, the line number of the new cursor position is highlighted", -> - editorView.setCursorScreenPosition([1,0]) - editorView.backspace() + editor.setCursorScreenPosition([1,0]) + editor.backspace() expect(editorView.find('.line-number.cursor-line').length).toBe 1 expect(editorView.find('.line-number.cursor-line').intValue()).toBe 1 @@ -2085,19 +2087,19 @@ describe "EditorView", -> describe "when there is no wrapping", -> it "highlights the line where the initial cursor position is", -> - expect(editorView.getCursorBufferPosition().row).toBe 0 + expect(editor.getCursorBufferPosition().row).toBe 0 expect(editorView.find('.line.cursor-line').length).toBe 1 expect(editorView.find('.line.cursor-line').text()).toBe buffer.lineForRow(0) it "updates the highlighted line when the cursor position changes", -> - editorView.setCursorBufferPosition([1,0]) - expect(editorView.getCursorBufferPosition().row).toBe 1 + editor.setCursorBufferPosition([1,0]) + expect(editor.getCursorBufferPosition().row).toBe 1 expect(editorView.find('.line.cursor-line').length).toBe 1 expect(editorView.find('.line.cursor-line').text()).toBe buffer.lineForRow(1) it "when a newline is deleted with backspace, the line of the new cursor position is highlighted", -> - editorView.setCursorScreenPosition([1,0]) - editorView.backspace() + editor.setCursorScreenPosition([1,0]) + editor.backspace() expect(editorView.find('.line.cursor-line').length).toBe 1 describe "when there is wrapping", -> @@ -2106,19 +2108,19 @@ describe "EditorView", -> setEditorWidthInChars(editorView, 20) it "highlights the line where the initial cursor position is", -> - expect(editorView.getCursorBufferPosition().row).toBe 0 + expect(editor.getCursorBufferPosition().row).toBe 0 expect(editorView.find('.line.cursor-line').length).toBe 1 expect(editorView.find('.line.cursor-line').text()).toBe 'var quicksort = ' it "updates the highlighted line when the cursor position changes", -> - editorView.setCursorBufferPosition([1,0]) - expect(editorView.getCursorBufferPosition().row).toBe 1 + editor.setCursorBufferPosition([1,0]) + expect(editor.getCursorBufferPosition().row).toBe 1 expect(editorView.find('.line.cursor-line').length).toBe 1 expect(editorView.find('.line.cursor-line').text()).toBe ' var sort = ' describe "when there is a non-empty selection", -> it "does not highlight the line", -> - editorView.setSelectedBufferRange([[1, 0], [1, 1]]) + editor.setSelectedBufferRange([[1, 0], [1, 1]]) expect(editorView.find('.line.cursor-line').length).toBe 0 describe "folding", -> @@ -2130,25 +2132,25 @@ describe "EditorView", -> describe "when a fold-selection event is triggered", -> it "folds the lines covered by the selection into a single line with a fold class and marker", -> - editorView.getSelection().setBufferRange([[4,29],[7,4]]) + editor.getSelection().setBufferRange([[4,29],[7,4]]) editorView.trigger 'editor:fold-selection' expect(editorView.renderedLines.find('.line:eq(4)')).toHaveClass('fold') expect(editorView.renderedLines.find('.line:eq(4) > .fold-marker')).toExist() expect(editorView.renderedLines.find('.line:eq(5)').text()).toBe '8' - expect(editorView.getSelection().isEmpty()).toBeTruthy() - expect(editorView.getCursorScreenPosition()).toEqual [5, 0] + expect(editor.getSelection().isEmpty()).toBeTruthy() + expect(editor.getCursorScreenPosition()).toEqual [5, 0] it "keeps the gutter line and the editor view line the same heights (regression)", -> - editorView.getSelection().setBufferRange([[4,29],[7,4]]) + editor.getSelection().setBufferRange([[4,29],[7,4]]) editorView.trigger 'editor:fold-selection' expect(editorView.gutter.find('.line-number:eq(4)').height()).toBe editorView.renderedLines.find('.line:eq(4)').height() describe "when a fold placeholder line is clicked", -> it "removes the associated fold and places the cursor at its beginning", -> - editorView.setCursorBufferPosition([3,0]) + editor.setCursorBufferPosition([3,0]) editor.createFold(3, 5) foldLine = editorView.find('.line.fold') @@ -2160,43 +2162,43 @@ describe "EditorView", -> expect(editorView.renderedLines.find('.line:eq(4)').text()).toMatch /4-+/ expect(editorView.renderedLines.find('.line:eq(5)').text()).toMatch /5/ - expect(editorView.getCursorBufferPosition()).toEqual [3, 0] + expect(editor.getCursorBufferPosition()).toEqual [3, 0] describe "when the unfold-current-row event is triggered when the cursor is on a fold placeholder line", -> it "removes the associated fold and places the cursor at its beginning", -> - editorView.setCursorBufferPosition([3,0]) + editor.setCursorBufferPosition([3,0]) editorView.trigger 'editor:fold-current-row' - editorView.setCursorBufferPosition([3,0]) + editor.setCursorBufferPosition([3,0]) editorView.trigger 'editor:unfold-current-row' expect(editorView.find('.fold')).not.toExist() expect(editorView.renderedLines.find('.line:eq(4)').text()).toMatch /4-+/ expect(editorView.renderedLines.find('.line:eq(5)').text()).toMatch /5/ - expect(editorView.getCursorBufferPosition()).toEqual [3, 0] + expect(editor.getCursorBufferPosition()).toEqual [3, 0] describe "when a selection starts/stops intersecting a fold", -> it "adds/removes the 'fold-selected' class to the fold's line element and hides the cursor if it is on the fold line", -> - editorView.createFold(2, 4) + editor.createFold(2, 4) - editorView.setSelectedBufferRange([[1, 0], [2, 0]], preserveFolds: true, isReversed: true) + editor.setSelectedBufferRange([[1, 0], [2, 0]], preserveFolds: true, isReversed: true) expect(editorView.lineElementForScreenRow(2)).toMatchSelector('.fold.fold-selected') - editorView.setSelectedBufferRange([[1, 0], [1, 1]], preserveFolds: true) + editor.setSelectedBufferRange([[1, 0], [1, 1]], preserveFolds: true) expect(editorView.lineElementForScreenRow(2)).not.toMatchSelector('.fold.fold-selected') - editorView.setSelectedBufferRange([[1, 0], [5, 0]], preserveFolds: true) + editor.setSelectedBufferRange([[1, 0], [5, 0]], preserveFolds: true) expect(editorView.lineElementForScreenRow(2)).toMatchSelector('.fold.fold-selected') - editorView.setCursorScreenPosition([3,0]) + editor.setCursorScreenPosition([3,0]) expect(editorView.lineElementForScreenRow(2)).not.toMatchSelector('.fold.fold-selected') - editorView.setCursorScreenPosition([2,0]) + editor.setCursorScreenPosition([2,0]) expect(editorView.lineElementForScreenRow(2)).toMatchSelector('.fold.fold-selected') expect(editorView.find('.cursor')).toBeHidden() - editorView.setCursorScreenPosition([3,0]) + editor.setCursorScreenPosition([3,0]) expect(editorView.find('.cursor')).toBeVisible() describe "when a selected fold is scrolled into view (and the fold line was not previously rendered)", -> @@ -2204,8 +2206,8 @@ describe "EditorView", -> setEditorHeightInLines(editorView, 5) editorView.resetDisplay() - editorView.createFold(2, 4) - editorView.setSelectedBufferRange([[1, 0], [5, 0]], preserveFolds: true) + editor.createFold(2, 4) + editor.setSelectedBufferRange([[1, 0], [5, 0]], preserveFolds: true) expect(editorView.renderedLines.find('.fold.fold-selected')).toExist() editorView.scrollToBottom() @@ -2219,13 +2221,13 @@ describe "EditorView", -> editorView.attachToDom() it "moves to the last line when page down is repeated from the first line", -> - rows = editorView.getLineCount() - 1 + rows = editor.getLineCount() - 1 expect(rows).toBeGreaterThan(0) - row = editorView.getCursor().getScreenPosition().row + row = editor.getCursor().getScreenPosition().row expect(row).toBe(0) while row < rows editorView.pageDown() - newRow = editorView.getCursor().getScreenPosition().row + newRow = editor.getCursor().getScreenPosition().row expect(newRow).toBeGreaterThan(row) if (newRow <= row) break @@ -2234,12 +2236,12 @@ describe "EditorView", -> expect(editorView.getLastVisibleScreenRow()).toBe(rows) it "moves to the first line when page up is repeated from the last line", -> - editorView.moveCursorToBottom() - row = editorView.getCursor().getScreenPosition().row + editor.moveCursorToBottom() + row = editor.getCursor().getScreenPosition().row expect(row).toBeGreaterThan(0) while row > 0 editorView.pageUp() - newRow = editorView.getCursor().getScreenPosition().row + newRow = editor.getCursor().getScreenPosition().row expect(newRow).toBeLessThan(row) if (newRow >= row) break @@ -2248,11 +2250,11 @@ describe "EditorView", -> expect(editorView.getFirstVisibleScreenRow()).toBe(0) it "resets to original position when down is followed by up", -> - expect(editorView.getCursor().getScreenPosition().row).toBe(0) + expect(editor.getCursor().getScreenPosition().row).toBe(0) editorView.pageDown() - expect(editorView.getCursor().getScreenPosition().row).toBeGreaterThan(0) + expect(editor.getCursor().getScreenPosition().row).toBeGreaterThan(0) editorView.pageUp() - expect(editorView.getCursor().getScreenPosition().row).toBe(0) + expect(editor.getCursor().getScreenPosition().row).toBe(0) expect(editorView.getFirstVisibleScreenRow()).toBe(0) describe ".checkoutHead()", -> @@ -2261,17 +2263,18 @@ describe "EditorView", -> beforeEach -> filePath = atom.project.resolve('git/working-dir/file.txt') originalPathText = fs.readFileSync(filePath, 'utf8') - editorView.edit(atom.project.openSync(filePath)) + editor = atom.project.openSync(filePath) + editorView.edit(editor) afterEach -> fs.writeFileSync(filePath, originalPathText) it "restores the contents of the editor view to the HEAD revision", -> - editorView.setText('') - editorView.getBuffer().save() + editor.setText('') + editor.save() fileChangeHandler = jasmine.createSpy('fileChange') - editorView.getBuffer().file.on 'contents-changed', fileChangeHandler + editor.getBuffer().file.on 'contents-changed', fileChangeHandler editorView.checkoutHead() @@ -2279,7 +2282,7 @@ describe "EditorView", -> fileChangeHandler.callCount > 0 runs -> - expect(editorView.getText()).toBe(originalPathText) + expect(editor.getText()).toBe(originalPathText) describe ".pixelPositionForBufferPosition(position)", -> describe "when the editor view is detached", -> @@ -2316,22 +2319,22 @@ describe "EditorView", -> describe "when single clicking", -> it "moves the cursor to the start of the selected line", -> - expect(editorView.getCursorScreenPosition()).toEqual [0,0] + expect(editor.getCursorScreenPosition()).toEqual [0,0] event = $.Event("mousedown") event.pageY = editorView.gutter.find(".line-number:eq(1)").offset().top event.originalEvent = {detail: 1} editorView.gutter.find(".line-number:eq(1)").trigger event - expect(editorView.getCursorScreenPosition()).toEqual [1,0] + expect(editor.getCursorScreenPosition()).toEqual [1,0] describe "when shift-clicking", -> it "selects to the start of the selected line", -> - expect(editorView.getSelection().getScreenRange()).toEqual [[0,0], [0,0]] + expect(editor.getSelection().getScreenRange()).toEqual [[0,0], [0,0]] event = $.Event("mousedown") event.pageY = editorView.gutter.find(".line-number:eq(1)").offset().top event.originalEvent = {detail: 1} event.shiftKey = true editorView.gutter.find(".line-number:eq(1)").trigger event - expect(editorView.getSelection().getScreenRange()).toEqual [[0,0], [2,0]] + expect(editor.getSelection().getScreenRange()).toEqual [[0,0], [2,0]] describe "when mousing down and then moving across multiple lines before mousing up", -> describe "when selecting from top to bottom", -> @@ -2348,7 +2351,7 @@ describe "EditorView", -> $(document).trigger 'mouseup' - expect(editorView.getSelection().getScreenRange()).toEqual [[1,0], [6,0]] + expect(editor.getSelection().getScreenRange()).toEqual [[1,0], [6,0]] describe "when selecting from bottom to top", -> it "selects the lines", -> @@ -2364,24 +2367,25 @@ describe "EditorView", -> $(document).trigger 'mouseup' - expect(editorView.getSelection().getScreenRange()).toEqual [[1,0], [6,0]] + expect(editor.getSelection().getScreenRange()).toEqual [[1,0], [6,0]] describe "when clicking below the last line", -> beforeEach -> editorView.attachToDom() it "move the cursor to the end of the file", -> - expect(editorView.getCursorScreenPosition()).toEqual [0,0] + expect(editor.getCursorScreenPosition()).toEqual [0,0] event = mousedownEvent(editorView: editorView, point: [Infinity, 10]) editorView.underlayer.trigger event - expect(editorView.getCursorScreenPosition()).toEqual [12,2] + expect(editor.getCursorScreenPosition()).toEqual [12,2] it "selects to the end of the files when shift is pressed", -> - expect(editorView.getSelection().getScreenRange()).toEqual [[0,0], [0,0]] + expect(editor.getSelection().getScreenRange()).toEqual [[0,0], [0,0]] event = mousedownEvent(editorView: editorView, point: [Infinity, 10], shiftKey: true) editorView.underlayer.trigger event - expect(editorView.getSelection().getScreenRange()).toEqual [[0,0], [12,2]] + expect(editor.getSelection().getScreenRange()).toEqual [[0,0], [12,2]] + # TODO: Move to editor-spec describe ".reloadGrammar()", -> [filePath] = [] @@ -2394,11 +2398,12 @@ describe "EditorView", -> fs.removeSync(filePath) if fs.existsSync(filePath) it "updates all the rendered lines when the grammar changes", -> - editorView.edit(atom.project.openSync(filePath)) - expect(editorView.getGrammar().name).toBe 'Plain Text' + editor = atom.project.openSync(filePath) + editorView.edit(editor) + expect(editor.getGrammar().name).toBe 'Plain Text' atom.syntax.setGrammarOverrideForPath(filePath, 'source.js') - editorView.reloadGrammar() - expect(editorView.getGrammar().name).toBe 'JavaScript' + editor.reloadGrammar() + expect(editor.getGrammar().name).toBe 'JavaScript' tokenizedBuffer = editorView.editor.displayBuffer.tokenizedBuffer line0 = tokenizedBuffer.lineForScreenRow(0) @@ -2406,24 +2411,25 @@ describe "EditorView", -> expect(line0.tokens[0]).toEqual(value: 'var', scopes: ['source.js', 'storage.modifier.js']) it "doesn't update the rendered lines when the grammar doesn't change", -> - expect(editorView.getGrammar().name).toBe 'JavaScript' + expect(editor.getGrammar().name).toBe 'JavaScript' spyOn(editorView, 'updateDisplay').andCallThrough() - editorView.reloadGrammar() - expect(editorView.reloadGrammar()).toBeFalsy() + editor.reloadGrammar() + expect(editor.reloadGrammar()).toBeFalsy() expect(editorView.updateDisplay).not.toHaveBeenCalled() - expect(editorView.getGrammar().name).toBe 'JavaScript' + expect(editor.getGrammar().name).toBe 'JavaScript' it "emits an editor:grammar-changed event when updated", -> - editorView.edit(atom.project.openSync(filePath)) + editor = atom.project.openSync(filePath) + editorView.edit(editor) eventHandler = jasmine.createSpy('eventHandler') editorView.on('editor:grammar-changed', eventHandler) - editorView.reloadGrammar() + editor.reloadGrammar() expect(eventHandler).not.toHaveBeenCalled() atom.syntax.setGrammarOverrideForPath(filePath, 'source.js') - editorView.reloadGrammar() + editor.reloadGrammar() expect(eventHandler).toHaveBeenCalled() describe ".replaceSelectedText()", -> @@ -2434,7 +2440,7 @@ describe "EditorView", -> replaced = true 'new' - editorView.moveCursorToTop() + editor.moveCursorToTop() edited = editorView.replaceSelectedText(replacer) expect(replaced).toBe false expect(edited).toBe false @@ -2446,8 +2452,8 @@ describe "EditorView", -> replaced = true 'new' - editorView.moveCursorToTop() - editorView.selectToEndOfLine() + editor.moveCursorToTop() + editor.selectToEndOfLine() edited = editorView.replaceSelectedText(replacer) expect(replaced).toBe true expect(edited).toBe true @@ -2459,8 +2465,8 @@ describe "EditorView", -> replaced = true null - editorView.moveCursorToTop() - editorView.selectToEndOfLine() + editor.moveCursorToTop() + editor.selectToEndOfLine() edited = editorView.replaceSelectedText(replacer) expect(replaced).toBe true expect(edited).toBe false @@ -2472,8 +2478,8 @@ describe "EditorView", -> replaced = true undefined - editorView.moveCursorToTop() - editorView.selectToEndOfLine() + editor.moveCursorToTop() + editor.selectToEndOfLine() edited = editorView.replaceSelectedText(replacer) expect(replaced).toBe true expect(edited).toBe false @@ -2481,33 +2487,33 @@ describe "EditorView", -> describe "when editor:copy-path is triggered", -> it "copies the absolute path to the editor view's file to the pasteboard", -> editorView.trigger 'editor:copy-path' - expect(atom.pasteboard.read()[0]).toBe editorView.getPath() + expect(atom.pasteboard.read()[0]).toBe editor.getPath() describe "when editor:move-line-up is triggered", -> describe "when there is no selection", -> it "moves the line where the cursor is up", -> - editorView.setCursorBufferPosition([1,0]) + editor.setCursorBufferPosition([1,0]) editorView.trigger 'editor:move-line-up' expect(buffer.lineForRow(0)).toBe ' var sort = function(items) {' expect(buffer.lineForRow(1)).toBe 'var quicksort = function () {' it "moves the cursor to the new row and the same column", -> - editorView.setCursorBufferPosition([1,2]) + editor.setCursorBufferPosition([1,2]) editorView.trigger 'editor:move-line-up' - expect(editorView.getCursorBufferPosition()).toEqual [0,2] + expect(editor.getCursorBufferPosition()).toEqual [0,2] describe "where there is a selection", -> describe "when the selection falls inside the line", -> it "maintains the selection", -> - editorView.setSelectedBufferRange([[1, 2], [1, 5]]) - expect(editorView.getSelectedText()).toBe 'var' + editor.setSelectedBufferRange([[1, 2], [1, 5]]) + expect(editor.getSelectedText()).toBe 'var' editorView.trigger 'editor:move-line-up' - expect(editorView.getSelectedBufferRange()).toEqual [[0, 2], [0, 5]] - expect(editorView.getSelectedText()).toBe 'var' + expect(editor.getSelectedBufferRange()).toEqual [[0, 2], [0, 5]] + expect(editor.getSelectedText()).toBe 'var' describe "where there are multiple lines selected", -> it "moves the selected lines up", -> - editorView.setSelectedBufferRange([[2, 0], [3, Infinity]]) + editor.setSelectedBufferRange([[2, 0], [3, Infinity]]) editorView.trigger 'editor:move-line-up' expect(buffer.lineForRow(0)).toBe 'var quicksort = function () {' expect(buffer.lineForRow(1)).toBe ' if (items.length <= 1) return items;' @@ -2515,20 +2521,20 @@ describe "EditorView", -> expect(buffer.lineForRow(3)).toBe ' var sort = function(items) {' it "maintains the selection", -> - editorView.setSelectedBufferRange([[2, 0], [3, 62]]) + editor.setSelectedBufferRange([[2, 0], [3, 62]]) editorView.trigger 'editor:move-line-up' - expect(editorView.getSelectedBufferRange()).toEqual [[1, 0], [2, 62]] + expect(editor.getSelectedBufferRange()).toEqual [[1, 0], [2, 62]] describe "when the last line is selected", -> it "moves the selected line up", -> - editorView.setSelectedBufferRange([[12, 0], [12, Infinity]]) + editor.setSelectedBufferRange([[12, 0], [12, Infinity]]) editorView.trigger 'editor:move-line-up' expect(buffer.lineForRow(11)).toBe '};' expect(buffer.lineForRow(12)).toBe ' return sort(Array.apply(this, arguments));' describe "when the last two lines are selected", -> it "moves the selected lines up", -> - editorView.setSelectedBufferRange([[11, 0], [12, Infinity]]) + editor.setSelectedBufferRange([[11, 0], [12, Infinity]]) editorView.trigger 'editor:move-line-up' expect(buffer.lineForRow(10)).toBe ' return sort(Array.apply(this, arguments));' expect(buffer.lineForRow(11)).toBe '};' @@ -2536,48 +2542,48 @@ describe "EditorView", -> describe "when the cursor is on the first line", -> it "does not move the line", -> - editorView.setCursorBufferPosition([0,0]) - originalText = editorView.getText() + editor.setCursorBufferPosition([0,0]) + originalText = editor.getText() editorView.trigger 'editor:move-line-up' - expect(editorView.getText()).toBe originalText + expect(editor.getText()).toBe originalText describe "when the cursor is on the trailing newline", -> it "does not move the line", -> - editorView.moveCursorToBottom() - editorView.insertNewline() - editorView.moveCursorToBottom() - originalText = editorView.getText() + editor.moveCursorToBottom() + editor.insertNewline() + editor.moveCursorToBottom() + originalText = editor.getText() editorView.trigger 'editor:move-line-up' - expect(editorView.getText()).toBe originalText + expect(editor.getText()).toBe originalText describe "when the cursor is on a folded line", -> it "moves all lines in the fold up and preserves the fold", -> - editorView.setCursorBufferPosition([4, 0]) - editorView.foldCurrentRow() + editor.setCursorBufferPosition([4, 0]) + editor.foldCurrentRow() editorView.trigger 'editor:move-line-up' expect(buffer.lineForRow(3)).toBe ' while(items.length > 0) {' expect(buffer.lineForRow(7)).toBe ' var pivot = items.shift(), current, left = [], right = [];' - expect(editorView.getSelectedBufferRange()).toEqual [[3, 0], [3, 0]] - expect(editorView.isFoldedAtScreenRow(3)).toBeTruthy() + expect(editor.getSelectedBufferRange()).toEqual [[3, 0], [3, 0]] + expect(editor.isFoldedAtScreenRow(3)).toBeTruthy() describe "when the selection contains a folded and unfolded line", -> it "moves the selected lines up and preserves the fold", -> - editorView.setCursorBufferPosition([4, 0]) - editorView.foldCurrentRow() - editorView.setCursorBufferPosition([3, 4]) - editorView.selectDown() - expect(editorView.isFoldedAtScreenRow(4)).toBeTruthy() + editor.setCursorBufferPosition([4, 0]) + editor.foldCurrentRow() + editor.setCursorBufferPosition([3, 4]) + editor.selectDown() + expect(editor.isFoldedAtScreenRow(4)).toBeTruthy() editorView.trigger 'editor:move-line-up' expect(buffer.lineForRow(2)).toBe ' var pivot = items.shift(), current, left = [], right = [];' expect(buffer.lineForRow(3)).toBe ' while(items.length > 0) {' - expect(editorView.getSelectedBufferRange()).toEqual [[2, 4], [3, 0]] - expect(editorView.isFoldedAtScreenRow(3)).toBeTruthy() + expect(editor.getSelectedBufferRange()).toEqual [[2, 4], [3, 0]] + expect(editor.isFoldedAtScreenRow(3)).toBeTruthy() describe "when an entire line is selected including the newline", -> it "moves the selected line up", -> - editorView.setCursorBufferPosition([1]) - editorView.selectToEndOfLine() - editorView.selectRight() + editor.setCursorBufferPosition([1]) + editor.selectToEndOfLine() + editor.selectRight() editorView.trigger 'editor:move-line-up' expect(buffer.lineForRow(0)).toBe ' var sort = function(items) {' expect(buffer.lineForRow(1)).toBe 'var quicksort = function () {' @@ -2585,26 +2591,26 @@ describe "EditorView", -> describe "when editor:move-line-down is triggered", -> describe "when there is no selection", -> it "moves the line where the cursor is down", -> - editorView.setCursorBufferPosition([0, 0]) + editor.setCursorBufferPosition([0, 0]) editorView.trigger 'editor:move-line-down' expect(buffer.lineForRow(0)).toBe ' var sort = function(items) {' expect(buffer.lineForRow(1)).toBe 'var quicksort = function () {' it "moves the cursor to the new row and the same column", -> - editorView.setCursorBufferPosition([0, 2]) + editor.setCursorBufferPosition([0, 2]) editorView.trigger 'editor:move-line-down' - expect(editorView.getCursorBufferPosition()).toEqual [1, 2] + expect(editor.getCursorBufferPosition()).toEqual [1, 2] describe "when the cursor is on the last line", -> it "does not move the line", -> - editorView.moveCursorToBottom() + editor.moveCursorToBottom() editorView.trigger 'editor:move-line-down' expect(buffer.lineForRow(12)).toBe '};' - expect(editorView.getSelectedBufferRange()).toEqual [[12, 2], [12, 2]] + expect(editor.getSelectedBufferRange()).toEqual [[12, 2], [12, 2]] describe "when the cursor is on the second to last line", -> it "moves the line down", -> - editorView.setCursorBufferPosition([11, 0]) + editor.setCursorBufferPosition([11, 0]) editorView.trigger 'editor:move-line-down' expect(buffer.lineForRow(11)).toBe '};' expect(buffer.lineForRow(12)).toBe ' return sort(Array.apply(this, arguments));' @@ -2612,26 +2618,26 @@ describe "EditorView", -> describe "when the cursor is on the second to last line and the last line is empty", -> it "does not move the line", -> - editorView.moveCursorToBottom() - editorView.insertNewline() - editorView.setCursorBufferPosition([12, 2]) + editor.moveCursorToBottom() + editor.insertNewline() + editor.setCursorBufferPosition([12, 2]) editorView.trigger 'editor:move-line-down' expect(buffer.lineForRow(12)).toBe '};' expect(buffer.lineForRow(13)).toBe '' - expect(editorView.getSelectedBufferRange()).toEqual [[12, 2], [12, 2]] + expect(editor.getSelectedBufferRange()).toEqual [[12, 2], [12, 2]] describe "where there is a selection", -> describe "when the selection falls inside the line", -> it "maintains the selection", -> - editorView.setSelectedBufferRange([[1, 2], [1, 5]]) - expect(editorView.getSelectedText()).toBe 'var' + editor.setSelectedBufferRange([[1, 2], [1, 5]]) + expect(editor.getSelectedText()).toBe 'var' editorView.trigger 'editor:move-line-down' - expect(editorView.getSelectedBufferRange()).toEqual [[2, 2], [2, 5]] - expect(editorView.getSelectedText()).toBe 'var' + expect(editor.getSelectedBufferRange()).toEqual [[2, 2], [2, 5]] + expect(editor.getSelectedText()).toBe 'var' describe "where there are multiple lines selected", -> it "moves the selected lines down", -> - editorView.setSelectedBufferRange([[2, 0], [3, Infinity]]) + editor.setSelectedBufferRange([[2, 0], [3, Infinity]]) editorView.trigger 'editor:move-line-down' expect(buffer.lineForRow(2)).toBe ' while(items.length > 0) {' expect(buffer.lineForRow(3)).toBe ' if (items.length <= 1) return items;' @@ -2639,39 +2645,39 @@ describe "EditorView", -> expect(buffer.lineForRow(5)).toBe ' current = items.shift();' it "maintains the selection", -> - editorView.setSelectedBufferRange([[2, 0], [3, 62]]) + editor.setSelectedBufferRange([[2, 0], [3, 62]]) editorView.trigger 'editor:move-line-down' - expect(editorView.getSelectedBufferRange()).toEqual [[3, 0], [4, 62]] + expect(editor.getSelectedBufferRange()).toEqual [[3, 0], [4, 62]] describe "when the cursor is on a folded line", -> it "moves all lines in the fold down and preserves the fold", -> - editorView.setCursorBufferPosition([4, 0]) - editorView.foldCurrentRow() + editor.setCursorBufferPosition([4, 0]) + editor.foldCurrentRow() editorView.trigger 'editor:move-line-down' expect(buffer.lineForRow(4)).toBe ' return sort(left).concat(pivot).concat(sort(right));' expect(buffer.lineForRow(5)).toBe ' while(items.length > 0) {' - expect(editorView.getSelectedBufferRange()).toEqual [[5, 0], [5, 0]] - expect(editorView.isFoldedAtScreenRow(5)).toBeTruthy() + expect(editor.getSelectedBufferRange()).toEqual [[5, 0], [5, 0]] + expect(editor.isFoldedAtScreenRow(5)).toBeTruthy() describe "when the selection contains a folded and unfolded line", -> it "moves the selected lines down and preserves the fold", -> - editorView.setCursorBufferPosition([4, 0]) - editorView.foldCurrentRow() - editorView.setCursorBufferPosition([3, 4]) - editorView.selectDown() - expect(editorView.isFoldedAtScreenRow(4)).toBeTruthy() + editor.setCursorBufferPosition([4, 0]) + editor.foldCurrentRow() + editor.setCursorBufferPosition([3, 4]) + editor.selectDown() + expect(editor.isFoldedAtScreenRow(4)).toBeTruthy() editorView.trigger 'editor:move-line-down' expect(buffer.lineForRow(3)).toBe ' return sort(left).concat(pivot).concat(sort(right));' expect(buffer.lineForRow(4)).toBe ' var pivot = items.shift(), current, left = [], right = [];' expect(buffer.lineForRow(5)).toBe ' while(items.length > 0) {' - expect(editorView.getSelectedBufferRange()).toEqual [[4, 4], [5, 0]] - expect(editorView.isFoldedAtScreenRow(5)).toBeTruthy() + expect(editor.getSelectedBufferRange()).toEqual [[4, 4], [5, 0]] + expect(editor.isFoldedAtScreenRow(5)).toBeTruthy() describe "when an entire line is selected including the newline", -> it "moves the selected line down", -> - editorView.setCursorBufferPosition([1]) - editorView.selectToEndOfLine() - editorView.selectRight() + editor.setCursorBufferPosition([1]) + editor.selectToEndOfLine() + editor.selectRight() editorView.trigger 'editor:move-line-down' expect(buffer.lineForRow(1)).toBe ' if (items.length <= 1) return items;' expect(buffer.lineForRow(2)).toBe ' var sort = function(items) {' @@ -2680,20 +2686,20 @@ describe "EditorView", -> describe "where there is no selection", -> describe "when the cursor isn't on a folded line", -> it "duplicates the current line below and moves the cursor down one row", -> - editorView.setCursorBufferPosition([0, 5]) + editor.setCursorBufferPosition([0, 5]) editorView.trigger 'editor:duplicate-line' expect(buffer.lineForRow(0)).toBe 'var quicksort = function () {' expect(buffer.lineForRow(1)).toBe 'var quicksort = function () {' - expect(editorView.getCursorBufferPosition()).toEqual [1, 5] + expect(editor.getCursorBufferPosition()).toEqual [1, 5] describe "when the cursor is on a folded line", -> it "duplicates the entire fold before and moves the cursor to the new fold", -> - editorView.setCursorBufferPosition([4]) - editorView.foldCurrentRow() + editor.setCursorBufferPosition([4]) + editor.foldCurrentRow() editorView.trigger 'editor:duplicate-line' - expect(editorView.getCursorScreenPosition()).toEqual [5] - expect(editorView.isFoldedAtScreenRow(4)).toBeTruthy() - expect(editorView.isFoldedAtScreenRow(5)).toBeTruthy() + expect(editor.getCursorScreenPosition()).toEqual [5] + expect(editor.isFoldedAtScreenRow(4)).toBeTruthy() + expect(editor.isFoldedAtScreenRow(5)).toBeTruthy() expect(buffer.lineForRow(8)).toBe ' while(items.length > 0) {' expect(buffer.lineForRow(9)).toBe ' current = items.shift();' expect(buffer.lineForRow(10)).toBe ' current < pivot ? left.push(current) : right.push(current);' @@ -2701,39 +2707,39 @@ describe "EditorView", -> describe "when the cursor is on the last line and it doesn't have a trailing newline", -> it "inserts a newline and the duplicated line", -> - editorView.moveCursorToBottom() + editor.moveCursorToBottom() editorView.trigger 'editor:duplicate-line' expect(buffer.lineForRow(12)).toBe '};' expect(buffer.lineForRow(13)).toBe '};' expect(buffer.lineForRow(14)).toBeUndefined() - expect(editorView.getCursorBufferPosition()).toEqual [13, 2] + expect(editor.getCursorBufferPosition()).toEqual [13, 2] describe "when the cursor in on the last line and it is only a newline", -> it "duplicates the current line below and moves the cursor down one row", -> - editorView.moveCursorToBottom() - editorView.insertNewline() - editorView.moveCursorToBottom() + editor.moveCursorToBottom() + editor.insertNewline() + editor.moveCursorToBottom() editorView.trigger 'editor:duplicate-line' expect(buffer.lineForRow(13)).toBe '' expect(buffer.lineForRow(14)).toBe '' expect(buffer.lineForRow(15)).toBeUndefined() - expect(editorView.getCursorBufferPosition()).toEqual [14, 0] + expect(editor.getCursorBufferPosition()).toEqual [14, 0] describe "when the cursor is on the second to last line and the last line only a newline", -> it "duplicates the current line below and moves the cursor down one row", -> - editorView.moveCursorToBottom() - editorView.insertNewline() - editorView.setCursorBufferPosition([12]) + editor.moveCursorToBottom() + editor.insertNewline() + editor.setCursorBufferPosition([12]) editorView.trigger 'editor:duplicate-line' expect(buffer.lineForRow(12)).toBe '};' expect(buffer.lineForRow(13)).toBe '};' expect(buffer.lineForRow(14)).toBe '' expect(buffer.lineForRow(15)).toBeUndefined() - expect(editorView.getCursorBufferPosition()).toEqual [13, 0] + expect(editor.getCursorBufferPosition()).toEqual [13, 0] describe "when the escape key is pressed on the editor view", -> it "clears multiple selections if there are any, and otherwise allows other bindings to be handled", -> - atom.keymap.bindKeys 'name', '.editor', 'escape': 'test-event' + atom.keymap.bindKeys 'name', '.editor', {'escape': 'test-event'} testEventHandler = jasmine.createSpy("testEventHandler") editorView.on 'test-event', testEventHandler @@ -2759,8 +2765,8 @@ describe "EditorView", -> editorView.getPane().activateItem(view) expect(editorView.isVisible()).toBeFalsy() - editorView.setText('hidden changes') - editorView.setCursorBufferPosition([0,4]) + editor.setText('hidden changes') + editor.setCursorBufferPosition([0,4]) displayUpdatedHandler = jasmine.createSpy("displayUpdatedHandler") editorView.on 'editor:display-updated', displayUpdatedHandler @@ -2776,8 +2782,8 @@ describe "EditorView", -> it "redraws the editor view when it is next reattached", -> editorView.attachToDom() editorView.hide() - editorView.setText('hidden changes') - editorView.setCursorBufferPosition([0,4]) + editor.setText('hidden changes') + editor.setCursorBufferPosition([0,4]) editorView.detach() displayUpdatedHandler = jasmine.createSpy("displayUpdatedHandler") @@ -2794,7 +2800,7 @@ describe "EditorView", -> describe "editor:scroll-to-cursor", -> it "scrolls to and centers the editor view on the cursor's position", -> editorView.attachToDom(heightInLines: 3) - editorView.setCursorBufferPosition([1, 2]) + editor.setCursorBufferPosition([1, 2]) editorView.scrollToBottom() expect(editorView.getFirstVisibleScreenRow()).not.toBe 0 expect(editorView.getLastVisibleScreenRow()).not.toBe 2 @@ -2817,10 +2823,10 @@ describe "EditorView", -> describe "when setInvisibles is toggled (regression)", -> it "renders inserted newlines properly", -> editorView.setShowInvisibles(true) - editorView.setCursorBufferPosition([0, 0]) + editor.setCursorBufferPosition([0, 0]) editorView.attachToDom(heightInLines: 20) editorView.setShowInvisibles(false) - editorView.insertText("\n") + editor.insertText("\n") for rowNumber in [1..5] expect(editorView.lineElementForScreenRow(rowNumber).text()).toBe buffer.lineForRow(rowNumber) @@ -2838,7 +2844,7 @@ describe "EditorView", -> describe "when soft wrap is enabled", -> it "correctly calculates the the position left for a column", -> editor.setSoftWrap(true) - editorView.setText('lllll 00000') + editor.setText('lllll 00000') editorView.setFontFamily('serif') editorView.setFontSize(10) editorView.attachToDom() @@ -2855,7 +2861,7 @@ describe "EditorView", -> describe "when the editor contains hard tabs", -> it "correctly calculates the the position left for a column", -> - editorView.setText('\ttest') + editor.setText('\ttest') editorView.attachToDom() expect(editorView.pixelPositionForScreenPosition([0, editor.getTabLength()]).left).toEqual 20 diff --git a/spec/jasmine-helper.coffee b/spec/jasmine-helper.coffee index c57a5dc37..5ff4ee868 100644 --- a/spec/jasmine-helper.coffee +++ b/spec/jasmine-helper.coffee @@ -4,8 +4,7 @@ module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) -> {$, $$} = require 'atom' window[key] = value for key, value of require '../vendor/jasmine' - require 'jasmine-focused' - require 'jasmine-tagged' + {TerminalReporter} = require 'jasmine-tagged' TimeReporter = require './time-reporter' timeReporter = new TimeReporter() @@ -18,8 +17,7 @@ module.exports.runSpecSuite = (specSuite, logFile, logErrors=true) -> process.stderr.write(str) if atom.getLoadSettings().exitWhenDone - {jasmineNode} = require 'jasmine-node/lib/jasmine-node/reporter' - reporter = new jasmineNode.TerminalReporter + reporter = new TerminalReporter print: (str) -> log(str) onComplete: (runner) -> diff --git a/spec/pane-container-model-spec.coffee b/spec/pane-container-spec.coffee similarity index 100% rename from spec/pane-container-model-spec.coffee rename to spec/pane-container-spec.coffee diff --git a/spec/pane-container-view-spec.coffee b/spec/pane-container-view-spec.coffee index fa0bcb83d..c68180c1d 100644 --- a/spec/pane-container-view-spec.coffee +++ b/spec/pane-container-view-spec.coffee @@ -42,7 +42,7 @@ describe "PaneContainerView", -> describe ".focusPreviousPane()", -> it "focuses the pane preceding the focused pane or the last pane if no pane has focus", -> container.attachToDom() - $(document.body).focus() # clear focus + container.getPanes()[0].focus() # activate first pane container.focusPreviousPane() expect(pane3.activeItem).toMatchSelector ':focus' @@ -121,7 +121,7 @@ describe "PaneContainerView", -> describe "serialization", -> it "can be serialized and deserialized, and correctly adjusts dimensions of deserialized panes after attach", -> - newContainer = atom.deserializers.deserialize(container.serialize()) + newContainer = new PaneContainerView(container.model.testSerialization()) expect(newContainer.find('.pane-row > :contains(1)')).toExist() expect(newContainer.find('.pane-row > .pane-column > :contains(2)')).toExist() expect(newContainer.find('.pane-row > .pane-column > :contains(3)')).toExist() @@ -133,7 +133,7 @@ describe "PaneContainerView", -> it "removes empty panes on deserialization", -> # only deserialize pane 1's view successfully TestView.deserialize = ({name}) -> new TestView(name) if name is '1' - newContainer = atom.deserializers.deserialize(container.serialize()) + newContainer = new PaneContainerView(container.model.testSerialization()) expect(newContainer.find('.pane-row, .pane-column')).not.toExist() expect(newContainer.find('> :contains(1)')).toExist() diff --git a/spec/pane-model-spec.coffee b/spec/pane-model-spec.coffee deleted file mode 100644 index 0c15683a4..000000000 --- a/spec/pane-model-spec.coffee +++ /dev/null @@ -1,134 +0,0 @@ -{Model} = require 'theorist' -Pane = require '../src/pane' -PaneAxis = require '../src/pane-axis' -PaneContainer = require '../src/pane-container' - -describe "Pane", -> - describe "split methods", -> - [pane1, container] = [] - - beforeEach -> - pane1 = new Pane(items: ["A"]) - container = new PaneContainer(root: pane1) - - describe "::splitLeft(params)", -> - describe "when the parent is the container root", -> - it "replaces itself with a row and inserts a new pane to the left of itself", -> - pane2 = pane1.splitLeft(items: ["B"]) - pane3 = pane1.splitLeft(items: ["C"]) - expect(container.root.orientation).toBe 'horizontal' - expect(container.root.children).toEqual [pane2, pane3, pane1] - - describe "when the parent is a column", -> - it "replaces itself with a row and inserts a new pane to the left of itself", -> - pane1.splitDown() - pane2 = pane1.splitLeft(items: ["B"]) - pane3 = pane1.splitLeft(items: ["C"]) - row = container.root.children[0] - expect(row.orientation).toBe 'horizontal' - expect(row.children).toEqual [pane2, pane3, pane1] - - describe "::splitRight(params)", -> - describe "when the parent is the container root", -> - it "replaces itself with a row and inserts a new pane to the right of itself", -> - pane2 = pane1.splitRight(items: ["B"]) - pane3 = pane1.splitRight(items: ["C"]) - expect(container.root.orientation).toBe 'horizontal' - expect(container.root.children).toEqual [pane1, pane3, pane2] - - describe "when the parent is a column", -> - it "replaces itself with a row and inserts a new pane to the right of itself", -> - pane1.splitDown() - pane2 = pane1.splitRight(items: ["B"]) - pane3 = pane1.splitRight(items: ["C"]) - row = container.root.children[0] - expect(row.orientation).toBe 'horizontal' - expect(row.children).toEqual [pane1, pane3, pane2] - - describe "::splitUp(params)", -> - describe "when the parent is the container root", -> - it "replaces itself with a column and inserts a new pane above itself", -> - pane2 = pane1.splitUp(items: ["B"]) - pane3 = pane1.splitUp(items: ["C"]) - expect(container.root.orientation).toBe 'vertical' - expect(container.root.children).toEqual [pane2, pane3, pane1] - - describe "when the parent is a row", -> - it "replaces itself with a column and inserts a new pane above itself", -> - pane1.splitRight() - pane2 = pane1.splitUp(items: ["B"]) - pane3 = pane1.splitUp(items: ["C"]) - column = container.root.children[0] - expect(column.orientation).toBe 'vertical' - expect(column.children).toEqual [pane2, pane3, pane1] - - describe "::splitDown(params)", -> - describe "when the parent is the container root", -> - it "replaces itself with a column and inserts a new pane below itself", -> - pane2 = pane1.splitDown(items: ["B"]) - pane3 = pane1.splitDown(items: ["C"]) - expect(container.root.orientation).toBe 'vertical' - expect(container.root.children).toEqual [pane1, pane3, pane2] - - describe "when the parent is a row", -> - it "replaces itself with a column and inserts a new pane below itself", -> - pane1.splitRight() - pane2 = pane1.splitDown(items: ["B"]) - pane3 = pane1.splitDown(items: ["C"]) - column = container.root.children[0] - expect(column.orientation).toBe 'vertical' - expect(column.children).toEqual [pane1, pane3, pane2] - - it "sets up the new pane to be focused", -> - expect(pane1.focused).toBe false - pane2 = pane1.splitRight() - expect(pane2.focused).toBe true - - describe "::destroyItem(item)", -> - describe "when the last item is destroyed", -> - it "destroys the pane", -> - pane = new Pane(items: ["A", "B"]) - pane.destroyItem("A") - pane.destroyItem("B") - expect(pane.isDestroyed()).toBe true - - describe "when an item emits a destroyed event", -> - it "removes it from the list of items", -> - pane = new Pane(items: [new Model, new Model, new Model]) - [item1, item2, item3] = pane.items - pane.items[1].destroy() - expect(pane.items).toEqual [item1, item3] - - describe "::destroy()", -> - [pane1, container] = [] - - beforeEach -> - pane1 = new Pane(items: [new Model, new Model]) - container = new PaneContainer(root: pane1) - - it "destroys the pane's destroyable items", -> - [item1, item2] = pane1.items - pane1.destroy() - expect(item1.isDestroyed()).toBe true - expect(item2.isDestroyed()).toBe true - - describe "if the pane's parent has more than two children", -> - it "removes the pane from its parent", -> - pane2 = pane1.splitRight() - pane3 = pane2.splitRight() - - expect(container.root.children).toEqual [pane1, pane2, pane3] - pane2.destroy() - expect(container.root.children).toEqual [pane1, pane3] - - describe "if the pane's parent has two children", -> - it "replaces the parent with its last remaining child", -> - pane2 = pane1.splitRight() - pane3 = pane2.splitDown() - - expect(container.root.children[0]).toBe pane1 - expect(container.root.children[1].children).toEqual [pane2, pane3] - pane3.destroy() - expect(container.root.children).toEqual [pane1, pane2] - pane2.destroy() - expect(container.root).toBe pane1 diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee new file mode 100644 index 000000000..71c6606ac --- /dev/null +++ b/spec/pane-spec.coffee @@ -0,0 +1,437 @@ +{Model} = require 'theorist' +Pane = require '../src/pane' +PaneAxis = require '../src/pane-axis' +PaneContainer = require '../src/pane-container' + +describe "Pane", -> + class Item extends Model + @deserialize: ({name, uri}) -> new this(name, uri) + constructor: (@name, @uri) -> + getUri: -> @uri + getPath: -> @path + serialize: -> {deserializer: 'Item', @name, @uri} + isEqual: (other) -> @name is other?.name + + beforeEach -> + atom.deserializers.add(Item) + + afterEach -> + atom.deserializers.remove(Item) + + describe "construction", -> + it "sets the active item to the first item", -> + pane = new Pane(items: [new Item("A"), new Item("B")]) + expect(pane.activeItem).toBe pane.items[0] + + describe "::activateItem(item)", -> + pane = null + + beforeEach -> + pane = new Pane(items: [new Item("A"), new Item("B")]) + + it "changes the active item to the current item", -> + expect(pane.activeItem).toBe pane.items[0] + pane.activateItem(pane.items[1]) + expect(pane.activeItem).toBe pane.items[1] + + it "adds the given item if it isn't present in ::items", -> + item = new Item("C") + pane.activateItem(item) + expect(item in pane.items).toBe true + expect(pane.activeItem).toBe item + + describe "::activateNextItem() and ::activatePreviousItem()", -> + it "sets the active item to the next/previous item, looping around at either end", -> + pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) + [item1, item2, item3] = pane.items + + expect(pane.activeItem).toBe item1 + pane.activatePreviousItem() + expect(pane.activeItem).toBe item3 + pane.activatePreviousItem() + expect(pane.activeItem).toBe item2 + pane.activateNextItem() + expect(pane.activeItem).toBe item3 + pane.activateNextItem() + expect(pane.activeItem).toBe item1 + + describe "::activateItemAtIndex(index)", -> + it "activates the item at the given index", -> + pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) + [item1, item2, item3] = pane.items + pane.activateItemAtIndex(2) + expect(pane.activeItem).toBe item3 + pane.activateItemAtIndex(1) + expect(pane.activeItem).toBe item2 + pane.activateItemAtIndex(0) + expect(pane.activeItem).toBe item1 + + # Doesn't fail with out-of-bounds indices + pane.activateItemAtIndex(100) + expect(pane.activeItem).toBe item1 + pane.activateItemAtIndex(-1) + expect(pane.activeItem).toBe item1 + + describe "::destroyItem(item)", -> + [pane, item1, item2, item3] = [] + + beforeEach -> + pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) + [item1, item2, item3] = pane.items + + it "removes the item from the items list and activates the next item if it was the active item", -> + expect(pane.activeItem).toBe item1 + pane.destroyItem(item2) + expect(item2 in pane.items).toBe false + expect(pane.activeItem).toBe item1 + + pane.destroyItem(item1) + expect(item1 in pane.items).toBe false + expect(pane.activeItem).toBe item3 + + it "emits 'item-removed' with the item, its index, and true indicating the item is being destroyed", -> + pane.on 'item-removed', itemRemovedHandler = jasmine.createSpy("itemRemovedHandler") + pane.destroyItem(item2) + expect(itemRemovedHandler).toHaveBeenCalledWith(item2, 1, true) + + describe "if the item is modified", -> + itemUri = null + + beforeEach -> + item1.shouldPromptToSave = -> true + item1.save = jasmine.createSpy("save") + item1.saveAs = jasmine.createSpy("saveAs") + item1.getUri = -> itemUri + + describe "if the [Save] option is selected", -> + describe "when the item has a uri", -> + it "saves the item before destroying it", -> + itemUri = "test" + spyOn(atom, 'confirm').andReturn(0) + pane.destroyItem(item1) + + expect(item1.save).toHaveBeenCalled() + expect(item1 in pane.items).toBe false + expect(item1.isDestroyed()).toBe true + + describe "when the item has no uri", -> + it "presents a save-as dialog, then saves the item with the given uri before removing and destroying it", -> + itemUri = null + + spyOn(atom, 'showSaveDialogSync').andReturn("/selected/path") + spyOn(atom, 'confirm').andReturn(0) + pane.destroyItem(item1) + + expect(atom.showSaveDialogSync).toHaveBeenCalled() + expect(item1.saveAs).toHaveBeenCalledWith("/selected/path") + expect(item1 in pane.items).toBe false + expect(item1.isDestroyed()).toBe true + + describe "if the [Don't Save] option is selected", -> + it "removes and destroys the item without saving it", -> + spyOn(atom, 'confirm').andReturn(2) + pane.destroyItem(item1) + + expect(item1.save).not.toHaveBeenCalled() + expect(item1 in pane.items).toBe false + expect(item1.isDestroyed()).toBe true + + describe "if the [Cancel] option is selected", -> + it "does not save, remove, or destroy the item", -> + spyOn(atom, 'confirm').andReturn(1) + pane.destroyItem(item1) + + expect(item1.save).not.toHaveBeenCalled() + expect(item1 in pane.items).toBe true + expect(item1.isDestroyed()).toBe false + + describe "when the last item is destroyed", -> + it "destroys the pane", -> + pane.destroyItem(item) for item in pane.getItems() + expect(pane.isDestroyed()).toBe true + + describe "::destroyItems()", -> + it "destroys all items and the pane", -> + pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) + [item1, item2, item3] = pane.items + pane.destroyItems() + expect(item1.isDestroyed()).toBe true + expect(item2.isDestroyed()).toBe true + expect(item3.isDestroyed()).toBe true + expect(pane.isDestroyed()).toBe true + expect(pane.items).toEqual [] + + describe "when an item emits a destroyed event", -> + it "removes it from the list of items", -> + pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) + [item1, item2, item3] = pane.items + pane.items[1].destroy() + expect(pane.items).toEqual [item1, item3] + + describe "::destroyInactiveItems()", -> + it "destroys all items but the active item", -> + pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) + [item1, item2, item3] = pane.items + pane.activateItem(item2) + pane.destroyInactiveItems() + expect(pane.items).toEqual [item2] + + describe "::saveActiveItem()", -> + pane = null + + beforeEach -> + pane = new Pane(items: [new Item("A")]) + spyOn(atom, 'showSaveDialogSync').andReturn('/selected/path') + + describe "when the active item has a uri", -> + beforeEach -> + pane.activeItem.uri = "test" + + describe "when the active item has a save method", -> + it "saves the current item", -> + pane.activeItem.save = jasmine.createSpy("save") + pane.saveActiveItem() + expect(pane.activeItem.save).toHaveBeenCalled() + + describe "when the current item has no save method", -> + it "does nothing", -> + expect(pane.activeItem.save).toBeUndefined() + pane.saveActiveItem() + + describe "when the current item has no uri", -> + describe "when the current item has a saveAs method", -> + it "opens a save dialog and saves the current item as the selected path", -> + pane.activeItem.saveAs = jasmine.createSpy("saveAs") + pane.saveActiveItem() + expect(atom.showSaveDialogSync).toHaveBeenCalled() + expect(pane.activeItem.saveAs).toHaveBeenCalledWith('/selected/path') + + describe "when the current item has no saveAs method", -> + it "does nothing", -> + expect(pane.activeItem.saveAs).toBeUndefined() + pane.saveActiveItem() + expect(atom.showSaveDialogSync).not.toHaveBeenCalled() + + describe "::saveActiveItemAs()", -> + pane = null + + beforeEach -> + pane = new Pane(items: [new Item("A")]) + spyOn(atom, 'showSaveDialogSync').andReturn('/selected/path') + + describe "when the current item has a saveAs method", -> + it "opens the save dialog and calls saveAs on the item with the selected path", -> + pane.activeItem.path = __filename + pane.activeItem.saveAs = jasmine.createSpy("saveAs") + pane.saveActiveItemAs() + expect(atom.showSaveDialogSync).toHaveBeenCalledWith(__dirname) + expect(pane.activeItem.saveAs).toHaveBeenCalledWith('/selected/path') + + describe "when the current item does not have a saveAs method", -> + it "does nothing", -> + expect(pane.activeItem.saveAs).toBeUndefined() + pane.saveActiveItemAs() + expect(atom.showSaveDialogSync).not.toHaveBeenCalled() + + describe "::itemForUri(uri)", -> + it "returns the item for which a call to .getUri() returns the given uri", -> + pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")]) + [item1, item2, item3] = pane.items + item1.uri = "a" + item2.uri = "b" + expect(pane.itemForUri("a")).toBe item1 + expect(pane.itemForUri("b")).toBe item2 + expect(pane.itemForUri("bogus")).toBeUndefined() + + describe "::moveItem(item, index)", -> + it "moves the item to the given index and emits an 'item-moved' event with the item and its new index", -> + pane = new Pane(items: [new Item("A"), new Item("B"), new Item("C"), new Item("D")]) + [item1, item2, item3, item4] = pane.items + pane.on 'item-moved', itemMovedHandler = jasmine.createSpy("itemMovedHandler") + + pane.moveItem(item1, 2) + expect(pane.getItems()).toEqual [item2, item3, item1, item4] + expect(itemMovedHandler).toHaveBeenCalledWith(item1, 2) + itemMovedHandler.reset() + + pane.moveItem(item2, 3) + expect(pane.getItems()).toEqual [item3, item1, item4, item2] + expect(itemMovedHandler).toHaveBeenCalledWith(item2, 3) + itemMovedHandler.reset() + + pane.moveItem(item2, 1) + expect(pane.getItems()).toEqual [item3, item2, item1, item4] + expect(itemMovedHandler).toHaveBeenCalledWith(item2, 1) + + describe "::moveItemToPane(item, pane, index)", -> + [container, pane1, pane2] = [] + [item1, item2, item3, item4, item5] = [] + + beforeEach -> + pane1 = new Pane(items: [new Item("A"), new Item("B"), new Item("C")]) + container = new PaneContainer(root: pane1) + pane2 = pane1.splitRight(items: [new Item("D"), new Item("E")]) + [item1, item2, item3] = pane1.items + [item4, item5] = pane2.items + + it "moves the item to the given pane at the given index", -> + pane1.moveItemToPane(item2, pane2, 1) + expect(pane1.items).toEqual [item1, item3] + expect(pane2.items).toEqual [item4, item2, item5] + + describe "when the moved item the last item in the source pane", -> + it "destroys the pane, but not the item", -> + item5.destroy() + pane2.moveItemToPane(item4, pane1, 0) + expect(pane2.isDestroyed()).toBe true + expect(item4.isDestroyed()).toBe false + + describe "split methods", -> + [pane1, container] = [] + + beforeEach -> + pane1 = new Pane(items: ["A"]) + container = new PaneContainer(root: pane1) + + describe "::splitLeft(params)", -> + describe "when the parent is the container root", -> + it "replaces itself with a row and inserts a new pane to the left of itself", -> + pane2 = pane1.splitLeft(items: ["B"]) + pane3 = pane1.splitLeft(items: ["C"]) + expect(container.root.orientation).toBe 'horizontal' + expect(container.root.children).toEqual [pane2, pane3, pane1] + + describe "when the parent is a column", -> + it "replaces itself with a row and inserts a new pane to the left of itself", -> + pane1.splitDown() + pane2 = pane1.splitLeft(items: ["B"]) + pane3 = pane1.splitLeft(items: ["C"]) + row = container.root.children[0] + expect(row.orientation).toBe 'horizontal' + expect(row.children).toEqual [pane2, pane3, pane1] + + describe "::splitRight(params)", -> + describe "when the parent is the container root", -> + it "replaces itself with a row and inserts a new pane to the right of itself", -> + pane2 = pane1.splitRight(items: ["B"]) + pane3 = pane1.splitRight(items: ["C"]) + expect(container.root.orientation).toBe 'horizontal' + expect(container.root.children).toEqual [pane1, pane3, pane2] + + describe "when the parent is a column", -> + it "replaces itself with a row and inserts a new pane to the right of itself", -> + pane1.splitDown() + pane2 = pane1.splitRight(items: ["B"]) + pane3 = pane1.splitRight(items: ["C"]) + row = container.root.children[0] + expect(row.orientation).toBe 'horizontal' + expect(row.children).toEqual [pane1, pane3, pane2] + + describe "::splitUp(params)", -> + describe "when the parent is the container root", -> + it "replaces itself with a column and inserts a new pane above itself", -> + pane2 = pane1.splitUp(items: ["B"]) + pane3 = pane1.splitUp(items: ["C"]) + expect(container.root.orientation).toBe 'vertical' + expect(container.root.children).toEqual [pane2, pane3, pane1] + + describe "when the parent is a row", -> + it "replaces itself with a column and inserts a new pane above itself", -> + pane1.splitRight() + pane2 = pane1.splitUp(items: ["B"]) + pane3 = pane1.splitUp(items: ["C"]) + column = container.root.children[0] + expect(column.orientation).toBe 'vertical' + expect(column.children).toEqual [pane2, pane3, pane1] + + describe "::splitDown(params)", -> + describe "when the parent is the container root", -> + it "replaces itself with a column and inserts a new pane below itself", -> + pane2 = pane1.splitDown(items: ["B"]) + pane3 = pane1.splitDown(items: ["C"]) + expect(container.root.orientation).toBe 'vertical' + expect(container.root.children).toEqual [pane1, pane3, pane2] + + describe "when the parent is a row", -> + it "replaces itself with a column and inserts a new pane below itself", -> + pane1.splitRight() + pane2 = pane1.splitDown(items: ["B"]) + pane3 = pane1.splitDown(items: ["C"]) + column = container.root.children[0] + expect(column.orientation).toBe 'vertical' + expect(column.children).toEqual [pane1, pane3, pane2] + + it "sets up the new pane to be focused", -> + expect(pane1.focused).toBe false + pane2 = pane1.splitRight() + expect(pane2.focused).toBe true + + describe "::destroy()", -> + [pane1, container] = [] + + beforeEach -> + pane1 = new Pane(items: [new Model, new Model]) + container = new PaneContainer(root: pane1) + + it "destroys the pane's destroyable items", -> + [item1, item2] = pane1.items + pane1.destroy() + expect(item1.isDestroyed()).toBe true + expect(item2.isDestroyed()).toBe true + + describe "if the pane is active", -> + it "makes the next pane active", -> + pane2 = pane1.splitRight() + expect(pane2.isActive()).toBe true + pane2.destroy() + expect(pane1.isActive()).to + + describe "if the pane's parent has more than two children", -> + it "removes the pane from its parent", -> + pane2 = pane1.splitRight() + pane3 = pane2.splitRight() + + expect(container.root.children).toEqual [pane1, pane2, pane3] + pane2.destroy() + expect(container.root.children).toEqual [pane1, pane3] + + describe "if the pane's parent has two children", -> + it "replaces the parent with its last remaining child", -> + pane2 = pane1.splitRight() + pane3 = pane2.splitDown() + + expect(container.root.children[0]).toBe pane1 + expect(container.root.children[1].children).toEqual [pane2, pane3] + pane3.destroy() + expect(container.root.children).toEqual [pane1, pane2] + pane2.destroy() + expect(container.root).toBe pane1 + + describe "serialization", -> + pane = null + + beforeEach -> + pane = new Pane(items: [new Item("A", "a"), new Item("B", "b"), new Item("C", "c")]) + + it "can serialize and deserialize the pane and all its items", -> + newPane = pane.testSerialization() + expect(newPane.items).toEqual pane.items + + it "restores the active item on deserialization", -> + pane.activateItemAtIndex(1) + newPane = pane.testSerialization() + expect(newPane.activeItem).toEqual newPane.items[1] + + it "does not include items that cannot be deserialized", -> + spyOn(console, 'warn') + unserializable = {} + pane.activateItem(unserializable) + + newPane = pane.testSerialization() + expect(newPane.activeItem).toEqual pane.items[0] + expect(newPane.items.length).toBe pane.items.length - 1 + + it "includes the pane's focus state in the serialized state", -> + pane.focus() + newPane = pane.testSerialization() + expect(newPane.focused).toBe true diff --git a/spec/pane-view-spec.coffee b/spec/pane-view-spec.coffee index 7440f7e73..fb1815308 100644 --- a/spec/pane-view-spec.coffee +++ b/spec/pane-view-spec.coffee @@ -5,7 +5,7 @@ path = require 'path' temp = require 'temp' describe "PaneView", -> - [container, view1, view2, editor1, editor2, pane] = [] + [container, view1, view2, editor1, editor2, pane, paneModel] = [] class TestView extends View @deserialize: ({id, text}) -> new TestView({id, text}) @@ -23,377 +23,120 @@ describe "PaneView", -> editor1 = atom.project.openSync('sample.js') editor2 = atom.project.openSync('sample.txt') pane = new PaneView(view1, editor1, view2, editor2) + paneModel = pane.model container.setRoot(pane) afterEach -> atom.deserializers.remove(TestView) - describe "::initialize(items...)", -> - it "displays the first item in the pane", -> - expect(pane.itemViews.find('#view-1')).toExist() - - describe "::activateItem(item)", -> + describe "when the active pane item changes", -> it "hides all item views except the one being shown and sets the activeItem", -> expect(pane.activeItem).toBe view1 + expect(view1.css('display')).not.toBe 'none' + pane.activateItem(view2) expect(view1.css('display')).toBe 'none' expect(view2.css('display')).not.toBe 'none' - expect(pane.activeItem).toBe view2 - it "triggers 'pane:active-item-changed' if the item isn't already the activeItem", -> - pane.activate() + it "triggers 'pane:active-item-changed'", -> itemChangedHandler = jasmine.createSpy("itemChangedHandler") container.on 'pane:active-item-changed', itemChangedHandler expect(pane.activeItem).toBe view1 - pane.activateItem(view2) - pane.activateItem(view2) + paneModel.activateItem(view2) + paneModel.activateItem(view2) + expect(itemChangedHandler.callCount).toBe 1 expect(itemChangedHandler.argsForCall[0][1]).toBe view2 itemChangedHandler.reset() - pane.activateItem(editor1) + paneModel.activateItem(editor1) expect(itemChangedHandler).toHaveBeenCalled() expect(itemChangedHandler.argsForCall[0][1]).toBe editor1 itemChangedHandler.reset() - describe "if the pane's active view is focused before calling activateItem", -> - it "focuses the new active view", -> - container.attachToDom() - pane.focus() - expect(pane.activeView).not.toBe view2 - expect(pane.activeView).toMatchSelector ':focus' - pane.activateItem(view2) - expect(view2).toMatchSelector ':focus' + it "transfers focus to the new active view if the previous view was focused", -> + container.attachToDom() + pane.focus() + expect(pane.activeView).not.toBe view2 + expect(pane.activeView).toMatchSelector ':focus' + paneModel.activateItem(view2) + expect(view2).toMatchSelector ':focus' - describe "when the given item isn't yet in the items list on the pane", -> - view3 = null - beforeEach -> - view3 = new TestView(id: 'view-3', text: "View 3") - pane.activateItem(editor1) - expect(pane.getActiveItemIndex()).toBe 1 + describe "when the new activeItem is a model", -> + it "shows the item's view or creates and shows a new view for the item if none exists", -> + initialViewCount = pane.itemViews.find('.test-view').length - it "adds it to the items list after the active item", -> - pane.activateItem(view3) - expect(pane.getItems()).toEqual [view1, editor1, view3, view2, editor2] - expect(pane.activeItem).toBe view3 - expect(pane.getActiveItemIndex()).toBe 2 + model1 = + id: 'test-model-1' + text: 'Test Model 1' + serialize: -> {@id, @text} + getViewClass: -> TestView - it "triggers the 'item-added' event with the item and its index before the 'active-item-changed' event", -> - events = [] - container.on 'pane:item-added', (e, item, index) -> events.push(['pane:item-added', item, index]) - container.on 'pane:active-item-changed', (e, item) -> events.push(['pane:active-item-changed', item]) - pane.activateItem(view3) - expect(events).toEqual [['pane:item-added', view3, 2], ['pane:active-item-changed', view3]] + model2 = + id: 'test-model-2' + text: 'Test Model 2' + serialize: -> {@id, @text} + getViewClass: -> TestView - describe "when showing a model item", -> - describe "when no view has yet been appended for that item", -> - it "appends and shows a view to display the item based on its `.getViewClass` method", -> - pane.activateItem(editor1) - editorView = pane.activeView - expect(editorView.css('display')).not.toBe 'none' - expect(editorView.editor).toBe editor1 + paneModel.activateItem(model1) + paneModel.activateItem(model2) + expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2 - describe "when a valid view has already been appended for another item", -> - it "multiple views are created for multiple items", -> - pane.activateItem(editor1) - pane.activateItem(editor2) - expect(pane.itemViews.find('.editor').length).toBe 2 - editorView = pane.activeView - expect(editorView.css('display')).not.toBe 'none' - expect(editorView.editor).toBe editor2 + paneModel.activatePreviousItem() + expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2 - it "creates a new view with the item", -> - initialViewCount = pane.itemViews.find('.test-view').length + paneModel.destroyItem(model2) + expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 1 - model1 = - id: 'test-model-1' - text: 'Test Model 1' - serialize: -> {@id, @text} - getViewClass: -> TestView + paneModel.destroyItem(model1) + expect(pane.itemViews.find('.test-view').length).toBe initialViewCount - model2 = - id: 'test-model-2' - text: 'Test Model 2' - serialize: -> {@id, @text} - getViewClass: -> TestView - - pane.activateItem(model1) - pane.activateItem(model2) - expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2 - - pane.activatePreviousItem() - expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 2 - - pane.destroyItem(model2) - expect(pane.itemViews.find('.test-view').length).toBe initialViewCount + 1 - - pane.destroyItem(model1) - expect(pane.itemViews.find('.test-view').length).toBe initialViewCount - - describe "when showing a view item", -> + describe "when the new activeItem is a view", -> it "appends it to the itemViews div if it hasn't already been appended and shows it", -> expect(pane.itemViews.find('#view-2')).not.toExist() - pane.activateItem(view2) + paneModel.activateItem(view2) expect(pane.itemViews.find('#view-2')).toExist() - expect(pane.activeView).toBe view2 + paneModel.activateItem(view1) + paneModel.activateItem(view2) + expect(pane.itemViews.find('#view-2').length).toBe 1 - describe "::destroyItem(item)", -> - describe "if the item is not modified", -> - it "removes the item and tries to call destroy on it", -> - pane.destroyItem(editor2) - expect(pane.getItems().indexOf(editor2)).toBe -1 - expect(editor2.isDestroyed()).toBe true - - describe "if the item is modified", -> - beforeEach -> - jasmine.unspy(editor2, 'shouldPromptToSave') - spyOn(editor2, 'save') - spyOn(editor2, 'saveAs') - - editor2.insertText('a') - expect(editor2.isModified()).toBeTruthy() - - describe "if the [Save] option is selected", -> - describe "when the item has a uri", -> - it "saves the item before removing and destroying it", -> - spyOn(atom, 'confirm').andReturn(0) - pane.destroyItem(editor2) - - expect(editor2.save).toHaveBeenCalled() - expect(pane.getItems().indexOf(editor2)).toBe -1 - expect(editor2.isDestroyed()).toBe true - - describe "when the item has no uri", -> - it "presents a save-as dialog, then saves the item with the given uri before removing and destroying it", -> - editor2.buffer.setPath(undefined) - - spyOn(atom, 'showSaveDialogSync').andReturn("/selected/path") - spyOn(atom, 'confirm').andReturn(0) - pane.destroyItem(editor2) - - expect(atom.showSaveDialogSync).toHaveBeenCalled() - - expect(editor2.saveAs).toHaveBeenCalledWith("/selected/path") - expect(pane.getItems().indexOf(editor2)).toBe -1 - expect(editor2.isDestroyed()).toBe true - - describe "if the [Don't Save] option is selected", -> - it "removes and destroys the item without saving it", -> - spyOn(atom, 'confirm').andReturn(2) - pane.destroyItem(editor2) - - expect(editor2.save).not.toHaveBeenCalled() - expect(pane.getItems().indexOf(editor2)).toBe -1 - expect(editor2.isDestroyed()).toBe true - - describe "if the [Cancel] option is selected", -> - it "does not save, remove, or destroy the item", -> - spyOn(atom, 'confirm').andReturn(1) - pane.destroyItem(editor2) - - expect(editor2.save).not.toHaveBeenCalled() - expect(pane.getItems().indexOf(editor2)).not.toBe -1 - expect(editor2.isDestroyed()).toBe false - - it "removes the item's associated view", -> - view1.remove = (selector, keepData) -> @wasRemoved = not keepData - pane.destroyItem(view1) - expect(view1.wasRemoved).toBe true - - it "removes the item from the items list and shows the next item if it was showing", -> - pane.destroyItem(view1) - expect(pane.getItems()).toEqual [editor1, view2, editor2] - expect(pane.activeItem).toBe editor1 - - pane.activateItem(editor2) - pane.destroyItem(editor2) - expect(pane.getItems()).toEqual [editor1, view2] - expect(pane.activeItem).toBe editor1 - - it "triggers 'pane:item-removed' with the item and its former index", -> + describe "when an item is destroyed", -> + it "triggers the 'pane:item-removed' event with the item and its former index", -> itemRemovedHandler = jasmine.createSpy("itemRemovedHandler") pane.on 'pane:item-removed', itemRemovedHandler - pane.destroyItem(editor1) + paneModel.destroyItem(editor1) expect(itemRemovedHandler).toHaveBeenCalled() expect(itemRemovedHandler.argsForCall[0][1..2]).toEqual [editor1, 1] - describe "when removing the last item", -> - it "removes the pane", -> - pane.destroyItem(item) for item in pane.getItems() - expect(pane.hasParent()).toBeFalsy() - - describe "when the pane is focused", -> - it "shifts focus to the next pane", -> - expect(container.getRoot()).toBe pane - container.attachToDom() - pane2 = pane.splitRight(new TestView(id: 'view-3', text: 'View 3')) - pane.focus() - expect(pane).toMatchSelector(':has(:focus)') - pane.destroyItem(item) for item in pane.getItems() - expect(pane2).toMatchSelector ':has(:focus)' - - describe "when the item is a view", -> + describe "when the destroyed item is a view", -> it "removes the item from the 'item-views' div", -> expect(view1.parent()).toMatchSelector pane.itemViews - pane.destroyItem(view1) + paneModel.destroyItem(view1) expect(view1.parent()).not.toMatchSelector pane.itemViews - describe "when the item is a model", -> - it "removes the associated view only when all items that require it have been removed", -> - pane.activateItem(editor1) - pane.activateItem(editor2) - pane.destroyItem(editor2) - expect(pane.itemViews.find('.editor')).toExist() + describe "when the destroyed item is a model", -> + it "removes the associated view", -> + paneModel.activateItem(editor1) + expect(pane.itemViews.find('.editor').length).toBe 1 pane.destroyItem(editor1) - expect(pane.itemViews.find('.editor')).not.toExist() + expect(pane.itemViews.find('.editor').length).toBe 0 - describe "::moveItem(item, index)", -> - it "moves the item to the given index and emits a 'pane:item-moved' event with the item and the new index", -> - itemMovedHandler = jasmine.createSpy("itemMovedHandler") - pane.on 'pane:item-moved', itemMovedHandler - - pane.moveItem(view1, 2) - expect(pane.getItems()).toEqual [editor1, view2, view1, editor2] + describe "when an item is moved within the same pane", -> + it "emits a 'pane:item-moved' event with the item and the new index", -> + pane.on 'pane:item-moved', itemMovedHandler = jasmine.createSpy("itemMovedHandler") + paneModel.moveItem(view1, 2) expect(itemMovedHandler).toHaveBeenCalled() expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [view1, 2] - itemMovedHandler.reset() - pane.moveItem(editor1, 3) - expect(pane.getItems()).toEqual [view2, view1, editor2, editor1] - expect(itemMovedHandler).toHaveBeenCalled() - expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [editor1, 3] - itemMovedHandler.reset() - - pane.moveItem(editor1, 1) - expect(pane.getItems()).toEqual [view2, editor1, view1, editor2] - expect(itemMovedHandler).toHaveBeenCalled() - expect(itemMovedHandler.argsForCall[0][1..2]).toEqual [editor1, 1] - itemMovedHandler.reset() - - describe "::moveItemToPane(item, pane, index)", -> - [pane2, view3] = [] - - beforeEach -> - view3 = new TestView(id: 'view-3', text: "View 3") - pane2 = pane.splitRight(view3) - - it "moves the item to the given pane at the given index", -> - pane.moveItemToPane(view1, pane2, 1) - expect(pane.getItems()).toEqual [editor1, view2, editor2] - expect(pane2.getItems()).toEqual [view3, view1] - - describe "when it is the last item on the source pane", -> - it "removes the source pane, but does not destroy the item", -> - pane.destroyItem(view1) - pane.destroyItem(view2) - pane.destroyItem(editor2) - - expect(pane.getItems()).toEqual [editor1] - pane.moveItemToPane(editor1, pane2, 1) - - expect(pane.hasParent()).toBeFalsy() - expect(pane2.getItems()).toEqual [view3, editor1] - expect(editor1.isDestroyed()).toBe false - - describe "when the item is a jQuery object", -> - it "preserves data by detaching instead of removing", -> - view1.data('preservative', 1234) - pane.moveItemToPane(view1, pane2, 1) - pane2.activateItemAtIndex(1) - expect(pane2.activeView.data('preservative')).toBe 1234 - - describe "pane:close", -> - it "destroys all items and removes the pane", -> - pane.activateItem(editor1) - pane.trigger 'pane:close' - expect(pane.hasParent()).toBeFalsy() - expect(editor2.isDestroyed()).toBe true - expect(editor1.isDestroyed()).toBe true - - describe "pane:close-other-items", -> - it "destroys all items except the current", -> - pane.activateItem(editor1) - pane.trigger 'pane:close-other-items' - expect(editor2.isDestroyed()).toBe true - expect(pane.getItems()).toEqual [editor1] - - describe "::saveActiveItem()", -> - describe "when the current item has a uri", -> - describe "when the current item has a save method", -> - it "saves the current item", -> - spyOn(editor2, 'save') - pane.activateItem(editor2) - pane.saveActiveItem() - expect(editor2.save).toHaveBeenCalled() - - describe "when the current item has no save method", -> - it "does nothing", -> - pane.activeItem.getUri = -> 'you are eye' - expect(pane.activeItem.save).toBeUndefined() - pane.saveActiveItem() - - describe "when the current item has no uri", -> - beforeEach -> - spyOn(atom, 'showSaveDialogSync').andReturn('/selected/path') - - describe "when the current item has a saveAs method", -> - it "opens a save dialog and saves the current item as the selected path", -> - newEditor = atom.project.openSync() - spyOn(newEditor, 'saveAs') - pane.activateItem(newEditor) - - pane.saveActiveItem() - - expect(atom.showSaveDialogSync).toHaveBeenCalled() - expect(newEditor.saveAs).toHaveBeenCalledWith('/selected/path') - - describe "when the current item has no saveAs method", -> - it "does nothing", -> - expect(pane.activeItem.saveAs).toBeUndefined() - pane.saveActiveItem() - expect(atom.showSaveDialogSync).not.toHaveBeenCalled() - - describe "::saveActiveItemAs()", -> - beforeEach -> - spyOn(atom, 'showSaveDialogSync').andReturn('/selected/path') - - describe "when the current item has a saveAs method", -> - it "opens the save dialog and calls saveAs on the item with the selected path", -> - spyOn(editor2, 'saveAs') - pane.activateItem(editor2) - - pane.saveActiveItemAs() - - expect(atom.showSaveDialogSync).toHaveBeenCalledWith(path.dirname(editor2.getPath())) - expect(editor2.saveAs).toHaveBeenCalledWith('/selected/path') - - describe "when the current item does not have a saveAs method", -> - it "does nothing", -> - expect(pane.activeItem.saveAs).toBeUndefined() - pane.saveActiveItemAs() - expect(atom.showSaveDialogSync).not.toHaveBeenCalled() - - describe "pane:show-next-item and pane:show-previous-item", -> - it "advances forward/backward through the pane's items, looping around at either end", -> - expect(pane.activeItem).toBe view1 - pane.trigger 'pane:show-previous-item' - expect(pane.activeItem).toBe editor2 - pane.trigger 'pane:show-previous-item' - expect(pane.activeItem).toBe view2 - pane.trigger 'pane:show-next-item' - expect(pane.activeItem).toBe editor2 - pane.trigger 'pane:show-next-item' - expect(pane.activeItem).toBe view1 - - describe "pane:show-item-N events", -> - it "shows the (n-1)th item if it exists", -> - pane.trigger 'pane:show-item-2' - expect(pane.activeItem).toBe pane.itemAtIndex(1) - pane.trigger 'pane:show-item-1' - expect(pane.activeItem).toBe pane.itemAtIndex(0) - pane.trigger 'pane:show-item-9' # don't fail on out-of-bounds indices - expect(pane.activeItem).toBe pane.itemAtIndex(0) + describe "when an item is moved to another pane", -> + it "detaches the item's view rather than removing it", -> + paneModel2 = paneModel.splitRight() + view1.data('preservative', 1234) + paneModel.moveItemToPane(view1, paneModel2, 1) + expect(view1.data('preservative')).toBe 1234 + paneModel2.activateItemAtIndex(1) + expect(view1.data('preservative')).toBe 1234 describe "when the title of the active item changes", -> it "emits pane:active-item-title-changed", -> @@ -424,12 +167,7 @@ describe "PaneView", -> waitsFor -> pane.items.length == 4 - describe "::remove()", -> - it "destroys all the pane's items", -> - pane.remove() - expect(editor1.isDestroyed()).toBe true - expect(editor2.isDestroyed()).toBe true - + describe "when a pane is destroyed", -> it "triggers a 'pane:removed' event with the pane", -> removedHandler = jasmine.createSpy("removedHandler") container.on 'pane:removed', removedHandler @@ -437,52 +175,28 @@ describe "PaneView", -> expect(removedHandler).toHaveBeenCalled() expect(removedHandler.argsForCall[0][1]).toBe pane - describe "when there are other panes", -> + describe "if the destroyed pane has focus", -> [paneToLeft, paneToRight] = [] - beforeEach -> - pane.activateItem(editor1) - paneToLeft = pane.splitLeft(pane.copyActiveItem()) - paneToRight = pane.splitRight(pane.copyActiveItem()) - container.attachToDom() + describe "if it is not the last pane in the container", -> + it "focuses the next pane", -> + paneModel.activateItem(editor1) + pane2Model = paneModel.splitRight(items: [paneModel.copyActiveItem()]) + pane2 = pane2Model._view + container.attachToDom() + expect(pane.hasFocus()).toBe false + pane2Model.destroy() + expect(pane.hasFocus()).toBe true - describe "when the removed pane is active", -> - it "makes the next the next pane active and focuses it", -> - pane.activate() - pane.remove() - expect(paneToLeft.isActive()).toBeFalsy() - expect(paneToRight.isActive()).toBeTruthy() - expect(paneToRight).toMatchSelector ':has(:focus)' - - describe "when the removed pane is not active", -> - it "does not affect the active pane or the focus", -> - paneToLeft.focus() - expect(paneToLeft.isActive()).toBeTruthy() - expect(paneToRight.isActive()).toBeFalsy() - - pane.remove() - expect(paneToLeft.isActive()).toBeTruthy() - expect(paneToRight.isActive()).toBeFalsy() - expect(paneToLeft).toMatchSelector ':has(:focus)' - - describe "when it is the last pane", -> - beforeEach -> - expect(container.getPanes().length).toBe 1 - atom.workspaceView = focus: jasmine.createSpy("workspaceView.focus") - - describe "when the removed pane is focused", -> - it "calls focus on workspaceView so we don't lose focus", -> + describe "if it is the last pane in the container", -> + it "shifts focus to the workspace view", -> + atom.workspaceView = {focus: jasmine.createSpy("atom.workspaceView.focus")} container.attachToDom() pane.focus() - pane.remove() + expect(container.hasFocus()).toBe true + paneModel.destroy() expect(atom.workspaceView.focus).toHaveBeenCalled() - describe "when the removed pane is not focused", -> - it "does not call focus on root view", -> - expect(pane).not.toMatchSelector ':has(:focus)' - pane.remove() - expect(atom.workspaceView.focus).not.toHaveBeenCalled() - describe "::getNextPane()", -> it "returns the next pane if one exists, wrapping around from the last pane to the first", -> pane.activateItem(editor1) @@ -491,148 +205,78 @@ describe "PaneView", -> expect(pane.getNextPane()).toBe pane2 expect(pane2.getNextPane()).toBe pane + describe "when the pane's active status changes", -> + [pane2, pane2Model] = [] + + beforeEach -> + pane2Model = paneModel.splitRight(items: [pane.copyActiveItem()]) + pane2 = pane2Model._view + expect(pane2Model.isActive()).toBe true + + it "adds or removes the .active class as appropriate", -> + expect(pane).not.toHaveClass('active') + paneModel.activate() + expect(pane).toHaveClass('active') + pane2Model.activate() + expect(pane).not.toHaveClass('active') + + it "triggers 'pane:became-active' or 'pane:became-inactive' according to the current status", -> + pane.on 'pane:became-active', becameActiveHandler = jasmine.createSpy("becameActiveHandler") + pane.on 'pane:became-inactive', becameInactiveHandler = jasmine.createSpy("becameInactiveHandler") + paneModel.activate() + + expect(becameActiveHandler.callCount).toBe 1 + expect(becameInactiveHandler.callCount).toBe 0 + + pane2Model.activate() + expect(becameActiveHandler.callCount).toBe 1 + expect(becameInactiveHandler.callCount).toBe 1 + describe "when the pane is focused", -> beforeEach -> container.attachToDom() - it "focuses the active item view", -> + it "transfers focus to the active view", -> focusHandler = jasmine.createSpy("focusHandler") pane.activeItem.on 'focus', focusHandler pane.focus() expect(focusHandler).toHaveBeenCalled() - it "triggers 'pane:became-active' if it was not previously active", -> - pane2 = pane.splitRight(view2) # Make pane inactive + it "makes the pane active", -> + paneModel.splitRight(items: [pane.copyActiveItem()]) + expect(paneModel.isActive()).toBe false + pane.focus() + expect(paneModel.isActive()).toBe true - becameActiveHandler = jasmine.createSpy("becameActiveHandler") - pane.on 'pane:became-active', becameActiveHandler - expect(pane.isActive()).toBeFalsy() - pane.focusin() - expect(pane.isActive()).toBeTruthy() - pane.focusin() - - expect(becameActiveHandler.callCount).toBe 1 - - it "triggers 'pane:became-inactive' when it was previously active", -> - pane2 = pane.splitRight(view2) # Make pane inactive - - becameInactiveHandler = jasmine.createSpy("becameInactiveHandler") - pane.on 'pane:became-inactive', becameInactiveHandler - - expect(pane.isActive()).toBeFalsy() - pane.focusin() - expect(pane.isActive()).toBeTruthy() - pane.splitRight(pane.copyActiveItem()) - expect(pane.isActive()).toBeFalsy() - - expect(becameInactiveHandler.callCount).toBe 1 - - describe "split methods", -> - [pane1, view3, view4] = [] - beforeEach -> + describe "when a pane is split", -> + it "builds the appropriate pane-row and pane-column views", -> pane1 = pane + pane1Model = pane.model pane.activateItem(editor1) - view3 = new TestView(id: 'view-3', text: 'View 3') - view4 = new TestView(id: 'view-4', text: 'View 4') - describe "splitRight(items...)", -> - it "builds a row if needed, then appends a new pane after itself", -> - # creates the new pane with a copy of the active item if none are given - pane2 = pane1.splitRight(pane1.copyActiveItem()) - expect(container.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]] - expect(pane2.items).toEqual [editor1] - expect(pane2.activeItem).not.toBe editor1 # it's a copy + pane2Model = pane1Model.splitRight(items: [pane1Model.copyActiveItem()]) + pane3Model = pane2Model.splitDown(items: [pane2Model.copyActiveItem()]) + pane2 = pane2Model._view + pane3 = pane3Model._view - pane3 = pane2.splitRight(view3, view4) - expect(pane3.getItems()).toEqual [view3, view4] - expect(container.find('.pane-row .pane').toArray()).toEqual [pane[0], pane2[0], pane3[0]] + expect(container.find('> .pane-row > .pane').toArray()).toEqual [pane1[0]] + expect(container.find('> .pane-row > .pane-column > .pane').toArray()).toEqual [pane2[0], pane3[0]] - it "builds a row if needed, then appends a new pane after itself ", -> - # creates the new pane with a copy of the active item if none are given - pane2 = pane1.splitRight() - expect(container.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0]] - expect(pane2.items).toEqual [] - expect(pane2.activeItem).toBeUndefined() - - pane3 = pane2.splitRight() - expect(container.find('.pane-row .pane').toArray()).toEqual [pane1[0], pane2[0], pane3[0]] - expect(pane3.items).toEqual [] - expect(pane3.activeItem).toBeUndefined() - - describe "splitLeft(items...)", -> - it "builds a row if needed, then appends a new pane before itself", -> - # creates the new pane with a copy of the active item if none are given - pane2 = pane.splitLeft(pane1.copyActiveItem()) - expect(container.find('.pane-row .pane').toArray()).toEqual [pane2[0], pane[0]] - expect(pane2.items).toEqual [editor1] - expect(pane2.activeItem).not.toBe editor1 # it's a copy - - pane3 = pane2.splitLeft(view3, view4) - expect(pane3.getItems()).toEqual [view3, view4] - expect(container.find('.pane-row .pane').toArray()).toEqual [pane3[0], pane2[0], pane[0]] - - describe "splitDown(items...)", -> - it "builds a column if needed, then appends a new pane after itself", -> - # creates the new pane with a copy of the active item if none are given - pane2 = pane.splitDown(pane1.copyActiveItem()) - expect(container.find('.pane-column .pane').toArray()).toEqual [pane[0], pane2[0]] - expect(pane2.items).toEqual [editor1] - expect(pane2.activeItem).not.toBe editor1 # it's a copy - - pane3 = pane2.splitDown(view3, view4) - expect(pane3.getItems()).toEqual [view3, view4] - expect(container.find('.pane-column .pane').toArray()).toEqual [pane[0], pane2[0], pane3[0]] - - describe "splitUp(items...)", -> - it "builds a column if needed, then appends a new pane before itself", -> - # creates the new pane with a copy of the active item if none are given - pane2 = pane.splitUp(pane1.copyActiveItem()) - expect(container.find('.pane-column .pane').toArray()).toEqual [pane2[0], pane[0]] - expect(pane2.items).toEqual [editor1] - expect(pane2.activeItem).not.toBe editor1 # it's a copy - - pane3 = pane2.splitUp(view3, view4) - expect(pane3.getItems()).toEqual [view3, view4] - expect(container.find('.pane-column .pane').toArray()).toEqual [pane3[0], pane2[0], pane[0]] - - describe "::itemForUri(uri)", -> - it "returns the item for which a call to .getUri() returns the given uri", -> - expect(pane.itemForUri(editor1.getUri())).toBe editor1 - expect(pane.itemForUri(editor2.getUri())).toBe editor2 + pane1Model.destroy() + expect(container.find('> .pane-column > .pane').toArray()).toEqual [pane2[0], pane3[0]] describe "serialization", -> - it "can serialize and deserialize the pane and all its items", -> - newPane = pane.testSerialization() - expect(newPane.getItems()).toEqual [view1, editor1, view2, editor2] - - it "restores the active item on deserialization", -> - pane.activateItem(editor2) - newPane = pane.testSerialization() - expect(newPane.activeItem).toEqual editor2 - - it "does not show items that cannot be deserialized", -> - spyOn(console, 'warn') - - class Unserializable - getViewClass: -> TestView - - pane.activateItem(new Unserializable) - - newPane = pane.testSerialization() - expect(newPane.activeItem).toEqual pane.items[0] - expect(newPane.items.length).toBe pane.items.length - 1 - it "focuses the pane after attach only if had focus when serialized", -> container.attachToDom() pane.focus() - container2 = container.testSerialization() + container2 = new PaneContainerView(container.model.testSerialization()) pane2 = container2.getRoot() container2.attachToDom() expect(pane2).toMatchSelector(':has(:focus)') $(document.activeElement).blur() - container3 = container.testSerialization() + container3 = new PaneContainerView(container.model.testSerialization()) pane3 = container3.getRoot() container3.attachToDom() expect(pane3).not.toMatchSelector(':has(:focus)') diff --git a/spec/select-list-spec.coffee b/spec/select-list-spec.coffee index 9c1c64555..152ba64ef 100644 --- a/spec/select-list-spec.coffee +++ b/spec/select-list-spec.coffee @@ -33,7 +33,7 @@ describe "SelectList", -> selectList.attachToDom() it "filters the elements in the list based on the scoreElement function and selects the first item", -> - miniEditor.insertText('la') + miniEditor.getEditor().insertText('la') window.advanceClock(selectList.inputThrottle) expect(list.find('li').length).toBe 2 @@ -43,13 +43,13 @@ describe "SelectList", -> expect(selectList.error).not.toBeVisible() it "displays an error if there are no matches, removes error when there are matches", -> - miniEditor.insertText('nothing will match this') + miniEditor.getEditor().insertText('nothing will match this') window.advanceClock(selectList.inputThrottle) expect(list.find('li').length).toBe 0 expect(selectList.error).not.toBeHidden() - miniEditor.setText('la') + miniEditor.getEditor().setText('la') window.advanceClock(selectList.inputThrottle) expect(list.find('li').length).toBe 2 @@ -58,7 +58,7 @@ describe "SelectList", -> it "displays no elements until the array has been set on the list", -> selectList.array = null selectList.list.empty() - miniEditor.insertText('la') + miniEditor.getEditor().insertText('la') window.advanceClock(selectList.inputThrottle) expect(list.find('li').length).toBe 0 @@ -124,7 +124,7 @@ describe "SelectList", -> selectList.attachToDom() it "does not trigger the confirmed hook", -> - miniEditor.insertText("i will never match anything") + miniEditor.getEditor().insertText("i will never match anything") window.advanceClock(selectList.inputThrottle) expect(list.find('li')).not.toExist() @@ -132,7 +132,7 @@ describe "SelectList", -> expect(selectList.confirmed).not.toHaveBeenCalled() it "does trigger the cancelled hook", -> - miniEditor.insertText("i will never match anything") + miniEditor.getEditor().insertText("i will never match anything") window.advanceClock(selectList.inputThrottle) expect(list.find('li')).not.toExist() diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index 9c672e7a9..8e939d03d 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -107,7 +107,7 @@ afterEach -> atom.workspaceView?.remove?() atom.workspaceView = null - delete atom.state.workspaceView + delete atom.state.workspace atom.project?.destroy?() atom.project = null diff --git a/spec/window-spec.coffee b/spec/window-spec.coffee index 53f992555..78f90d3b7 100644 --- a/spec/window-spec.coffee +++ b/spec/window-spec.coffee @@ -88,12 +88,12 @@ describe "Window", -> describe ".unloadEditorWindow()", -> it "saves the serialized state of the window so it can be deserialized after reload", -> - workspaceViewState = atom.workspaceView.serialize() + workspaceState = atom.workspace.serialize() syntaxState = atom.syntax.serialize() atom.unloadEditorWindow() - expect(atom.state.workspaceView).toEqual workspaceViewState + expect(atom.state.workspace).toEqual workspaceState expect(atom.state.syntax).toEqual syntaxState expect(atom.saveSync).toHaveBeenCalled() diff --git a/spec/workspace-view-spec.coffee b/spec/workspace-view-spec.coffee index f91fb8845..5fcd7beb0 100644 --- a/spec/workspace-view-spec.coffee +++ b/spec/workspace-view-spec.coffee @@ -3,6 +3,7 @@ Q = require 'q' path = require 'path' temp = require 'temp' PaneView = require '../src/pane-view' +Workspace = require '../src/workspace' describe "WorkspaceView", -> pathToOpen = null @@ -10,7 +11,8 @@ describe "WorkspaceView", -> beforeEach -> atom.project.setPath(atom.project.resolve('dir')) pathToOpen = atom.project.resolve('a') - atom.workspaceView = new WorkspaceView + atom.workspace = new Workspace + atom.workspaceView = new WorkspaceView(atom.workspace) atom.workspaceView.enableKeymap() atom.workspaceView.openSync(pathToOpen) atom.workspaceView.focus() @@ -19,20 +21,21 @@ describe "WorkspaceView", -> viewState = null simulateReload = -> - workspaceState = atom.workspaceView.serialize() + workspaceState = atom.workspace.serialize() projectState = atom.project.serialize() atom.workspaceView.remove() atom.project = atom.deserializers.deserialize(projectState) - atom.workspaceView = WorkspaceView.deserialize(workspaceState) + atom.workspace = Workspace.deserialize(workspaceState) + atom.workspaceView = new WorkspaceView(atom.workspace) atom.workspaceView.attachToDom() describe "when the serialized WorkspaceView has an unsaved buffer", -> it "constructs the view with the same panes", -> atom.workspaceView.attachToDom() atom.workspaceView.openSync() - editor1 = atom.workspaceView.getActiveView() - buffer = editor1.getBuffer() - editor1.splitRight() + editorView1 = atom.workspaceView.getActiveView() + buffer = editorView1.getEditor().getBuffer() + editorView1.splitRight() expect(atom.workspaceView.getActivePane()).toBe atom.workspaceView.getPanes()[1] simulateReload() @@ -58,31 +61,31 @@ describe "WorkspaceView", -> simulateReload() expect(atom.workspaceView.getEditorViews().length).toBe 4 - editor1 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(0)').view() - editor3 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(1)').view() - editor2 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(0)').view() - editor4 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(1)').view() + editorView1 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(0)').view() + editorView3 = atom.workspaceView.panes.find('.pane-row > .pane .editor:eq(1)').view() + editorView2 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(0)').view() + editorView4 = atom.workspaceView.panes.find('.pane-row > .pane-column > .pane .editor:eq(1)').view() - expect(editor1.getPath()).toBe atom.project.resolve('a') - expect(editor2.getPath()).toBe atom.project.resolve('b') - expect(editor3.getPath()).toBe atom.project.resolve('../sample.js') - expect(editor3.getCursorScreenPosition()).toEqual [2, 4] - expect(editor4.getPath()).toBe atom.project.resolve('../sample.txt') - expect(editor4.getCursorScreenPosition()).toEqual [0, 2] + expect(editorView1.getEditor().getPath()).toBe atom.project.resolve('a') + expect(editorView2.getEditor().getPath()).toBe atom.project.resolve('b') + expect(editorView3.getEditor().getPath()).toBe atom.project.resolve('../sample.js') + expect(editorView3.getEditor().getCursorScreenPosition()).toEqual [2, 4] + expect(editorView4.getEditor().getPath()).toBe atom.project.resolve('../sample.txt') + expect(editorView4.getEditor().getCursorScreenPosition()).toEqual [0, 2] # ensure adjust pane dimensions is called - expect(editor1.width()).toBeGreaterThan 0 - expect(editor2.width()).toBeGreaterThan 0 - expect(editor3.width()).toBeGreaterThan 0 - expect(editor4.width()).toBeGreaterThan 0 + expect(editorView1.width()).toBeGreaterThan 0 + expect(editorView2.width()).toBeGreaterThan 0 + expect(editorView3.width()).toBeGreaterThan 0 + expect(editorView4.width()).toBeGreaterThan 0 - # ensure correct editor is focused again - expect(editor2.isFocused).toBeTruthy() - expect(editor1.isFocused).toBeFalsy() - expect(editor3.isFocused).toBeFalsy() - expect(editor4.isFocused).toBeFalsy() + # ensure correct editorView is focused again + expect(editorView2.isFocused).toBeTruthy() + expect(editorView1.isFocused).toBeFalsy() + expect(editorView3.isFocused).toBeFalsy() + expect(editorView4.isFocused).toBeFalsy() - expect(atom.workspaceView.title).toBe "#{path.basename(editor2.getPath())} - #{atom.project.getPath()}" + expect(atom.workspaceView.title).toBe "#{path.basename(editorView2.getEditor().getPath())} - #{atom.project.getPath()}" describe "where there are no open editors", -> it "constructs the view with no open editors", -> @@ -187,7 +190,7 @@ describe "WorkspaceView", -> describe "when the root view is deserialized", -> it "updates the title to contain the project's path", -> - workspaceView2 = atom.deserializers.deserialize(atom.workspaceView.serialize()) + workspaceView2 = new WorkspaceView(atom.workspace.testSerialization()) item = atom.workspaceView.getActivePaneItem() expect(workspaceView2.title).toBe "#{item.getTitle()} - #{atom.project.getPath()}" workspaceView2.remove() @@ -462,27 +465,27 @@ describe "WorkspaceView", -> it "shows/hides invisibles in all open and future editors", -> atom.workspaceView.height(200) atom.workspaceView.attachToDom() - rightEditor = atom.workspaceView.getActiveView() - rightEditor.setText(" \t ") - leftEditor = rightEditor.splitLeft() - expect(rightEditor.find(".line:first").text()).toBe " " - expect(leftEditor.find(".line:first").text()).toBe " " + rightEditorView = atom.workspaceView.getActiveView() + rightEditorView.getEditor().setText(" \t ") + leftEditorView = rightEditorView.splitLeft() + expect(rightEditorView.find(".line:first").text()).toBe " " + expect(leftEditorView.find(".line:first").text()).toBe " " - withInvisiblesShowing = "#{rightEditor.invisibles.space}#{rightEditor.invisibles.tab} #{rightEditor.invisibles.space}#{rightEditor.invisibles.eol}" + withInvisiblesShowing = "#{rightEditorView.invisibles.space}#{rightEditorView.invisibles.tab} #{rightEditorView.invisibles.space}#{rightEditorView.invisibles.eol}" atom.workspaceView.trigger "window:toggle-invisibles" - expect(rightEditor.find(".line:first").text()).toBe withInvisiblesShowing - expect(leftEditor.find(".line:first").text()).toBe withInvisiblesShowing + expect(rightEditorView.find(".line:first").text()).toBe withInvisiblesShowing + expect(leftEditorView.find(".line:first").text()).toBe withInvisiblesShowing - lowerLeftEditor = leftEditor.splitDown() - expect(lowerLeftEditor.find(".line:first").text()).toBe withInvisiblesShowing + lowerLeftEditorView = leftEditorView.splitDown() + expect(lowerLeftEditorView.find(".line:first").text()).toBe withInvisiblesShowing atom.workspaceView.trigger "window:toggle-invisibles" - expect(rightEditor.find(".line:first").text()).toBe " " - expect(leftEditor.find(".line:first").text()).toBe " " + expect(rightEditorView.find(".line:first").text()).toBe " " + expect(leftEditorView.find(".line:first").text()).toBe " " - lowerRightEditor = rightEditor.splitDown() - expect(lowerRightEditor.find(".line:first").text()).toBe " " + lowerRightEditorView = rightEditorView.splitDown() + expect(lowerRightEditorView.find(".line:first").text()).toBe " " describe ".eachEditorView(callback)", -> beforeEach -> diff --git a/src/atom.coffee b/src/atom.coffee index 4be78d895..f4cc5371e 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -232,8 +232,10 @@ class Atom extends Model # Private: deserializeWorkspaceView: -> + Workspace = require './workspace' WorkspaceView = require './workspace-view' - @workspaceView = @deserializers.deserialize(@state.workspaceView) ? new WorkspaceView + @workspace = Workspace.deserialize(@state.workspace) ? new Workspace + @workspaceView = new WorkspaceView(@workspace) $(@workspaceViewParentSelector).append(@workspaceView) # Private: @@ -279,7 +281,7 @@ class Atom extends Model return if not @project and not @workspaceView @state.syntax = @syntax.serialize() - @state.workspaceView = @workspaceView.serialize() + @state.workspace = @workspace.serialize() @packages.deactivatePackages() @state.packageStates = @packages.packageStates @saveSync() diff --git a/src/cursor-view.coffee b/src/cursor-view.coffee index 5a5bff89f..f76de9436 100644 --- a/src/cursor-view.coffee +++ b/src/cursor-view.coffee @@ -49,7 +49,7 @@ class CursorView extends View else if !@startBlinkingTimeout @startBlinking() - @setVisible(@cursor.isVisible() and not @editorView.isFoldedAtScreenRow(screenPosition.row)) + @setVisible(@cursor.isVisible() and not @editorView.getEditor().isFoldedAtScreenRow(screenPosition.row)) # Override for speed. The base function checks the computedStyle isHidden: -> diff --git a/src/editor-view.coffee b/src/editor-view.coffee index ccf6d7919..d8a2a947d 100644 --- a/src/editor-view.coffee +++ b/src/editor-view.coffee @@ -119,393 +119,118 @@ class EditorView extends View # Some commands are excluded from mini-editors. bindKeys: -> editorBindings = - 'core:move-left': @moveCursorLeft - 'core:move-right': @moveCursorRight - 'core:select-left': @selectLeft - 'core:select-right': @selectRight - 'core:select-all': @selectAll - 'core:backspace': @backspace - 'core:delete': @delete - 'core:undo': @undo - 'core:redo': @redo - 'core:cut': @cutSelection - 'core:copy': @copySelection - 'core:paste': @paste - 'editor:move-to-previous-word': @moveCursorToPreviousWord - 'editor:select-word': @selectWord - 'editor:consolidate-selections': @consolidateSelections - 'editor:backspace-to-beginning-of-word': @backspaceToBeginningOfWord - 'editor:backspace-to-beginning-of-line': @backspaceToBeginningOfLine - 'editor:delete-to-end-of-word': @deleteToEndOfWord - 'editor:delete-line': @deleteLine - 'editor:cut-to-end-of-line': @cutToEndOfLine + 'core:move-left': => @editor.moveCursorLeft() + 'core:move-right': => @editor.moveCursorRight() + 'core:select-left': => @editor.selectLeft() + 'core:select-right': => @editor.selectRight() + 'core:select-all': => @editor.selectAll() + 'core:backspace': => @editor.backspace() + 'core:delete': => @editor.delete() + 'core:undo': => @editor.undo() + 'core:redo': => @editor.redo() + 'core:cut': => @editor.cutSelectedText() + 'core:copy': => @editor.copySelectedText() + 'core:paste': => @editor.pasteText() + 'editor:move-to-previous-word': => @editor.moveCursorToPreviousWord() + 'editor:select-word': => @editor.selectWord() + 'editor:consolidate-selections': (event) => @consolidateSelections(event) + 'editor:backspace-to-beginning-of-word': => @editor.backspaceToBeginningOfWord() + 'editor:backspace-to-beginning-of-line': => @editor.backspaceToBeginningOfLine() + 'editor:delete-to-end-of-word': => @editor.deleteToEndOfWord() + 'editor:delete-line': => @editor.deleteLine() + 'editor:cut-to-end-of-line': => @editor.cutToEndOfLine() 'editor:move-to-beginning-of-screen-line': => @editor.moveCursorToBeginningOfScreenLine() - 'editor:move-to-beginning-of-line': @moveCursorToBeginningOfLine + 'editor:move-to-beginning-of-line': => @editor.moveCursorToBeginningOfLine() 'editor:move-to-end-of-screen-line': => @editor.moveCursorToEndOfScreenLine() - 'editor:move-to-end-of-line': @moveCursorToEndOfLine - 'editor:move-to-first-character-of-line': @moveCursorToFirstCharacterOfLine - 'editor:move-to-beginning-of-word': @moveCursorToBeginningOfWord - 'editor:move-to-end-of-word': @moveCursorToEndOfWord - 'editor:move-to-beginning-of-next-word': @moveCursorToBeginningOfNextWord - 'editor:move-to-previous-word-boundary': @moveCursorToPreviousWordBoundary - 'editor:move-to-next-word-boundary': @moveCursorToNextWordBoundary - 'editor:select-to-end-of-line': @selectToEndOfLine - 'editor:select-to-beginning-of-line': @selectToBeginningOfLine - 'editor:select-to-end-of-word': @selectToEndOfWord - 'editor:select-to-beginning-of-word': @selectToBeginningOfWord - 'editor:select-to-beginning-of-next-word': @selectToBeginningOfNextWord - 'editor:select-to-next-word-boundary': @selectToNextWordBoundary - 'editor:select-to-previous-word-boundary': @selectToPreviousWordBoundary - 'editor:select-to-first-character-of-line': @selectToFirstCharacterOfLine - 'editor:select-line': @selectLine - 'editor:transpose': @transpose - 'editor:upper-case': @upperCase - 'editor:lower-case': @lowerCase + 'editor:move-to-end-of-line': => @editor.moveCursorToEndOfLine() + 'editor:move-to-first-character-of-line': => @editor.moveCursorToFirstCharacterOfLine() + 'editor:move-to-beginning-of-word': => @editor.moveCursorToBeginningOfWord() + 'editor:move-to-end-of-word': => @editor.moveCursorToEndOfWord() + 'editor:move-to-beginning-of-next-word': => @editor.moveCursorToBeginningOfNextWord() + 'editor:move-to-previous-word-boundary': => @editor.moveCursorToPreviousWordBoundary() + 'editor:move-to-next-word-boundary': => @editor.moveCursorToNextWordBoundary() + 'editor:select-to-end-of-line': => @editor.selectToEndOfLine() + 'editor:select-to-beginning-of-line': => @editor.selectToBeginningOfLine() + 'editor:select-to-end-of-word': => @editor.selectToEndOfWord() + 'editor:select-to-beginning-of-word': => @editor.selectToBeginningOfWord() + 'editor:select-to-beginning-of-next-word': => @editor.selectToBeginningOfNextWord() + 'editor:select-to-next-word-boundary': => @editor.selectToNextWordBoundary() + 'editor:select-to-previous-word-boundary': => @editor.selectToPreviousWordBoundary() + 'editor:select-to-first-character-of-line': => @editor.selectToFirstCharacterOfLine() + 'editor:select-line': => @editor.selectLine() + 'editor:transpose': => @editor.transpose() + 'editor:upper-case': => @editor.upperCase() + 'editor:lower-case': => @editor.lowerCase() unless @mini _.extend editorBindings, - 'core:move-up': @moveCursorUp - 'core:move-down': @moveCursorDown - 'core:move-to-top': @moveCursorToTop - 'core:move-to-bottom': @moveCursorToBottom - 'core:page-down': @pageDown - 'core:page-up': @pageUp - 'core:select-up': @selectUp - 'core:select-down': @selectDown - 'core:select-to-top': @selectToTop - 'core:select-to-bottom': @selectToBottom - 'editor:indent': @indent - 'editor:auto-indent': @autoIndent - 'editor:indent-selected-rows': @indentSelectedRows - 'editor:outdent-selected-rows': @outdentSelectedRows - 'editor:newline': @insertNewline - 'editor:newline-below': @insertNewlineBelow - 'editor:newline-above': @insertNewlineAbove - 'editor:add-selection-below': @addSelectionBelow - 'editor:add-selection-above': @addSelectionAbove + 'core:move-up': => @editor.moveCursorUp() + 'core:move-down': => @editor.moveCursorDown() + 'core:move-to-top': => @editor.moveCursorToTop() + 'core:move-to-bottom': => @editor.moveCursorToBottom() + 'core:page-down': => @editor.pageDown() + 'core:page-up': => @editor.pageUp() + 'core:select-up': => @editor.selectUp() + 'core:select-down': => @editor.selectDown() + 'core:select-to-top': => @editor.selectToTop() + 'core:select-to-bottom': => @editor.selectToBottom() + 'editor:indent': => @editor.indent() + 'editor:auto-indent': => @editor.autoIndent() + 'editor:indent-selected-rows': => @editor.indentSelectedRows() + 'editor:outdent-selected-rows': => @editor.outdentSelectedRows() + 'editor:newline': => @editor.insertNewline() + 'editor:newline-below': => @editor.insertNewlineBelow() + 'editor:newline-above': => @editor.insertNewlineAbove() + 'editor:add-selection-below': => @editor.addSelectionBelow() + 'editor:add-selection-above': => @editor.addSelectionAbove() 'editor:split-selections-into-lines': => @editor.splitSelectionsIntoLines() - 'editor:toggle-soft-tabs': @toggleSoftTabs - 'editor:toggle-soft-wrap': @toggleSoftWrap - 'editor:fold-all': @foldAll - 'editor:unfold-all': @unfoldAll - 'editor:fold-current-row': @foldCurrentRow - 'editor:unfold-current-row': @unfoldCurrentRow - 'editor:fold-selection': @foldSelection - 'editor:fold-at-indent-level-1': => @foldAllAtIndentLevel(0) - 'editor:fold-at-indent-level-2': => @foldAllAtIndentLevel(1) - 'editor:fold-at-indent-level-3': => @foldAllAtIndentLevel(2) - 'editor:fold-at-indent-level-4': => @foldAllAtIndentLevel(3) - 'editor:fold-at-indent-level-5': => @foldAllAtIndentLevel(4) - 'editor:fold-at-indent-level-6': => @foldAllAtIndentLevel(5) - 'editor:fold-at-indent-level-7': => @foldAllAtIndentLevel(6) - 'editor:fold-at-indent-level-8': => @foldAllAtIndentLevel(7) - 'editor:fold-at-indent-level-9': => @foldAllAtIndentLevel(8) - 'editor:toggle-line-comments': @toggleLineCommentsInSelection - 'editor:log-cursor-scope': @logCursorScope - 'editor:checkout-head-revision': @checkoutHead - 'editor:copy-path': @copyPathToPasteboard - 'editor:move-line-up': @moveLineUp - 'editor:move-line-down': @moveLineDown - 'editor:duplicate-line': @duplicateLine - 'editor:join-line': @joinLine + 'editor:toggle-soft-tabs': => @toggleSoftTabs() + 'editor:toggle-soft-wrap': => @toggleSoftWrap() + 'editor:fold-all': => @editor.foldAll() + 'editor:unfold-all': => @editor.unfoldAll() + 'editor:fold-current-row': => @editor.foldCurrentRow() + 'editor:unfold-current-row': => @editor.unfoldCurrentRow() + 'editor:fold-selection': => @editor.foldSelection() + 'editor:fold-at-indent-level-1': => @editor.foldAllAtIndentLevel(0) + 'editor:fold-at-indent-level-2': => @editor.foldAllAtIndentLevel(1) + 'editor:fold-at-indent-level-3': => @editor.foldAllAtIndentLevel(2) + 'editor:fold-at-indent-level-4': => @editor.foldAllAtIndentLevel(3) + 'editor:fold-at-indent-level-5': => @editor.foldAllAtIndentLevel(4) + 'editor:fold-at-indent-level-6': => @editor.foldAllAtIndentLevel(5) + 'editor:fold-at-indent-level-7': => @editor.foldAllAtIndentLevel(6) + 'editor:fold-at-indent-level-8': => @editor.foldAllAtIndentLevel(7) + 'editor:fold-at-indent-level-9': => @editor.foldAllAtIndentLevel(8) + 'editor:toggle-line-comments': => @toggleLineCommentsInSelection() + 'editor:log-cursor-scope': => @logCursorScope() + 'editor:checkout-head-revision': => @checkoutHead() + 'editor:copy-path': => @copyPathToPasteboard() + 'editor:move-line-up': => @editor.moveLineUp() + 'editor:move-line-down': => @editor.moveLineDown() + 'editor:duplicate-line': => @editor.duplicateLine() + 'editor:join-line': => @editor.joinLine() 'editor:toggle-indent-guide': => atom.config.toggle('editor.showIndentGuide') 'editor:toggle-line-numbers': => atom.config.toggle('editor.showLineNumbers') - 'editor:scroll-to-cursor': @scrollToCursorPosition + 'editor:scroll-to-cursor': => @scrollToCursorPosition() documentation = {} for name, method of editorBindings do (name, method) => - @command name, (e) => method.call(this, e); false + @command name, (e) -> method(e); false - # {Delegates to: Editor.getCursor} - getCursor: -> @editor.getCursor() + getEditor: -> + @editor - # {Delegates to: Editor.getCursors} - getCursors: -> @editor.getCursors() + # {Delegates to: Editor.getText} + getText: -> + @editor.getText() - # {Delegates to: Editor.addCursorAtScreenPosition} - addCursorAtScreenPosition: (screenPosition) -> @editor.addCursorAtScreenPosition(screenPosition) - - # {Delegates to: Editor.addCursorAtBufferPosition} - addCursorAtBufferPosition: (bufferPosition) -> @editor.addCursorAtBufferPosition(bufferPosition) - - # {Delegates to: Editor.moveCursorUp} - moveCursorUp: -> @editor.moveCursorUp() - - # {Delegates to: Editor.moveCursorDown} - moveCursorDown: -> @editor.moveCursorDown() - - # {Delegates to: Editor.moveCursorLeft} - moveCursorLeft: -> @editor.moveCursorLeft() - - # {Delegates to: Editor.moveCursorRight} - moveCursorRight: -> @editor.moveCursorRight() - - # {Delegates to: Editor.moveCursorToBeginningOfWord} - moveCursorToBeginningOfWord: -> @editor.moveCursorToBeginningOfWord() - - # {Delegates to: Editor.moveCursorToEndOfWord} - moveCursorToEndOfWord: -> @editor.moveCursorToEndOfWord() - - # {Delegates to: Editor.moveCursorToBeginningOfNextWord} - moveCursorToBeginningOfNextWord: -> @editor.moveCursorToBeginningOfNextWord() - - # {Delegates to: Editor.moveCursorToTop} - moveCursorToTop: -> @editor.moveCursorToTop() - - # {Delegates to: Editor.moveCursorToBottom} - moveCursorToBottom: -> @editor.moveCursorToBottom() - - # {Delegates to: Editor.moveCursorToBeginningOfLine} - moveCursorToBeginningOfLine: -> @editor.moveCursorToBeginningOfLine() - - # {Delegates to: Editor.moveCursorToFirstCharacterOfLine} - moveCursorToFirstCharacterOfLine: -> @editor.moveCursorToFirstCharacterOfLine() - - # {Delegates to: Editor.moveCursorToPreviousWordBoundary} - moveCursorToPreviousWordBoundary: -> @editor.moveCursorToPreviousWordBoundary() - - # {Delegates to: Editor.moveCursorToNextWordBoundary} - moveCursorToNextWordBoundary: -> @editor.moveCursorToNextWordBoundary() - - # {Delegates to: Editor.moveCursorToEndOfLine} - moveCursorToEndOfLine: -> @editor.moveCursorToEndOfLine() - - # {Delegates to: Editor.moveLineUp} - moveLineUp: -> @editor.moveLineUp() - - # {Delegates to: Editor.moveLineDown} - moveLineDown: -> @editor.moveLineDown() - - # {Delegates to: Editor.setCursorScreenPosition} - setCursorScreenPosition: (position, options) -> @editor.setCursorScreenPosition(position, options) - - # {Delegates to: Editor.duplicateLine} - duplicateLine: -> @editor.duplicateLine() - - # {Delegates to: Editor.joinLine} - joinLine: -> @editor.joinLine() - - # {Delegates to: Editor.getCursorScreenPosition} - getCursorScreenPosition: -> @editor.getCursorScreenPosition() - - # {Delegates to: Editor.getCursorScreenRow} - getCursorScreenRow: -> @editor.getCursorScreenRow() - - # {Delegates to: Editor.setCursorBufferPosition} - setCursorBufferPosition: (position, options) -> @editor.setCursorBufferPosition(position, options) - - # {Delegates to: Editor.getCursorBufferPosition} - getCursorBufferPosition: -> @editor.getCursorBufferPosition() - - # {Delegates to: Editor.getCurrentParagraphBufferRange} - getCurrentParagraphBufferRange: -> @editor.getCurrentParagraphBufferRange() - - # {Delegates to: Editor.getWordUnderCursor} - getWordUnderCursor: (options) -> @editor.getWordUnderCursor(options) - - # {Delegates to: Editor.getSelection} - getSelection: (index) -> @editor.getSelection(index) - - # {Delegates to: Editor.getSelections} - getSelections: -> @editor.getSelections() - - # {Delegates to: Editor.getSelectionsOrderedByBufferPosition} - getSelectionsOrderedByBufferPosition: -> @editor.getSelectionsOrderedByBufferPosition() - - # {Delegates to: Editor.getLastSelectionInBuffer} - getLastSelectionInBuffer: -> @editor.getLastSelectionInBuffer() - - # {Delegates to: Editor.getSelectedText} - getSelectedText: -> @editor.getSelectedText() - - # {Delegates to: Editor.getSelectedBufferRanges} - getSelectedBufferRanges: -> @editor.getSelectedBufferRanges() - - # {Delegates to: Editor.getSelectedBufferRange} - getSelectedBufferRange: -> @editor.getSelectedBufferRange() - - # {Delegates to: Editor.setSelectedBufferRange} - setSelectedBufferRange: (bufferRange, options) -> @editor.setSelectedBufferRange(bufferRange, options) - - # {Delegates to: Editor.setSelectedBufferRanges} - setSelectedBufferRanges: (bufferRanges, options) -> @editor.setSelectedBufferRanges(bufferRanges, options) - - # {Delegates to: Editor.addSelectionForBufferRange} - addSelectionForBufferRange: (bufferRange, options) -> @editor.addSelectionForBufferRange(bufferRange, options) - - # {Delegates to: Editor.selectRight} - selectRight: -> @editor.selectRight() - - # {Delegates to: Editor.selectLeft} - selectLeft: -> @editor.selectLeft() - - # {Delegates to: Editor.selectUp} - selectUp: -> @editor.selectUp() - - # {Delegates to: Editor.selectDown} - selectDown: -> @editor.selectDown() - - # {Delegates to: Editor.selectToTop} - selectToTop: -> @editor.selectToTop() - - # {Delegates to: Editor.selectToBottom} - selectToBottom: -> @editor.selectToBottom() - - # {Delegates to: Editor.selectAll} - selectAll: -> @editor.selectAll() - - # {Delegates to: Editor.selectToBeginningOfLine} - selectToBeginningOfLine: -> @editor.selectToBeginningOfLine() - - # {Delegates to: Editor.selectToFirstCharacterOfLine} - selectToFirstCharacterOfLine: -> @editor.selectToFirstCharacterOfLine() - - # {Delegates to: Editor.selectToEndOfLine} - selectToEndOfLine: -> @editor.selectToEndOfLine() - - # {Delegates to: Editor.selectToPreviousWordBoundary} - selectToPreviousWordBoundary: -> @editor.selectToPreviousWordBoundary() - - # {Delegates to: Editor.selectToNextWordBoundary} - selectToNextWordBoundary: -> @editor.selectToNextWordBoundary() - - # {Delegates to: Editor.addSelectionBelow} - addSelectionBelow: -> @editor.addSelectionBelow() - - # {Delegates to: Editor.addSelectionAbove} - addSelectionAbove: -> @editor.addSelectionAbove() - - # {Delegates to: Editor.selectToBeginningOfWord} - selectToBeginningOfWord: -> @editor.selectToBeginningOfWord() - - # {Delegates to: Editor.selectToEndOfWord} - selectToEndOfWord: -> @editor.selectToEndOfWord() - - # {Delegates to: Editor.selectToBeginningOfNextWord} - selectToBeginningOfNextWord: -> @editor.selectToBeginningOfNextWord() - - # {Delegates to: Editor.selectWord} - selectWord: -> @editor.selectWord() - - # {Delegates to: Editor.selectLine} - selectLine: -> @editor.selectLine() - - # {Delegates to: Editor.selectToScreenPosition} - selectToScreenPosition: (position) -> @editor.selectToScreenPosition(position) - - # {Delegates to: Editor.transpose} - transpose: -> @editor.transpose() - - # {Delegates to: Editor.upperCase} - upperCase: -> @editor.upperCase() - - # {Delegates to: Editor.lowerCase} - lowerCase: -> @editor.lowerCase() - - # {Delegates to: Editor.clearSelections} - clearSelections: -> @editor.clearSelections() - - # {Delegates to: Editor.backspace} - backspace: -> @editor.backspace() - - # {Delegates to: Editor.backspaceToBeginningOfWord} - backspaceToBeginningOfWord: -> @editor.backspaceToBeginningOfWord() - - # {Delegates to: Editor.backspaceToBeginningOfLine} - backspaceToBeginningOfLine: -> @editor.backspaceToBeginningOfLine() - - # {Delegates to: Editor.delete} - delete: -> @editor.delete() - - # {Delegates to: Editor.deleteToEndOfWord} - deleteToEndOfWord: -> @editor.deleteToEndOfWord() - - # {Delegates to: Editor.deleteLine} - deleteLine: -> @editor.deleteLine() - - # {Delegates to: Editor.cutToEndOfLine} - cutToEndOfLine: -> @editor.cutToEndOfLine() + # {Delegates to: Editor.setText} + setText: (text) -> + @editor.setText(text) # {Delegates to: Editor.insertText} - insertText: (text, options) -> @editor.insertText(text, options) - - # {Delegates to: Editor.insertNewline} - insertNewline: -> @editor.insertNewline() - - # {Delegates to: Editor.insertNewlineBelow} - insertNewlineBelow: -> @editor.insertNewlineBelow() - - # {Delegates to: Editor.insertNewlineAbove} - insertNewlineAbove: -> @editor.insertNewlineAbove() - - # {Delegates to: Editor.indent} - indent: (options) -> @editor.indent(options) - - # {Delegates to: Editor.autoIndentSelectedRows} - autoIndent: (options) -> @editor.autoIndentSelectedRows() - - # {Delegates to: Editor.indentSelectedRows} - indentSelectedRows: -> @editor.indentSelectedRows() - - # {Delegates to: Editor.outdentSelectedRows} - outdentSelectedRows: -> @editor.outdentSelectedRows() - - # {Delegates to: Editor.cutSelectedText} - cutSelection: -> @editor.cutSelectedText() - - # {Delegates to: Editor.copySelectedText} - copySelection: -> @editor.copySelectedText() - - # {Delegates to: Editor.pasteText} - paste: (options) -> @editor.pasteText(options) - - # {Delegates to: Editor.undo} - undo: -> @editor.undo() - - # {Delegates to: Editor.redo} - redo: -> @editor.redo() - - # {Delegates to: Editor.createFold} - createFold: (startRow, endRow) -> @editor.createFold(startRow, endRow) - - # {Delegates to: Editor.foldCurrentRow} - foldCurrentRow: -> @editor.foldCurrentRow() - - # {Delegates to: Editor.unfoldCurrentRow} - unfoldCurrentRow: -> @editor.unfoldCurrentRow() - - # {Delegates to: Editor.foldAll} - foldAll: -> @editor.foldAll() - - # {Delegates to: Editor.unfoldAll} - unfoldAll: -> @editor.unfoldAll() - - # {Delegates to: Editor.foldSelection} - foldSelection: -> @editor.foldSelection() - - # {Delegates to: Editor.destroyFoldsContainingBufferRow} - destroyFoldsContainingBufferRow: (bufferRow) -> @editor.destroyFoldsContainingBufferRow(bufferRow) - - # {Delegates to: Editor.isFoldedAtScreenRow} - isFoldedAtScreenRow: (screenRow) -> @editor.isFoldedAtScreenRow(screenRow) - - # {Delegates to: Editor.isFoldedAtBufferRow} - isFoldedAtBufferRow: (bufferRow) -> @editor.isFoldedAtBufferRow(bufferRow) - - # {Delegates to: Editor.isFoldedAtCursorRow} - isFoldedAtCursorRow: -> @editor.isFoldedAtCursorRow() - - foldAllAtIndentLevel: (indentLevel) -> @editor.foldAllAtIndentLevel(indentLevel) - - # {Delegates to: Editor.lineForScreenRow} - lineForScreenRow: (screenRow) -> @editor.lineForScreenRow(screenRow) - - # {Delegates to: Editor.linesForScreenRows} - linesForScreenRows: (start, end) -> @editor.linesForScreenRows(start, end) - - # {Delegates to: Editor.getScreenLineCount} - getScreenLineCount: -> @editor.getScreenLineCount() + insertText: (text, options) -> + @editor.insertText(text, options) # Private: setHeightInLines: (heightInLines)-> @@ -517,30 +242,6 @@ class EditorView extends View widthInChars ?= @calculateWidthInChars() @editor.setEditorWidthInChars(widthInChars) if widthInChars - # {Delegates to: Editor.getMaxScreenLineLength} - getMaxScreenLineLength: -> @editor.getMaxScreenLineLength() - - # {Delegates to: Editor.getLastScreenRow} - getLastScreenRow: -> @editor.getLastScreenRow() - - # {Delegates to: Editor.clipScreenPosition} - clipScreenPosition: (screenPosition, options={}) -> @editor.clipScreenPosition(screenPosition, options) - - # {Delegates to: Editor.screenPositionForBufferPosition} - screenPositionForBufferPosition: (position, options) -> @editor.screenPositionForBufferPosition(position, options) - - # {Delegates to: Editor.bufferPositionForScreenPosition} - bufferPositionForScreenPosition: (position, options) -> @editor.bufferPositionForScreenPosition(position, options) - - # {Delegates to: Editor.screenRangeForBufferRange} - screenRangeForBufferRange: (range) -> @editor.screenRangeForBufferRange(range) - - # {Delegates to: Editor.bufferRangeForScreenRange} - bufferRangeForScreenRange: (range) -> @editor.bufferRangeForScreenRange(range) - - # {Delegates to: Editor.bufferRowsForScreenRows} - bufferRowsForScreenRows: (startRow, endRow) -> @editor.bufferRowsForScreenRows(startRow, endRow) - # Public: Emulates the "page down" key, where the last row of a buffer scrolls to become the first. pageDown: -> newScrollTop = @scrollTop() + @scrollView[0].clientHeight @@ -600,51 +301,9 @@ class EditorView extends View # Checkout the HEAD revision of this editor's file. checkoutHead: -> - if path = @getPath() + if path = @editor.getPath() atom.project.getRepo()?.checkoutHead(path) - # {Delegates to: Editor.setText} - setText: (text) -> @editor.setText(text) - - # {Delegates to: Editor.save} - save: -> @editor.save() - - # {Delegates to: Editor.getText} - getText: -> @editor.getText() - - # {Delegates to: Editor.getPath} - getPath: -> @editor?.getPath() - - # {Delegates to: Editor.transact} - transact: (fn) -> @editor.transact(fn) - - # {Delegates to: TextBuffer.getLineCount} - getLineCount: -> @getBuffer().getLineCount() - - # {Delegates to: TextBuffer.getLastRow} - getLastBufferRow: -> @getBuffer().getLastRow() - - # {Delegates to: TextBuffer.getTextInRange} - getTextInRange: (range) -> @getBuffer().getTextInRange(range) - - # {Delegates to: TextBuffer.getEofPosition} - getEofPosition: -> @getBuffer().getEofPosition() - - # {Delegates to: TextBuffer.lineForRow} - lineForBufferRow: (row) -> @getBuffer().lineForRow(row) - - # {Delegates to: TextBuffer.lineLengthForRow} - lineLengthForBufferRow: (row) -> @getBuffer().lineLengthForRow(row) - - # {Delegates to: TextBuffer.rangeForRow} - rangeForBufferRow: (row) -> @getBuffer().rangeForRow(row) - - # {Delegates to: TextBuffer.scanInRange} - scanInBufferRange: (args...) -> @getBuffer().scanInRange(args...) - - # {Delegates to: TextBuffer.backwardsScanInRange} - backwardsScanInBufferRange: (args...) -> @getBuffer().backwardsScanInRange(args...) - ### Internal ### configure: -> @@ -695,11 +354,11 @@ class EditorView extends View screenPosition = @screenPositionFromMouseEvent(e) if clickCount == 1 if e.metaKey - @addCursorAtScreenPosition(screenPosition) + @editor.addCursorAtScreenPosition(screenPosition) else if e.shiftKey - @selectToScreenPosition(screenPosition) + @editor.selectToScreenPosition(screenPosition) else - @setCursorScreenPosition(screenPosition) + @editor.setCursorScreenPosition(screenPosition) else if clickCount == 2 @editor.selectWord() unless e.shiftKey else if clickCount == 3 @@ -744,9 +403,9 @@ class EditorView extends View selectedText = @getSelectedText() @hiddenInput.css('width', '100%') @hiddenInput.on 'compositionupdate', (e) => - @insertText(e.originalEvent.data, {select: true, undo: 'skip'}) + @editor.insertText(e.originalEvent.data, {select: true, undo: 'skip'}) @hiddenInput.on 'compositionend', => - @insertText(selectedText, {select: true, undo: 'skip'}) + @editor.insertText(selectedText, {select: true, undo: 'skip'}) @hiddenInput.css('width', '1px') lastInput = '' @@ -757,7 +416,7 @@ class EditorView extends View @selectLeft() lastInput = e.originalEvent.data - @insertText(lastInput) + @editor.insertText(lastInput) @hiddenInput.val(lastInput) false @@ -768,7 +427,7 @@ class EditorView extends View lastMoveEvent = null moveHandler = (event = lastMoveEvent) => if event - @selectToScreenPosition(@screenPositionFromMouseEvent(event)) + @editor.selectToScreenPosition(@screenPositionFromMouseEvent(event)) lastMoveEvent = event $(document).on "mousemove.editor-#{@id}", moveHandler @@ -814,6 +473,7 @@ class EditorView extends View @trigger 'editor:attached', [this] + # TODO: This should be private and only called from the constructor edit: (editor) -> return if editor is @editor @@ -834,7 +494,7 @@ class EditorView extends View @showBufferConflictAlert(@editor) @editor.on "path-changed.editor", => - @reloadGrammar() + @editor.reloadGrammar() @trigger 'editor:path-changed' @editor.on "grammar-changed.editor", => @@ -917,20 +577,15 @@ class EditorView extends View ### Public ### - # Retrieves the {Editor}'s buffer. - # - # Returns the current {TextBuffer}. - getBuffer: -> @editor.buffer - # Scrolls the editor to the bottom. scrollToBottom: -> - @scrollBottom(@getScreenLineCount() * @lineHeight) + @scrollBottom(@editor.getScreenLineCount() * @lineHeight) # Scrolls the editor to the position of the most recently added cursor. # # The editor is also centered. scrollToCursorPosition: -> - @scrollToBufferPosition(@getCursorBufferPosition(), center: true) + @scrollToBufferPosition(@editor.getCursorBufferPosition(), center: true) # Scrolls the editor to the given buffer position. # @@ -966,7 +621,7 @@ class EditorView extends View # # bufferRange - The {Range} to check. highlightFoldsContainingBufferRange: (bufferRange) -> - screenLines = @linesForScreenRows(@firstRenderedScreenRow, @lastRenderedScreenRow) + screenLines = @editor.linesForScreenRows(@firstRenderedScreenRow, @lastRenderedScreenRow) for screenLine, i in screenLines if fold = screenLine.fold screenRow = @firstRenderedScreenRow + i @@ -1180,7 +835,7 @@ class EditorView extends View @setHeightInLines() updateLayerDimensions: -> - height = @lineHeight * @getScreenLineCount() + height = @lineHeight * @editor.getScreenLineCount() unless @layerHeight == height @layerHeight = height @underlayer.height(@layerHeight) @@ -1189,7 +844,7 @@ class EditorView extends View @verticalScrollbarContent.height(@layerHeight) @scrollBottom(height) if @scrollBottom() > height - minWidth = Math.max(@charWidth * @getMaxScreenLineLength() + 20, @scrollView.width()) + minWidth = Math.max(@charWidth * @editor.getMaxScreenLineLength() + 20, @scrollView.width()) unless @layerMinWidth == minWidth @renderedLines.css('min-width', minWidth) @underlayer.css('min-width', minWidth) @@ -1303,9 +958,9 @@ class EditorView extends View updatePlaceholderText: -> return unless @mini - if (not @placeholderText) or @getText() + if (not @placeholderText) or @editor.getText() @find('.placeholder-text').remove() - else if @placeholderText and not @getText() + else if @placeholderText and not @editor.getText() element = @find('.placeholder-text') if element.length element.text(@placeholderText) @@ -1315,7 +970,7 @@ class EditorView extends View updateRenderedLines: -> firstVisibleScreenRow = @getFirstVisibleScreenRow() lastScreenRowToRender = firstVisibleScreenRow + @heightInLines - 1 - lastScreenRow = @getLastScreenRow() + lastScreenRow = @editor.getLastScreenRow() if @firstRenderedScreenRow? and firstVisibleScreenRow >= @firstRenderedScreenRow and lastScreenRowToRender <= @lastRenderedScreenRow renderFrom = Math.min(lastScreenRow, @firstRenderedScreenRow) @@ -1344,15 +999,15 @@ class EditorView extends View if change.bufferDelta? afterStart = change.end + change.bufferDelta + 1 - if @lineForBufferRow(afterStart) is '' + if @editor.lineForBufferRow(afterStart) is '' afterEnd = afterStart - afterEnd++ while @lineForBufferRow(afterEnd + 1) is '' + afterEnd++ while @editor.lineForBufferRow(afterEnd + 1) is '' emptyLineChanges.push({start: afterStart, end: afterEnd, screenDelta: 0}) beforeEnd = change.start - 1 - if @lineForBufferRow(beforeEnd) is '' + if @editor.lineForBufferRow(beforeEnd) is '' beforeStart = beforeEnd - beforeStart-- while @lineForBufferRow(beforeStart - 1) is '' + beforeStart-- while @editor.lineForBufferRow(beforeStart - 1) is '' emptyLineChanges.push({start: beforeStart, end: beforeEnd, screenDelta: 0}) emptyLineChanges @@ -1461,7 +1116,7 @@ class EditorView extends View @renderedLines.css('padding-top', paddingTop) @gutter.lineNumbers.css('padding-top', paddingTop) - paddingBottom = (@getLastScreenRow() - @lastRenderedScreenRow) * @lineHeight + paddingBottom = (@editor.getLastScreenRow() - @lastRenderedScreenRow) * @lineHeight @renderedLines.css('padding-bottom', paddingBottom) @gutter.lineNumbers.css('padding-bottom', paddingBottom) @@ -1480,7 +1135,7 @@ class EditorView extends View # Returns a {Number}. getLastVisibleScreenRow: -> calculatedRow = Math.ceil((@scrollTop() + @scrollView.height()) / @lineHeight) - 1 - screenRow = Math.max(0, Math.min(@getScreenLineCount() - 1, calculatedRow)) + screenRow = Math.max(0, Math.min(@editor.getScreenLineCount() - 1, calculatedRow)) screenRow = 0 if isNaN(screenRow) screenRow @@ -1585,7 +1240,7 @@ class EditorView extends View # # Returns an object with two values: `top` and `left`, representing the pixel positions. pixelPositionForBufferPosition: (position) -> - @pixelPositionForScreenPosition(@screenPositionForBufferPosition(position)) + @pixelPositionForScreenPosition(@editor.screenPositionForBufferPosition(position)) # Converts a screen position to a pixel position. # @@ -1734,31 +1389,15 @@ class EditorView extends View return if @mini @highlightedLine?.removeClass('cursor-line') - if @getSelection().isEmpty() - @highlightedLine = @lineElementForScreenRow(@getCursorScreenRow()) + if @editor.getSelection().isEmpty() + @highlightedLine = @lineElementForScreenRow(@editor.getCursorScreenRow()) @highlightedLine.addClass('cursor-line') else @highlightedLine = null - # {Delegates to: Editor.getGrammar} - getGrammar: -> - @editor.getGrammar() - - # {Delegates to: Editor.setGrammar} - setGrammar: (grammar) -> - @editor.setGrammar(grammar) - - # {Delegates to: Editor.reloadGrammar} - reloadGrammar: -> - @editor.reloadGrammar() - - # {Delegates to: Editor.scopesForBufferPosition} - scopesForBufferPosition: (bufferPosition) -> - @editor.scopesForBufferPosition(bufferPosition) - # Copies the current file path to the native clipboard. copyPathToPasteboard: -> - path = @getPath() + path = @editor.getPath() atom.pasteboard.write(path) if path? ### Internal ### @@ -1845,13 +1484,13 @@ class EditorView extends View ' ' replaceSelectedText: (replaceFn) -> - selection = @getSelection() + selection = @editor.getSelection() return false if selection.isEmpty() - text = replaceFn(@getTextInRange(selection.getBufferRange())) + text = replaceFn(@editor.getTextInRange(selection.getBufferRange())) return false if text is null or text is undefined - @insertText(text, select: true) + @editor.insertText(text, select: true) true consolidateSelections: (e) -> e.abortKeyBinding() unless @editor.consolidateSelections() @@ -1859,12 +1498,6 @@ class EditorView extends View logCursorScope: -> console.log @editor.getCursorScopes() - beginTransaction: -> @editor.beginTransaction() - - commitTransaction: -> @editor.commitTransaction() - - abortTransaction: -> @editor.abortTransaction() - logScreenLines: (start, end) -> @editor.logScreenLines(start, end) diff --git a/src/editor.coffee b/src/editor.coffee index 7e72e43ba..40615a4bf 100644 --- a/src/editor.coffee +++ b/src/editor.coffee @@ -930,7 +930,7 @@ class Editor extends Model # Public: # - # FIXME: What does this do? + # Removes all but one cursor (if there are multiple cursors) consolidateSelections: -> selections = @getSelections() if selections.length > 1 diff --git a/src/gutter.coffee b/src/gutter.coffee index be02dc6d4..9aff208ea 100644 --- a/src/gutter.coffee +++ b/src/gutter.coffee @@ -34,21 +34,22 @@ class Gutter extends View handleMouseEvents: (e) -> editorView = @getEditorView() + editor = @getEditor() startRow = editorView.screenPositionFromMouseEvent(e).row if e.shiftKey - editorView.selectToScreenPosition([startRow + 1, 0]) + editor.selectToScreenPosition([startRow + 1, 0]) return else - editorView.getSelection().setScreenRange([[startRow, 0], [startRow, 0]]) + editor.getSelection().setScreenRange([[startRow, 0], [startRow, 0]]) moveHandler = (e) => start = startRow end = editorView.screenPositionFromMouseEvent(e).row if end > start then end++ else start++ - editorView.getSelection().setScreenRange([[start, 0], [end, 0]]) + editor.getSelection().setScreenRange([[start, 0], [end, 0]]) - $(document).on "mousemove.gutter-#{@getEditorView().id}", moveHandler - $(document).one "mouseup.gutter-#{@getEditorView().id}", => $(document).off 'mousemove', moveHandler + $(document).on "mousemove.gutter-#{editorView.id}", moveHandler + $(document).one "mouseup.gutter-#{editorView.id}", => $(document).off 'mousemove', moveHandler ### Public ### @@ -58,6 +59,9 @@ class Gutter extends View getEditorView: -> @parentView + getEditor: -> + @getEditorView().getEditor() + # Defines whether to show the gutter or not. # # showLineNumbers - A {Boolean} which, if `false`, hides the gutter @@ -192,9 +196,9 @@ class Gutter extends View @elementBuilder.children buildLineElementsHtml: (startScreenRow, endScreenRow) => - editorView = @getEditorView() - maxDigits = editorView.getLineCount().toString().length - rows = editorView.bufferRowsForScreenRows(startScreenRow, endScreenRow) + editor = @getEditor() + maxDigits = editor.getLineCount().toString().length + rows = editor.bufferRowsForScreenRows(startScreenRow, endScreenRow) html = '' for row in rows @@ -204,7 +208,7 @@ class Gutter extends View rowValue = (row + 1).toString() classes = "line-number line-number-#{row}" - classes += ' fold' if editorView.isFoldedAtBufferRow(row) + classes += ' fold' if editor.isFoldedAtBufferRow(row) rowValuePadding = _.multiplyString(' ', maxDigits - rowValue.length) @@ -230,10 +234,11 @@ class Gutter extends View @highlightedLineNumbers.push(highlightedLineNumber) highlightLines: -> - return unless @getEditorView().editor?.isAlive() + editor = @getEditor() + return unless editor?.isAlive() - if @getEditorView().getSelection().isEmpty() - row = @getEditorView().getCursorScreenPosition().row + if editor.getSelection().isEmpty() + row = editor.getCursorScreenPosition().row rowRange = new Range([row, 0], [row, 0]) return if @selectionEmpty and @highlightedRows?.isEqual(rowRange) @@ -242,7 +247,7 @@ class Gutter extends View @highlightedRows = rowRange @selectionEmpty = true else - selectedRows = @getEditorView().getSelection().getScreenRange() + selectedRows = editor.getSelection().getScreenRange() endRow = selectedRows.end.row endRow-- if selectedRows.end.column is 0 selectedRows = new Range([selectedRows.start.row, 0], [endRow, 0]) diff --git a/src/pane-container-view.coffee b/src/pane-container-view.coffee index a55a1d65b..7c1ee5533 100644 --- a/src/pane-container-view.coffee +++ b/src/pane-container-view.coffee @@ -1,4 +1,4 @@ -Serializable = require 'serializable' +Delegator = require 'delegato' {$, View} = require './space-pen-extensions' PaneView = require './pane-view' PaneContainer = require './pane-container' @@ -6,11 +6,9 @@ PaneContainer = require './pane-container' # Private: Manages the list of panes within a {WorkspaceView} module.exports = class PaneContainerView extends View - atom.deserializers.add(this) - Serializable.includeInto(this) + Delegator.includeInto(this) - @deserialize: (state) -> - new this(PaneContainer.deserialize(state.model)) + @delegatesMethod 'saveAll', toProperty: 'model' @content: -> @div class: 'panes' @@ -29,14 +27,8 @@ class PaneContainerView extends View viewClass = model.getViewClass() model._view ?= new viewClass(model) - serializeParams: -> - model: @model.serialize() - ### Public ### - itemDestroyed: (item) -> - @trigger 'item-destroyed', [item] - getRoot: -> @children().first().view() @@ -65,9 +57,6 @@ class PaneContainerView extends View @setRoot(null) @trigger 'pane:removed', [child] if child instanceof PaneView - saveAll: -> - pane.saveItems() for pane in @getPanes() - confirmClose: -> saved = true for pane in @getPanes() @@ -105,28 +94,10 @@ class PaneContainerView extends View @getActivePane()?.activeView paneForUri: (uri) -> - for pane in @getPanes() - view = pane.itemForUri(uri) - return pane if view? - null + @viewForModel(@model.paneForUri(uri)) focusNextPane: -> - panes = @getPanes() - if panes.length > 1 - currentIndex = panes.indexOf(@getFocusedPane()) - nextIndex = (currentIndex + 1) % panes.length - panes[nextIndex].focus() - true - else - false + @model.activateNextPane() focusPreviousPane: -> - panes = @getPanes() - if panes.length > 1 - currentIndex = panes.indexOf(@getFocusedPane()) - previousIndex = currentIndex - 1 - previousIndex = panes.length - 1 if previousIndex < 0 - panes[previousIndex].focus() - true - else - false + @model.activatePreviousPane() diff --git a/src/pane-container.coffee b/src/pane-container.coffee index 14803afc8..9f0dfc7ea 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -1,3 +1,4 @@ +{find} = require 'underscore-plus' {Model} = require 'theorist' Serializable = require 'serializable' Pane = require './pane' @@ -36,14 +37,32 @@ class PaneContainer extends Model getPanes: -> @root?.getPanes() ? [] + paneForUri: (uri) -> + find @getPanes(), (pane) -> pane.itemForUri(uri)? + + saveAll: -> + pane.saveItems() for pane in @getPanes() + activateNextPane: -> panes = @getPanes() if panes.length > 1 currentIndex = panes.indexOf(@activePane) nextIndex = (currentIndex + 1) % panes.length panes[nextIndex].activate() + true else - @activePane = null + false + + activatePreviousPane: -> + panes = @getPanes() + if panes.length > 1 + currentIndex = panes.indexOf(@activePane) + previousIndex = currentIndex - 1 + previousIndex = panes.length - 1 if previousIndex < 0 + panes[previousIndex].activate() + true + else + false onRootChanged: (root) => @unsubscribe(@previousRoot) if @previousRoot? @@ -64,3 +83,6 @@ class PaneContainer extends Model destroyEmptyPanes: -> pane.destroy() for pane in @getPanes() when pane.items.length is 0 + + itemDestroyed: (item) -> + @emit 'item-destroyed', item diff --git a/src/pane-model.coffee b/src/pane-model.coffee deleted file mode 100644 index a6902968a..000000000 --- a/src/pane-model.coffee +++ /dev/null @@ -1,307 +0,0 @@ -{find, compact, extend} = require 'underscore-plus' -{dirname} = require 'path' -{Model, Sequence} = require 'theorist' -Serializable = require 'serializable' -PaneAxis = require './pane-axis' -PaneView = null - -# Public: A container for multiple items, one of which is *active* at a given -# time. With the default packages, a tab is displayed for each item and the -# active item's view is displayed. -module.exports = -class PaneModel extends Model - atom.deserializers.add(this) - Serializable.includeInto(this) - - @properties - container: null - activeItem: null - focused: false - - # Public: Only one pane is considered *active* at a time. A pane is activated - # when it is focused, and when focus returns to the pane container after - # moving to another element such as a panel, it returns to the active pane. - @behavior 'active', -> - @$container - .switch((container) -> container?.$activePane) - .map((activePane) => activePane is this) - .distinctUntilChanged() - - # Private: - constructor: (params) -> - super - - @items = Sequence.fromArray(params?.items ? []) - @activeItem ?= @items[0] - - @subscribe @items.onEach (item) => - if typeof item.on is 'function' - @subscribe item, 'destroyed', => @removeItem(item) - - @subscribe @items.onRemoval (item, index) => - @unsubscribe item if typeof item.on is 'function' - - @activate() if params?.active - - # Private: Called by the Serializable mixin during serialization. - serializeParams: -> - items: compact(@items.map((item) -> item.serialize?())) - activeItemUri: @activeItem?.getUri?() - focused: @focused - active: @active - - # Private: Called by the Serializable mixin during deserialization. - deserializeParams: (params) -> - {items, activeItemUri} = params - params.items = compact(items.map (itemState) -> atom.deserializers.deserialize(itemState)) - params.activeItem = find params.items, (item) -> item.getUri?() is activeItemUri - params - - # Private: Called by the view layer to construct a view for this model. - getViewClass: -> PaneView ?= require './pane-view' - - isActive: -> @active - - # Private: Called by the view layer to indicate that the pane has gained focus. - focus: -> - @focused = true - @activate() unless @isActive() - - # Private: Called by the view layer to indicate that the pane has lost focus. - blur: -> - @focused = false - true # if this is called from an event handler, don't cancel it - - # Public: Makes this pane the *active* pane, causing it to gain focus - # immediately. - activate: -> - @container?.activePane = this - @emit 'activated' - - # Private: - getPanes: -> [this] - - # Public: - getItems: -> - @items.slice() - - # Public: Returns the item at the specified index. - itemAtIndex: (index) -> - @items[index] - - # Public: Makes the next item active. - activateNextItem: -> - index = @getActiveItemIndex() - if index < @items.length - 1 - @activateItemAtIndex(index + 1) - else - @activateItemAtIndex(0) - - # Public: Makes the previous item active. - activatePreviousItem: -> - index = @getActiveItemIndex() - if index > 0 - @activateItemAtIndex(index - 1) - else - @activateItemAtIndex(@items.length - 1) - - # Public: Returns the index of the current active item. - getActiveItemIndex: -> - @items.indexOf(@activeItem) - - # Public: Makes the item at the given index active. - activateItemAtIndex: (index) -> - @activateItem(@itemAtIndex(index)) - - # Public: Makes the given item active, adding the item if necessary. - activateItem: (item) -> - if item? - @addItem(item) - @activeItem = item - - # Public: Adds the item to the pane. - # - # * item: - # The item to add. It can be a model with an associated view or a view. - # * index: - # An optional index at which to add the item. If omitted, the item is - # added to the end. - # - # Returns the added item - addItem: (item, index=@getActiveItemIndex() + 1) -> - return if item in @items - - @items.splice(index, 0, item) - @emit 'item-added', item, index - item - - # Private: - removeItem: (item, destroying) -> - index = @items.indexOf(item) - return if index is -1 - @activateNextItem() if item is @activeItem and @items.length > 1 - @items.splice(index, 1) - @emit 'item-removed', item, index, destroying - @destroy() if @items.length is 0 - - # Public: Moves the given item to the specified index. - moveItem: (item, newIndex) -> - oldIndex = @items.indexOf(item) - @items.splice(oldIndex, 1) - @items.splice(newIndex, 0, item) - @emit 'item-moved', item, newIndex - - # Public: Moves the given item to the given index at another pane. - moveItemToPane: (item, pane, index) -> - pane.addItem(item, index) - @removeItem(item) - - # Public: Destroys the currently active item and make the next item active. - destroyActiveItem: -> - @destroyItem(@activeItem) - false - - # Public: Destroys the given item. If it is the active item, activate the next - # one. If this is the last item, also destroys the pane. - destroyItem: (item) -> - @emit 'before-item-destroyed', item - if @promptToSaveItem(item) - @emit 'item-destroyed', item - @removeItem(item, true) - item.destroy?() - true - else - false - - # Public: Destroys all items and destroys the pane. - destroyItems: -> - @destroyItem(item) for item in @getItems() - - # Public: Destroys all items but the active one. - destroyInactiveItems: -> - @destroyItem(item) for item in @getItems() when item isnt @activeItem - - # Private: Called by model superclass. - destroyed: -> - @container.activateNextPane() if @isActive() - item.destroy?() for item in @items.slice() - - # Public: Prompts the user to save the given item if it can be saved and is - # currently unsaved. - promptToSaveItem: (item) -> - return true unless item.shouldPromptToSave?() - - uri = item.getUri() - chosen = atom.confirm - message: "'#{item.getTitle?() ? item.getUri()}' has changes, do you want to save them?" - detailedMessage: "Your changes will be lost if you close this item without saving." - buttons: ["Save", "Cancel", "Don't Save"] - - switch chosen - when 0 then @saveItem(item, -> true) - when 1 then false - when 2 then true - - # Public: Saves the active item. - saveActiveItem: -> - @saveItem(@activeItem) - - # Public: Saves the active item at a prompted-for location. - saveActiveItemAs: -> - @saveItemAs(@activeItem) - - # Public: Saves the specified item. - # - # * item: The item to save. - # * nextAction: An optional function which will be called after the item is saved. - saveItem: (item, nextAction) -> - if item.getUri?() - item.save?() - nextAction?() - else - @saveItemAs(item, nextAction) - - # Public: Saves the given item at a prompted-for location. - # - # * item: The item to save. - # * nextAction: An optional function which will be called after the item is saved. - saveItemAs: (item, nextAction) -> - return unless item.saveAs? - - itemPath = item.getPath?() - itemPath = dirname(itemPath) if itemPath - path = atom.showSaveDialogSync(itemPath) - if path - item.saveAs(path) - nextAction?() - - # Public: Saves all items. - saveItems: -> - @saveItem(item) for item in @getItems() - - # Public: Returns the first item that matches the given URI or undefined if - # none exists. - itemForUri: (uri) -> - find @items, (item) -> item.getUri?() is uri - - # Public: Activates the first item that matches the given URI. Returns a - # boolean indicating whether a matching item was found. - activateItemForUri: (uri) -> - if item = @itemForUri(uri) - @activateItem(item) - true - else - false - - # Private: - copyActiveItem: -> - @activeItem.copy?() ? atom.deserializers.deserialize(@activeItem.serialize()) - - # Public: Creates a new pane to the left of the receiver. - # - # * params: - # + items: An optional array of items with which to construct the new pane. - # - # Returns the new {PaneModel}. - splitLeft: (params) -> - @split('horizontal', 'before', params) - - # Public: Creates a new pane to the right of the receiver. - # - # * params: - # + items: An optional array of items with which to construct the new pane. - # - # Returns the new {PaneModel}. - splitRight: (params) -> - @split('horizontal', 'after', params) - - # Public: Creates a new pane above the receiver. - # - # * params: - # + items: An optional array of items with which to construct the new pane. - # - # Returns the new {PaneModel}. - splitUp: (params) -> - @split('vertical', 'before', params) - - # Public: Creates a new pane below the receiver. - # - # * params: - # + items: An optional array of items with which to construct the new pane. - # - # Returns the new {PaneModel}. - splitDown: (params) -> - @split('vertical', 'after', params) - - # Private: - split: (orientation, side, params) -> - if @parent.orientation isnt orientation - @parent.replaceChild(this, new PaneAxis({@container, orientation, children: [this]})) - - newPane = new @constructor(extend({focused: true}, params)) - switch side - when 'before' then @parent.insertChildBefore(this, newPane) - when 'after' then @parent.insertChildAfter(this, newPane) - - newPane.activate() - newPane diff --git a/src/pane-view.coffee b/src/pane-view.coffee index fe9da3f31..11b4a94cd 100644 --- a/src/pane-view.coffee +++ b/src/pane-view.coffee @@ -1,6 +1,6 @@ {$, View} = require './space-pen-extensions' -Serializable = require 'serializable' Delegator = require 'delegato' +PropertyAccessors = require 'property-accessors' Pane = require './pane' @@ -12,14 +12,11 @@ Pane = require './pane' # building a package that deals with switching between panes or tiems. module.exports = class PaneView extends View - Serializable.includeInto(this) Delegator.includeInto(this) + PropertyAccessors.includeInto(this) @version: 1 - @deserialize: (state) -> - new this(Pane.deserialize(state.model)) - @content: (wrappedView) -> @div class: 'pane', tabindex: -1, => @div class: 'item-views', outlet: 'itemViews' @@ -47,14 +44,11 @@ class PaneView extends View @handleEvents() handleEvents: -> - @subscribe @model, 'destroyed', => @remove() - @subscribe @model.$activeItem, @onActiveItemChanged @subscribe @model, 'item-added', @onItemAdded @subscribe @model, 'item-removed', @onItemRemoved @subscribe @model, 'item-moved', @onItemMoved @subscribe @model, 'before-item-destroyed', @onBeforeItemDestroyed - @subscribe @model, 'item-destroyed', @onItemDestroyed @subscribe @model, 'activated', @onActivated @subscribe @model.$active, @onActiveStatusChanged @@ -85,13 +79,6 @@ class PaneView extends View @command 'pane:close', => @destroyItems() @command 'pane:close-other-items', => @destroyInactiveItems() - deserializeParams: (params) -> - params.model = Pane.deserialize(params.model) - params - - serializeParams: -> - model: @model.serialize() - # Deprecated: Use ::destroyItem removeItem: (item) -> @destroyItem(item) @@ -153,7 +140,6 @@ class PaneView extends View view.show() if @attached view.focus() if hasFocus - @activeView = view @trigger 'pane:active-item-changed', [item] onItemAdded: (item, index) => @@ -166,7 +152,6 @@ class PaneView extends View @viewsByItem.delete(item) if viewToRemove? - viewToRemove.setModel?(null) if destroyed viewToRemove.remove() else @@ -181,15 +166,13 @@ class PaneView extends View @unsubscribe(item) if typeof item.off is 'function' @trigger 'pane:before-item-destroyed', [item] - onItemDestroyed: (item) => - @getContainer()?.itemDestroyed(item) - # Private: activeItemTitleChanged: => @trigger 'pane:active-item-title-changed' # Private: viewForItem: (item) -> + return unless item? if item instanceof $ item else if view = @viewsByItem.get(item) @@ -201,8 +184,7 @@ class PaneView extends View view # Private: - viewForActiveItem: -> - @viewForItem(@activeItem) + @::accessor 'activeView', -> @viewForItem(@activeItem) splitLeft: (items...) -> @model.splitLeft({items})._view diff --git a/src/pane.coffee b/src/pane.coffee index 746cfb989..346f46bce 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -36,7 +36,7 @@ class Pane extends Model @subscribe @items.onEach (item) => if typeof item.on is 'function' - @subscribe item, 'destroyed', => @removeItem(item) + @subscribe item, 'destroyed', => @removeItem(item, true) @subscribe @items.onRemoval (item, index) => @unsubscribe item if typeof item.on is 'function' @@ -142,6 +142,7 @@ class Pane extends Model @activateNextItem() if item is @activeItem and @items.length > 1 @items.splice(index, 1) @emit 'item-removed', item, index, destroying + @container?.itemDestroyed(item) if destroying @destroy() if @items.length is 0 # Public: Moves the given item to the specified index. @@ -166,7 +167,6 @@ class Pane extends Model destroyItem: (item) -> @emit 'before-item-destroyed', item if @promptToSaveItem(item) - @emit 'item-destroyed', item @removeItem(item, true) item.destroy?() true diff --git a/src/select-list.coffee b/src/select-list.coffee index 21cb270de..415560aed 100644 --- a/src/select-list.coffee +++ b/src/select-list.coffee @@ -30,7 +30,7 @@ class SelectList extends View # This method can be overridden by subclasses but `super` should always # be called. initialize: -> - @miniEditor.getBuffer().on 'changed', => @schedulePopulateList() + @miniEditor.getEditor().getBuffer().on 'changed', => @schedulePopulateList() @miniEditor.hiddenInput.on 'focusout', => @cancel() unless @cancelling @on 'core:move-up', => @selectPreviousItem() @on 'core:move-down', => @selectNextItem() @@ -98,7 +98,7 @@ class SelectList extends View # # Returns a {String} to use when fuzzy filtering the elements to display. getFilterQuery: -> - @miniEditor.getText() + @miniEditor.getEditor().getText() # Public: Build the DOM elements using the array from the last call to # {.setArray}. @@ -207,7 +207,7 @@ class SelectList extends View # Private: cancelled: -> - @miniEditor.setText('') + @miniEditor.getEditor().setText('') @miniEditor.updateDisplay() # Public: Cancel and close the select list dialog. diff --git a/src/workspace-view.coffee b/src/workspace-view.coffee index b5bdff230..8c7f8b4ac 100644 --- a/src/workspace-view.coffee +++ b/src/workspace-view.coffee @@ -1,10 +1,11 @@ ipc = require 'ipc' path = require 'path' Q = require 'q' -{$, $$, View} = require './space-pen-extensions' _ = require 'underscore-plus' +Delegator = require 'delegato' +{$, $$, View} = require './space-pen-extensions' fs = require 'fs-plus' -Serializable = require 'serializable' +Workspace = require './workspace' EditorView = require './editor-view' PaneView = require './pane-view' PaneColumnView = require './pane-column-view' @@ -38,10 +39,14 @@ Editor = require './editor' # module.exports = class WorkspaceView extends View - Serializable.includeInto(this) - atom.deserializers.add(this, PaneView, PaneRowView, PaneColumnView, EditorView) + Delegator.includeInto(this) - @version: 2 + @delegatesProperty 'fullScreen', 'destroyedItemUris', toProperty: 'model' + @delegatesMethods 'open', 'openSync', 'openSingletonSync', 'reopenItemSync', + 'saveActivePaneItem', 'saveActivePaneItemAs', 'saveAll', 'destroyActivePaneItem', + toProperty: 'model' + + @version: 4 @configDefaults: ignoredNames: [".git", ".svn", ".DS_Store"] @@ -59,13 +64,14 @@ class WorkspaceView extends View @div class: 'panes', outlet: 'panes' # Private: - initialize: ({panes, @fullScreen}={}) -> - panes ?= new PaneContainerView + initialize: (@model) -> + @model ?= new Workspace + + panes = new PaneContainerView(@model.paneContainer) @panes.replaceWith(panes) @panes = panes - @destroyedItemUris = [] - @subscribe @panes, 'item-destroyed', @onPaneItemDestroyed + @subscribe @model, 'uri-opened', => @trigger 'uri-opened' @updateTitle() @@ -116,16 +122,6 @@ class WorkspaceView extends View @command 'core:save', => @saveActivePaneItem() @command 'core:save-as', => @saveActivePaneItemAs() - # Private: - deserializeParams: (params) -> - params.panes = atom.deserializers.deserialize(params.panes) - params - - # Private: - serializeParams: -> - panes: @panes.serialize() - fullScreen: atom.isFullScreen() - # Private: handleFocus: (e) -> if @getActivePane() @@ -149,81 +145,6 @@ class WorkspaceView extends View confirmClose: -> @panes.confirmClose() - # Public: Asynchronously opens a given a filepath in Atom. - # - # * filePath: A file path - # * options - # + initialLine: The buffer line number to open to. - # - # Returns a promise that resolves to the {Editor} for the file URI. - open: (filePath, options={}) -> - changeFocus = options.changeFocus ? true - filePath = atom.project.resolve(filePath) - initialLine = options.initialLine - activePane = @getActivePane() - - editor = activePane.itemForUri(atom.project.relativize(filePath)) if activePane and filePath - promise = atom.project.open(filePath, {initialLine}) if not editor - - Q(editor ? promise) - .then (editor) => - if not activePane - activePane = new PaneView(editor) - @panes.setRoot(activePane) - - @itemOpened(editor) - activePane.activateItem(editor) - activePane.activate() if changeFocus - @trigger "uri-opened" - editor - .catch (error) -> - console.error(error.stack ? error) - - # Private: Only used in specs - openSync: (uri, {changeFocus, initialLine, pane, split}={}) -> - changeFocus ?= true - pane ?= @getActivePane() - uri = atom.project.relativize(uri) - - if pane - if uri - paneItem = pane.itemForUri(uri) ? atom.project.openSync(uri, {initialLine}) - else - paneItem = atom.project.openSync() - - if split == 'right' - panes = @getPanes() - if panes.length == 1 - pane = panes[0].splitRight() - else - pane = _.last(panes) - else if split == 'left' - pane = @getPanes()[0] - - pane.activateItem(paneItem) - else - paneItem = atom.project.openSync(uri, {initialLine}) - pane = new PaneView(paneItem) - @panes.setRoot(pane) - - @itemOpened(paneItem) - - pane.activate() if changeFocus - paneItem - - openSingletonSync: (uri, {changeFocus, initialLine, split}={}) -> - changeFocus ?= true - uri = atom.project.relativize(uri) - pane = @panes.paneForUri(uri) - - if pane - paneItem = pane.itemForUri(uri) - pane.activateItem(paneItem) - pane.activate() if changeFocus - paneItem - else - @openSync(uri, {changeFocus, initialLine, split}) - # Public: Updates the application's title, based on whichever file is open. updateTitle: -> if projectPath = atom.project.getPath() @@ -242,22 +163,6 @@ class WorkspaceView extends View getEditorViews: -> @panes.find('.pane > .item-views > .editor').map(-> $(this).view()).toArray() - # Private: Retrieves all of the modified buffers that are open and unsaved. - # - # Returns an {Array} of {TextBuffer}s. - getModifiedBuffers: -> - modifiedBuffers = [] - for pane in @getPanes() - for item in pane.getItems() when item instanceof Editor - modifiedBuffers.push item.buffer if item.buffer.isModified() - modifiedBuffers - - # Private: Retrieves all of the paths to open files. - # - # Returns an {Array} of {String}s. - getOpenBufferPaths: -> - _.uniq(_.flatten(@getEditorViews().map (editorView) -> editorView.getOpenBufferPaths())) - # Public: Prepends the element to the top of the window. prependToTop: (element) -> @vertical.prepend(element) @@ -296,39 +201,23 @@ class WorkspaceView extends View # Public: Returns the currently focused item from within the focused {PaneView} getActivePaneItem: -> - @panes.getActivePaneItem() + @model.activePaneItem # Public: Returns the view of the currently focused item. getActiveView: -> @panes.getActiveView() - # Public: destroy/close the active item. - destroyActivePaneItem: -> - @getActivePane()?.destroyActiveItem() - - # Public: save the active item. - saveActivePaneItem: -> - @getActivePane()?.saveActiveItem() - - # Public: save the active item as. - saveActivePaneItemAs: -> - @getActivePane()?.saveActiveItemAs() - # Public: Focuses the previous pane by id. - focusPreviousPane: -> @panes.focusPreviousPane() + focusPreviousPane: -> @model.activatePreviousPane() # Public: Focuses the next pane by id. - focusNextPane: -> @panes.focusNextPane() + focusNextPane: -> @model.activateNextPane() # Public: # # FIXME: Difference between active and focused pane? getFocusedPane: -> @panes.getFocusedPane() - # Public: Saves all of the open items within panes. - saveAll: -> - @panes.saveAll() - # Public: Fires a callback on each open {PaneView}. eachPane: (callback) -> @panes.eachPane(callback) @@ -350,20 +239,6 @@ class WorkspaceView extends View # Private: Destroys everything. remove: -> + @model.destroy() editorView.remove() for editorView in @getEditorViews() super - - # Private: Adds the destroyed item's uri to the list of items to reopen. - onPaneItemDestroyed: (e, item) => - if uri = item.getUri?() - @destroyedItemUris.push(uri) - - # Public: Reopens the last-closed item uri if it hasn't already been reopened. - reopenItemSync: -> - if uri = @destroyedItemUris.pop() - @openSync(uri) - - # Private: Removes the item's uri from the list of potential items to reopen. - itemOpened: (item) -> - if uri = item.getUri?() - _.remove(@destroyedItemUris, uri) diff --git a/src/workspace.coffee b/src/workspace.coffee new file mode 100644 index 000000000..39f730e0a --- /dev/null +++ b/src/workspace.coffee @@ -0,0 +1,143 @@ +{remove, last} = require 'underscore-plus' +{Model} = require 'theorist' +Q = require 'q' +Serializable = require 'serializable' +Delegator = require 'delegato' +PaneContainer = require './pane-container' +Pane = require './pane' + +# Public: Represents the view state of the entire window, including the panes at +# the center and panels around the periphery. You can access the singleton +# instance via `atom.workspace`. +module.exports = +class Workspace extends Model + atom.deserializers.add(this) + Serializable.includeInto(this) + + @delegatesProperty 'activePane', 'activePaneItem', toProperty: 'paneContainer' + @delegatesMethod 'getPanes', 'saveAll', 'activateNextPane', 'activatePreviousPane', + toProperty: 'paneContainer' + + @properties + paneContainer: -> new PaneContainer + fullScreen: false + destroyedItemUris: -> [] + + # Private: + constructor: -> + super + @subscribe @paneContainer, 'item-destroyed', @onPaneItemDestroyed + + # Private: Called by the Serializable mixin during deserialization + deserializeParams: (params) -> + params.paneContainer = PaneContainer.deserialize(params.paneContainer) + params + + # Private: Called by the Serializable mixin during serialization. + serializeParams: -> + paneContainer: @paneContainer.serialize() + fullScreen: atom.isFullScreen() + + # Public: Asynchronously opens a given a filepath in Atom. + # + # * filePath: A file path + # * options + # + initialLine: The buffer line number to open to. + # + # Returns a promise that resolves to the {Editor} for the file URI. + open: (filePath, options={}) -> + changeFocus = options.changeFocus ? true + filePath = atom.project.resolve(filePath) + initialLine = options.initialLine + activePane = @activePane + + editor = activePane.itemForUri(atom.project.relativize(filePath)) if activePane and filePath + promise = atom.project.open(filePath, {initialLine}) if not editor + + Q(editor ? promise) + .then (editor) => + if not activePane + activePane = new Pane(items: [editor]) + @paneContainer.root = activePane + + @itemOpened(editor) + activePane.activateItem(editor) + activePane.activate() if changeFocus + @emit "uri-opened" + editor + .catch (error) -> + console.error(error.stack ? error) + + # Private: Only used in specs + openSync: (uri, {changeFocus, initialLine, pane, split}={}) -> + changeFocus ?= true + pane ?= @activePane + uri = atom.project.relativize(uri) + + if pane + if uri + paneItem = pane.itemForUri(uri) ? atom.project.openSync(uri, {initialLine}) + else + paneItem = atom.project.openSync() + + if split == 'right' + panes = @getPanes() + if panes.length == 1 + pane = panes[0].splitRight() + else + pane = last(panes) + else if split == 'left' + pane = @getPanes()[0] + + pane.activateItem(paneItem) + else + paneItem = atom.project.openSync(uri, {initialLine}) + pane = new Pane(items: [paneItem]) + @paneContainer.root = pane + + @itemOpened(paneItem) + + pane.activate() if changeFocus + paneItem + + # Public: Synchronously open an editor for the given URI or activate an existing + # editor in any pane if one already exists. + openSingletonSync: (uri, {changeFocus, initialLine, split}={}) -> + changeFocus ?= true + uri = atom.project.relativize(uri) + pane = @paneContainer.paneForUri(uri) + + if pane + paneItem = pane.itemForUri(uri) + pane.activateItem(paneItem) + pane.activate() if changeFocus + paneItem + else + @openSync(uri, {changeFocus, initialLine, split}) + + # Public: Reopens the last-closed item uri if it hasn't already been reopened. + reopenItemSync: -> + if uri = @destroyedItemUris.pop() + @openSync(uri) + + # Public: save the active item. + saveActivePaneItem: -> + @activePane?.saveActiveItem() + + # Public: save the active item as. + saveActivePaneItemAs: -> + @activePane?.saveActiveItemAs() + + # Public: destroy/close the active item. + destroyActivePaneItem: -> + @activePane?.destroyActiveItem() + + # Private: Removes the item's uri from the list of potential items to reopen. + itemOpened: (item) -> + if uri = item.getUri?() + remove(@destroyedItemUris, uri) + + # Private: Adds the destroyed item's uri to the list of items to reopen. + onPaneItemDestroyed: (item) => + if uri = item.getUri?() + @destroyedItemUris.push(uri) diff --git a/vendor/apm b/vendor/apm index 35edbb07f..b843d54a8 160000 --- a/vendor/apm +++ b/vendor/apm @@ -1 +1 @@ -Subproject commit 35edbb07fb4abba49dd97d12a1ad8c4adb71625f +Subproject commit b843d54a8ac495231efd680ab3648dfd9247896c