diff --git a/.travis.yml b/.travis.yml index c9157d207..bf1bd330e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,17 +8,19 @@ branches: env: global: - ATOM_ACCESS_TOKEN=da809a6077bb1b0aa7c5623f7b2d5f1fec2faae4 - - NODE_VERSION=0.12 compiler: clang matrix: include: - os: linux + env: NODE_VERSION=0.12 + - os: linux + env: NODE_VERSION=4 - os: osx - env: ATOM_SPECS_TASK=core + env: ATOM_SPECS_TASK=core NODE_VERSION=0.12 - os: osx - env: ATOM_SPECS_TASK=packages + env: ATOM_SPECS_TASK=packages NODE_VERSION=0.12 sudo: false diff --git a/apm/package.json b/apm/package.json index 6ea664d38..3f366d857 100644 --- a/apm/package.json +++ b/apm/package.json @@ -6,6 +6,6 @@ "url": "https://github.com/atom/atom.git" }, "dependencies": { - "atom-package-manager": "1.0.5" + "atom-package-manager": "1.1.1" } } diff --git a/atom.sh b/atom.sh index ecd7da052..fcabc0e52 100755 --- a/atom.sh +++ b/atom.sh @@ -11,6 +11,12 @@ else exit 1 fi +if [ "$(basename $0)" == 'atom-beta' ]; then + BETA_VERSION=true +else + BETA_VERSION= +fi + while getopts ":wtfvh-:" opt; do case "$opt" in -) @@ -45,7 +51,11 @@ if [ $REDIRECT_STDERR ]; then fi if [ $OS == 'Mac' ]; then - ATOM_APP_NAME=Atom.app + if [ -n "$BETA_VERSION" ]; then + ATOM_APP_NAME="Atom Beta.app" + else + ATOM_APP_NAME="Atom.app" + fi if [ -z "${ATOM_PATH}" ]; then # If ATOM_PATH isnt set, check /Applications and then ~/Applications for Atom.app @@ -74,9 +84,14 @@ if [ $OS == 'Mac' ]; then elif [ $OS == 'Linux' ]; then SCRIPT=$(readlink -f "$0") USR_DIRECTORY=$(readlink -f $(dirname $SCRIPT)/..) - ATOM_PATH="$USR_DIRECTORY/share/atom/atom" - ATOM_HOME="${ATOM_HOME:-$HOME/.atom}" + if [ -n "$BETA_VERSION" ]; then + ATOM_PATH="$USR_DIRECTORY/share/atom-beta/atom" + else + ATOM_PATH="$USR_DIRECTORY/share/atom/atom" + fi + + ATOM_HOME="${ATOM_HOME:-$HOME/.atom}" mkdir -p "$ATOM_HOME" : ${TMPDIR:=/tmp} diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index abbb433ca..351db9d31 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -31,38 +31,52 @@ module.exports = (grunt) -> # This allows all subsequent paths to the relative to the root of the repo grunt.file.setBase(path.resolve('..')) - tmpDir = os.tmpdir() - appName = if process.platform is 'darwin' then 'Atom.app' else 'Atom' - buildDir = grunt.option('build-dir') ? path.join(tmpDir, 'atom-build') - buildDir = path.resolve(buildDir) + # Options installDir = grunt.option('install-dir') + buildDir = grunt.option('build-dir') + buildDir ?= path.join(os.tmpdir(), 'atom-build') + buildDir = path.resolve(buildDir) + disableAutoUpdate = grunt.option('no-auto-update') ? false channel = grunt.option('channel') channel ?= process.env.JANKY_BRANCH if process.env.JANKY_BRANCH in ['stable', 'beta'] channel ?= 'dev' - home = if process.platform is 'win32' then process.env.USERPROFILE else process.env.HOME - electronDownloadDir = path.join(home, '.atom', 'electron') + metadata = packageJson + appName = packageJson.productName + appFileName = packageJson.name + apmFileName = 'apm' - symbolsDir = path.join(buildDir, 'Atom.breakpad.syms') + if channel is 'beta' + appName += ' Beta' + appFileName += '-beta' + apmFileName += '-beta' + + appName += '.app' if process.platform is 'darwin' shellAppDir = path.join(buildDir, appName) + symbolsDir = path.join(buildDir, 'Atom.breakpad.syms') + if process.platform is 'win32' + homeDir = process.env.USERPROFILE contentsDir = shellAppDir appDir = path.join(shellAppDir, 'resources', 'app') installDir ?= path.join(process.env.ProgramFiles, appName) killCommand = 'taskkill /F /IM atom.exe' else if process.platform is 'darwin' + homeDir = process.env.HOME contentsDir = path.join(shellAppDir, 'Contents') appDir = path.join(contentsDir, 'Resources', 'app') installDir ?= path.join('/Applications', appName) killCommand = 'pkill -9 Atom' else + homeDir = process.env.HOME contentsDir = shellAppDir appDir = path.join(shellAppDir, 'resources', 'app') installDir ?= process.env.INSTALL_PREFIX ? '/usr/local' killCommand ='pkill -9 atom' installDir = path.resolve(installDir) + electronDownloadDir = path.join(homeDir, '.atom', 'electron') coffeeConfig = glob_to_multiple: @@ -104,7 +118,7 @@ module.exports = (grunt) -> csonConfig = options: rootObject: true - cachePath: path.join(home, '.atom', 'compile-cache', 'grunt-cson') + cachePath: path.join(homeDir, '.atom', 'compile-cache', 'grunt-cson') glob_to_multiple: expand: true @@ -155,7 +169,11 @@ module.exports = (grunt) -> grunt.initConfig pkg: grunt.file.readJSON('package.json') - atom: {appDir, appName, symbolsDir, buildDir, contentsDir, installDir, shellAppDir, channel} + atom: { + appName, channel, metadata, disableAutoUpdate, + appFileName, apmFileName, + appDir, buildDir, contentsDir, installDir, shellAppDir, symbolsDir, + } docsOutputDir: 'docs/output' @@ -236,8 +254,8 @@ module.exports = (grunt) -> outputDirectory: path.join(buildDir, 'installer') authors: 'GitHub Inc.' loadingGif: path.resolve(__dirname, '..', 'resources', 'win', 'loading.gif') - iconUrl: 'https://raw.githubusercontent.com/atom/atom/master/resources/app-icons/stable/atom.ico' - setupIcon: path.resolve(__dirname, '..', 'resources', 'app-icons', 'stable', 'atom.ico') + iconUrl: "https://raw.githubusercontent.com/atom/atom/master/resources/app-icons/#{channel}/atom.ico" + setupIcon: path.resolve(__dirname, '..', 'resources', 'app-icons', channel, 'atom.ico') remoteReleases: 'https://atom.io/api/updates' shell: diff --git a/build/package.json b/build/package.json index 1ed151e1f..4960d6571 100644 --- a/build/package.json +++ b/build/package.json @@ -20,9 +20,9 @@ "grunt-contrib-coffee": "~0.12.0", "grunt-contrib-csslint": "~0.2.0", "grunt-contrib-less": "~0.8.0", - "grunt-cson": "0.15.0", + "grunt-cson": "0.16.0", "grunt-download-electron": "^2.1.1", - "grunt-electron-installer": "1.0.3-0", + "grunt-electron-installer": "1.0.3", "grunt-lesslint": "0.17.0", "grunt-peg": "~1.1.0", "grunt-shell": "~0.3.1", @@ -33,7 +33,7 @@ "rcedit": "~0.3.0", "request": "~2.27.0", "rimraf": "~2.2.2", - "runas": "^2", + "runas": "^3.1", "tello": "1.0.5", "temp": "~0.8.1", "underscore-plus": "1.x", diff --git a/build/tasks/build-task.coffee b/build/tasks/build-task.coffee index 745858502..213aa0da4 100644 --- a/build/tasks/build-task.coffee +++ b/build/tasks/build-task.coffee @@ -90,10 +90,6 @@ module.exports = (grunt) -> path.join('snippets', 'node_modules', 'pegjs') path.join('snippets', 'node_modules', '.bin', 'pegjs') - # These aren't needed since WeakMap is built-in - path.join('emissary', 'node_modules', 'es6-weak-map') - path.join('property-accessors', 'node_modules', 'es6-weak-map') - '.DS_Store' '.jshintrc' '.npmignore' @@ -190,4 +186,5 @@ module.exports = (grunt) -> dependencies = ['compile', 'generate-license:save', 'generate-module-cache', 'compile-packages-slug'] dependencies.push('copy-info-plist') if process.platform is 'darwin' dependencies.push('set-exe-icon') if process.platform is 'win32' + dependencies.push('disable-autoupdate') if grunt.config.get('atom.disableAutoUpdate') grunt.task.run(dependencies...) diff --git a/build/tasks/disable-autoupdate-task.coffee b/build/tasks/disable-autoupdate-task.coffee new file mode 100644 index 000000000..7517543da --- /dev/null +++ b/build/tasks/disable-autoupdate-task.coffee @@ -0,0 +1,12 @@ +fs = require 'fs' +path = require 'path' + +module.exports = (grunt) -> + + grunt.registerTask 'disable-autoupdate', 'Set up disableAutoUpdate field in package.json file', -> + appDir = fs.realpathSync(grunt.config.get('atom.appDir')) + + metadata = grunt.file.readJSON(path.join(appDir, 'package.json')) + metadata._disableAutoUpdate = grunt.config.get('atom.disableAutoUpdate') + + grunt.file.write(path.join(appDir, 'package.json'), JSON.stringify(metadata)) diff --git a/build/tasks/install-task.coffee b/build/tasks/install-task.coffee index 86a827a1b..54fd06022 100644 --- a/build/tasks/install-task.coffee +++ b/build/tasks/install-task.coffee @@ -1,15 +1,19 @@ path = require 'path' -_ = require 'underscore-plus' fs = require 'fs-plus' runas = null temp = require 'temp' module.exports = (grunt) -> - {cp, mkdir, rm} = require('./task-helpers')(grunt) + {cp, fillTemplate, mkdir, rm} = require('./task-helpers')(grunt) grunt.registerTask 'install', 'Install the built application', -> + appName = grunt.config.get('atom.appName') + appFileName = grunt.config.get('atom.appFileName') + apmFileName = grunt.config.get('atom.apmFileName') + buildDir = grunt.config.get('atom.buildDir') installDir = grunt.config.get('atom.installDir') shellAppDir = grunt.config.get('atom.shellAppDir') + {description} = grunt.config.get('atom.metadata') if process.platform is 'win32' runas ?= require 'runas' @@ -18,7 +22,7 @@ module.exports = (grunt) -> grunt.log.error("Failed to copy #{shellAppDir} to #{installDir}") createShortcut = path.resolve 'script', 'create-shortcut.cmd' - runas('cmd', ['/c', createShortcut, path.join(installDir, 'atom.exe'), 'Atom']) + runas('cmd', ['/c', createShortcut, path.join(installDir, 'atom.exe'), appName]) else if process.platform is 'darwin' rm installDir mkdir path.dirname(installDir) @@ -28,32 +32,29 @@ module.exports = (grunt) -> cp shellAppDir, tempFolder fs.renameSync(tempFolder, installDir) else - binDir = path.join(installDir, 'bin') - shareDir = path.join(installDir, 'share', 'atom') - - mkdir binDir - cp 'atom.sh', path.join(binDir, 'atom') + shareDir = path.join(installDir, 'share', appFileName) rm shareDir mkdir path.dirname(shareDir) cp shellAppDir, shareDir - # Create atom.desktop if installation not in temporary folder - tmpDir = if process.env.TMPDIR? then process.env.TMPDIR else '/tmp' - if installDir.indexOf(tmpDir) isnt 0 - desktopFile = path.join('resources', 'linux', 'atom.desktop.in') - desktopInstallFile = path.join(installDir, 'share', 'applications', 'atom.desktop') + unless installDir.indexOf(process.env.TMPDIR ? '/tmp') is 0 + iconPath = path.join(shareDir, 'resources', 'app.asar.unpacked', 'resources', 'atom.png') - {description} = grunt.file.readJSON('package.json') - iconName = path.join(shareDir, 'resources', 'app.asar.unpacked', 'resources', 'atom.png') - executable = path.join(shareDir, 'atom') - template = _.template(String(fs.readFileSync(desktopFile))) - filled = template({description, iconName, executable}) + mkdir path.join(installDir, 'share', 'applications') + fillTemplate( + path.join('resources', 'linux', 'atom.desktop.in'), + path.join(installDir, 'share', 'applications', appFileName + '.desktop'), + {appName, appFileName, description, iconPath, installDir} + ) - grunt.file.write(desktopInstallFile, filled) + binDir = path.join(installDir, 'bin') + mkdir binDir + cp 'atom.sh', path.join(binDir, appFileName) - # Create relative symbol link for apm. - process.chdir(binDir) - rm('apm') - fs.symlinkSync(path.join('..', 'share', 'atom', 'resources', 'app', 'apm', 'node_modules', '.bin', 'apm'), 'apm') + rm(path.join(binDir, apmFileName)) + fs.symlinkSync( + path.join('..', 'share', appFileName, 'resources', 'app', 'apm', 'node_modules', '.bin', 'apm'), + path.join(binDir, apmFileName) + ) - fs.chmodSync(path.join(shareDir, 'atom'), "755") + fs.chmodSync(path.join(shareDir, 'atom'), '755') diff --git a/build/tasks/mkdeb-task.coffee b/build/tasks/mkdeb-task.coffee index e448bb91e..5f50122f8 100644 --- a/build/tasks/mkdeb-task.coffee +++ b/build/tasks/mkdeb-task.coffee @@ -1,28 +1,18 @@ -fs = require 'fs' path = require 'path' -_ = require 'underscore-plus' module.exports = (grunt) -> - {spawn} = require('./task-helpers')(grunt) - - fillTemplate = (filePath, data) -> - template = _.template(String(fs.readFileSync("#{filePath}.in"))) - filled = template(data) - - outputPath = path.join(grunt.config.get('atom.buildDir'), path.basename(filePath)) - grunt.file.write(outputPath, filled) - outputPath - - getInstalledSize = (buildDir, callback) -> - cmd = 'du' - args = ['-sk', path.join(buildDir, 'Atom')] - spawn {cmd, args}, (error, {stdout}) -> - installedSize = stdout.split(/\s+/)?[0] or '200000' # default to 200MB - callback(null, installedSize) + {spawn, fillTemplate} = require('./task-helpers')(grunt) grunt.registerTask 'mkdeb', 'Create debian package', -> done = @async() + + appName = grunt.config.get('atom.appName') + appFileName = grunt.config.get('atom.appFileName') + apmFileName = grunt.config.get('atom.apmFileName') buildDir = grunt.config.get('atom.buildDir') + installDir = '/usr' + shellAppDir = grunt.config.get('atom.shellAppDir') + {version, description} = grunt.config.get('atom.metadata') channel = grunt.config.get('atom.channel') if process.arch is 'ia32' @@ -32,23 +22,38 @@ module.exports = (grunt) -> else return done("Unsupported arch #{process.arch}") - {name, version, description} = grunt.file.readJSON('package.json') - section = 'devel' - maintainer = 'GitHub ' - installDir = '/usr' - iconName = 'atom' - executable = path.join(installDir, 'share', 'atom', 'atom') - getInstalledSize buildDir, (error, installedSize) -> - data = {name, version, description, section, arch, maintainer, installDir, iconName, installedSize, executable} - controlFilePath = fillTemplate(path.join('resources', 'linux', 'debian', 'control'), data) - desktopFilePath = fillTemplate(path.join('resources', 'linux', 'atom.desktop'), data) - iconPath = path.join('resources', 'app-icons', channel, 'png', '1024.png') + desktopFilePath = path.join(buildDir, appFileName + '.desktop') + fillTemplate( + path.join('resources', 'linux', 'atom.desktop.in'), + desktopFilePath, + {appName, appFileName, description, installDir, iconPath: appFileName} + ) + + getInstalledSize shellAppDir, (error, installedSize) -> + if error? + return done(error) + + controlFilePath = path.join(buildDir, 'control') + fillTemplate( + path.join('resources', 'linux', 'debian', 'control.in'), + controlFilePath, + {appFileName, version, arch, installedSize, description} + ) + + iconPath = path.join(shellAppDir, 'resources', 'app.asar.unpacked', 'resources', 'atom.png') cmd = path.join('script', 'mkdeb') - args = [version, arch, controlFilePath, desktopFilePath, iconPath, buildDir] + args = [appFileName, version, channel, arch, controlFilePath, desktopFilePath, iconPath, buildDir] spawn {cmd, args}, (error) -> if error? done(error) else - grunt.log.ok "Created #{buildDir}/atom-#{version}-#{arch}.deb" + grunt.log.ok "Created #{buildDir}/#{appFileName}-#{version}-#{arch}.deb" done() + + getInstalledSize = (directory, callback) -> + cmd = 'du' + args = ['-sk', directory] + spawn {cmd, args}, (error, {stdout}) -> + installedSize = stdout.split(/\s+/)?[0] or '200000' # default to 200MB + callback(null, installedSize) diff --git a/build/tasks/mkrpm-task.coffee b/build/tasks/mkrpm-task.coffee index eb4fd5847..641127857 100644 --- a/build/tasks/mkrpm-task.coffee +++ b/build/tasks/mkrpm-task.coffee @@ -1,21 +1,18 @@ -fs = require 'fs' path = require 'path' -_ = require 'underscore-plus' module.exports = (grunt) -> - {spawn, rm, mkdir} = require('./task-helpers')(grunt) - - fillTemplate = (filePath, data) -> - template = _.template(String(fs.readFileSync("#{filePath}.in"))) - filled = template(data) - - outputPath = path.join(grunt.config.get('atom.buildDir'), path.basename(filePath)) - grunt.file.write(outputPath, filled) - outputPath + {spawn, fillTemplate, rm, mkdir} = require('./task-helpers')(grunt) grunt.registerTask 'mkrpm', 'Create rpm package', -> done = @async() + appName = grunt.config.get('atom.appName') + appFileName = grunt.config.get('atom.appFileName') + apmFileName = grunt.config.get('atom.apmFileName') + buildDir = grunt.config.get('atom.buildDir') + installDir = '/usr' + {version, description} = grunt.config.get('atom.metadata') + if process.arch is 'ia32' arch = 'i386' else if process.arch is 'x64' @@ -23,7 +20,12 @@ module.exports = (grunt) -> else return done("Unsupported arch #{process.arch}") - {name, version, description} = grunt.file.readJSON('package.json') + desktopFilePath = path.join(buildDir, appFileName + '.desktop') + fillTemplate( + path.join('resources', 'linux', 'atom.desktop.in'), + desktopFilePath, + {appName, appFileName, description, installDir, iconPath: appFileName} + ) # RPM versions can't have dashes in them. # * http://www.rpm.org/max-rpm/ch-rpm-file-format.html @@ -31,23 +33,19 @@ module.exports = (grunt) -> version = version.replace(/-beta/, "~beta") version = version.replace(/-dev/, "~dev") - buildDir = grunt.config.get('atom.buildDir') + specFilePath = path.join(buildDir, appFileName + '.spec') + fillTemplate( + path.join('resources', 'linux', 'redhat', 'atom.spec.in'), + specFilePath, + {appName, appFileName, apmFileName, installDir, version, description} + ) rpmDir = path.join(buildDir, 'rpm') rm rpmDir mkdir rpmDir - installDir = grunt.config.get('atom.installDir') - shareDir = path.join(installDir, 'share', 'atom') - iconName = 'atom' - executable = path.join(shareDir, 'atom') - - data = {name, version, description, installDir, iconName, executable} - specFilePath = fillTemplate(path.join('resources', 'linux', 'redhat', 'atom.spec'), data) - desktopFilePath = fillTemplate(path.join('resources', 'linux', 'atom.desktop'), data) - cmd = path.join('script', 'mkrpm') - args = [specFilePath, desktopFilePath, buildDir] + args = [appName, appFileName, specFilePath, desktopFilePath, buildDir] spawn {cmd, args}, (error) -> if error? done(error) diff --git a/build/tasks/publish-build-task.coffee b/build/tasks/publish-build-task.coffee index 1f8925aed..38dd84839 100644 --- a/build/tasks/publish-build-task.coffee +++ b/build/tasks/publish-build-task.coffee @@ -31,7 +31,8 @@ module.exports = (gruntObject) -> cp path.join(docsOutputDir, 'api.json'), path.join(buildDir, 'atom-api.json') grunt.registerTask 'upload-assets', 'Upload the assets to a GitHub release', -> - switch process.env.JANKY_BRANCH + branchName = process.env.JANKY_BRANCH + switch branchName when 'stable' isPrerelease = false when 'beta' @@ -54,7 +55,7 @@ module.exports = (gruntObject) -> zipAssets buildDir, assets, (error) -> return done(error) if error? - getAtomDraftRelease isPrerelease, (error, release) -> + getAtomDraftRelease isPrerelease, branchName, (error, release) -> return done(error) if error? assetNames = (asset.assetName for asset in assets) deleteExistingAssets release, assetNames, (error) -> @@ -76,7 +77,12 @@ getAssets = -> ] when 'win32' assets = [{assetName: 'atom-windows.zip', sourcePath: 'Atom'}] - for squirrelAsset in ['AtomSetup.exe', 'RELEASES', "atom-#{version}-full.nupkg", "atom-#{version}-delta.nupkg"] + + # NuGet packages can't have dots in their pre-release name, so we remove + # those dots in `grunt-electron-installer` when generating the package. + nupkgVersion = version.replace(/\.(\d+)$/, '$1') + + for squirrelAsset in ['AtomSetup.exe', 'RELEASES', "atom-#{nupkgVersion}-full.nupkg", "atom-#{nupkgVersion}-delta.nupkg"] cp path.join(buildDir, 'installer', squirrelAsset), path.join(buildDir, squirrelAsset) assets.push({assetName: squirrelAsset, sourcePath: assetName}) assets @@ -128,7 +134,7 @@ zipAssets = (buildDir, assets, callback) -> tasks.push(zip.bind(this, buildDir, sourcePath, assetName)) async.parallel(tasks, callback) -getAtomDraftRelease = (isPrerelease, callback) -> +getAtomDraftRelease = (isPrerelease, branchName, callback) -> atomRepo = new GitHub({repo: 'atom/atom', token}) atomRepo.getReleases {prerelease: isPrerelease}, (error, releases=[]) -> if error? @@ -150,9 +156,9 @@ getAtomDraftRelease = (isPrerelease, callback) -> firstDraft.assets = assets callback(null, firstDraft) else - createAtomDraftRelease(isPrerelease, callback) + createAtomDraftRelease(isPrerelease, branchName, callback) -createAtomDraftRelease = (isPrerelease, callback) -> +createAtomDraftRelease = (isPrerelease, branchName, callback) -> {version} = require('../../package.json') options = uri: 'https://api.github.com/repos/atom/atom/releases' @@ -161,6 +167,7 @@ createAtomDraftRelease = (isPrerelease, callback) -> json: tag_name: "v#{version}" prerelease: isPrerelease + target_commitish: branchName name: version draft: true body: """ diff --git a/build/tasks/task-helpers.coffee b/build/tasks/task-helpers.coffee index 2819c952a..d24cdec77 100644 --- a/build/tasks/task-helpers.coffee +++ b/build/tasks/task-helpers.coffee @@ -1,5 +1,6 @@ fs = require 'fs-plus' path = require 'path' +_ = require 'underscore-plus' module.exports = (grunt) -> cp: (source, destination, {filter}={}) -> @@ -66,3 +67,7 @@ module.exports = (grunt) -> engines?.atom? catch error false + + fillTemplate: (templatePath, outputPath, data) -> + content = _.template(String(fs.readFileSync(templatePath)))(data) + grunt.file.write(outputPath, content) diff --git a/docs/build-instructions/windows.md b/docs/build-instructions/windows.md index d89033c82..f54e1e049 100644 --- a/docs/build-instructions/windows.md +++ b/docs/build-instructions/windows.md @@ -18,6 +18,8 @@ ### On Windows 8 or 10 * [Visual Studio Express 2013 or 2015 for Windows Desktop](http://www.visualstudio.com/en-us/downloads/download-visual-studio-vs#DownloadFamilies_2) + * For VS 2015, be sure to customize the installation to include Visual C++. It's not installed by default. + * Some have experienced issues with Node locating C++ on VS 2015. If so, try VS 2013. * [node.js](http://nodejs.org/download/) (0.10.x or 0.12.x) or [io.js](https://iojs.org) (1.x or 2.x) * [Python](https://www.python.org/downloads/) v2.7.x (required by [node-gyp](https://github.com/TooTallNate/node-gyp)) * [GitHub Desktop](http://desktop.github.com/) diff --git a/exports/atom.coffee b/exports/atom.coffee index 2a41b7d70..1ceb60fa9 100644 --- a/exports/atom.coffee +++ b/exports/atom.coffee @@ -2,7 +2,6 @@ TextBuffer = require 'text-buffer' {Point, Range} = TextBuffer {File, Directory} = require 'pathwatcher' {Emitter, Disposable, CompositeDisposable} = require 'event-kit' -{includeDeprecatedAPIs, deprecate} = require 'grim' module.exports = BufferedNodeProcess: require '../src/buffered-node-process' @@ -23,8 +22,3 @@ module.exports = unless process.env.ATOM_SHELL_INTERNAL_RUN_AS_NODE module.exports.Task = require '../src/task' module.exports.TextEditor = require '../src/text-editor' - -if includeDeprecatedAPIs - Object.defineProperty module.exports, 'Git', get: -> - deprecate "Please require `GitRepository` instead of `Git`: `{GitRepository} = require 'atom'`" - module.exports.GitRepository diff --git a/package.json b/package.json index 7d52810f6..22a339978 100644 --- a/package.json +++ b/package.json @@ -12,49 +12,44 @@ "url": "https://github.com/atom/atom/issues" }, "license": "MIT", - "electronVersion": "0.30.6", + "electronVersion": "0.30.7", "dependencies": { "async": "0.2.6", - "atom-keymap": "^5.1.11", + "atom-keymap": "^6.0.0", "babel-core": "^5.8.21", "bootstrap": "^3.3.4", "clear-cut": "^2.0.1", "coffee-script": "1.8.0", "color": "^0.7.3", - "delegato": "^1", - "emissary": "^1.3.3", "event-kit": "^1.3.0", "first-mate": "^5.0.0", "fs-plus": "^2.8.0", "fstream": "0.1.24", "fuzzaldrin": "^2.1", - "git-utils": "^3.0.0", + "git-utils": "^4", "grim": "1.4.2", "jasmine-json": "~0.0", "jasmine-tagged": "^1.1.4", "jquery": "^2.1.1", "less-cache": "0.22", "marked": "^0.3.4", - "mixto": "^1", "normalize-package-data": "^2.0.0", - "nslog": "^2.0.0", - "oniguruma": "^4.2.2", - "pathwatcher": "^5.0.0", + "nslog": "^3", + "oniguruma": "^5", + "pathwatcher": "^6.2", "property-accessors": "^1.1.3", "random-words": "0.0.1", - "runas": "2.0.0", - "scandal": "2.1.2", + "runas": "^3.1", + "scandal": "^2.2", "scoped-property-store": "^0.17.0", - "scrollbar-style": "^3.1", + "scrollbar-style": "^3.2", "season": "^5.3", "semver": "^4.3.3", - "serializable": "^1", "service-hub": "^0.6.2", "source-map-support": "^0.3.2", "stacktrace-parser": "0.1.1", "temp": "0.8.1", - "text-buffer": "7.0.3", - "theorist": "^1.0.2", + "text-buffer": "7.1.2", "typescript-simple": "1.0.0", "underscore-plus": "^1.6.6", "yargs": "^3.23.0" @@ -66,10 +61,10 @@ "atom-light-ui": "0.43.0", "base16-tomorrow-dark-theme": "0.27.0", "base16-tomorrow-light-theme": "0.9.0", - "one-dark-ui": "1.1.3", - "one-dark-syntax": "1.1.0", - "one-light-syntax": "1.1.0", - "one-light-ui": "1.1.3", + "one-dark-ui": "1.1.4", + "one-dark-syntax": "1.1.1", + "one-light-syntax": "1.1.1", + "one-light-ui": "1.1.4", "solarized-dark-syntax": "0.38.1", "solarized-light-syntax": "0.22.1", "about": "1.1.0", @@ -77,20 +72,20 @@ "autocomplete-atom-api": "0.9.2", "autocomplete-css": "0.11.0", "autocomplete-html": "0.7.2", - "autocomplete-plus": "2.19.1", + "autocomplete-plus": "2.20.0", "autocomplete-snippets": "1.7.1", "autoflow": "0.25.0", "autosave": "0.22.0", "background-tips": "0.26.0", "bookmarks": "0.38.0", - "bracket-matcher": "0.76.0", + "bracket-matcher": "0.78.0", "command-palette": "0.36.0", "deprecation-cop": "0.54.0", "dev-live-reload": "0.47.0", "encoding-selector": "0.21.0", "exception-reporting": "0.37.0", - "find-and-replace": "0.181.0", - "fuzzy-finder": "0.89.0", + "find-and-replace": "0.182.0", + "fuzzy-finder": "0.90.0", "git-diff": "0.56.0", "go-to-line": "0.30.0", "grammar-selector": "0.47.0", @@ -98,49 +93,49 @@ "incompatible-packages": "0.25.0", "keybinding-resolver": "0.33.0", "line-ending-selector": "0.0.5", - "link": "0.30.0", - "markdown-preview": "0.152.0", + "link": "0.31.0", + "markdown-preview": "0.154.0", "metrics": "0.51.0", "notifications": "0.59.0", "open-on-github": "0.38.0", "package-generator": "0.40.0", "release-notes": "0.53.0", - "settings-view": "0.219.0", - "snippets": "0.99.0", - "spell-check": "0.59.0", + "settings-view": "0.222.0", + "snippets": "0.100.0", + "spell-check": "0.60.0", "status-bar": "0.79.0", "styleguide": "0.44.0", - "symbols-view": "0.107.0", + "symbols-view": "0.108.0", "tabs": "0.84.0", "timecop": "0.33.0", - "tree-view": "0.188.0", + "tree-view": "0.189.0", "update-package-dependencies": "0.10.0", "welcome": "0.30.0", "whitespace": "0.31.0", "wrap-guide": "0.36.0", "language-c": "0.48.0", "language-clojure": "0.17.0", - "language-coffee-script": "0.41.0", + "language-coffee-script": "0.42.0", "language-csharp": "0.10.0", "language-css": "0.34.0", "language-gfm": "0.81.0", "language-git": "0.10.0", "language-go": "0.39.0", - "language-html": "0.41.2", + "language-html": "0.41.3", "language-hyperlink": "0.14.0", "language-java": "0.16.0", - "language-javascript": "0.95.0", + "language-javascript": "0.96.0", "language-json": "0.16.0", "language-less": "0.28.2", - "language-make": "0.17.0", + "language-make": "0.18.0", "language-mustache": "0.12.0", "language-objective-c": "0.15.0", "language-perl": "0.29.0", "language-php": "0.30.0", "language-property-list": "0.8.0", "language-python": "0.40.0", - "language-ruby": "0.59.0", - "language-ruby-on-rails": "0.22.0", + "language-ruby": "0.60.0", + "language-ruby-on-rails": "0.23.0", "language-sass": "0.41.0", "language-shellscript": "0.17.0", "language-source": "0.9.0", diff --git a/resources/linux/atom.desktop.in b/resources/linux/atom.desktop.in index 1969e3f26..290d368d2 100644 --- a/resources/linux/atom.desktop.in +++ b/resources/linux/atom.desktop.in @@ -1,9 +1,9 @@ [Desktop Entry] -Name=Atom +Name=<%= appName %> Comment=<%= description %> GenericName=Text Editor -Exec=<%= executable %> %U -Icon=<%= iconName %> +Exec=<%= installDir %>/share/<%= appFileName %>/atom %U +Icon=<%= iconPath %> Type=Application StartupNotify=true Categories=GNOME;GTK;Utility;TextEditor;Development; diff --git a/resources/linux/debian/control.in b/resources/linux/debian/control.in index 7e47f27c6..d06095f4b 100644 --- a/resources/linux/debian/control.in +++ b/resources/linux/debian/control.in @@ -1,12 +1,12 @@ -Package: <%= name %> +Package: <%= appFileName %> Version: <%= version %> Depends: git, gconf2, gconf-service, libgtk2.0-0, libudev0 | libudev1, libgcrypt11 | libgcrypt20, libnotify4, libxtst6, libnss3, python, gvfs-bin, xdg-utils, libcap2 Recommends: lsb-release Suggests: libgnome-keyring0, gir1.2-gnomekeyring-1.0 -Section: <%= section %> +Section: devel Priority: optional Architecture: <%= arch %> Installed-Size: <%= installedSize %> -Maintainer: <%= maintainer %> +Maintainer: GitHub Description: <%= description %> Atom is a free and open source text editor that is modern, approachable, and hackable to the core. diff --git a/resources/linux/redhat/atom.spec.in b/resources/linux/redhat/atom.spec.in index 8ef886e31..3397135bd 100644 --- a/resources/linux/redhat/atom.spec.in +++ b/resources/linux/redhat/atom.spec.in @@ -1,4 +1,4 @@ -Name: <%= name %> +Name: <%= appFileName %> Version: <%= version %> Release: 0.1%{?dist} Summary: <%= description %> @@ -13,25 +13,23 @@ Requires: lsb-core-noarch <%= description %> %install -mkdir -p %{buildroot}/<%= installDir %>/share/atom/ -cp -r Atom/* %{buildroot}/<%= installDir %>/share/atom/ -mkdir -p %{buildroot}/<%= installDir %>/bin/ -ln -sf ../share/atom/resources/app/apm/node_modules/.bin/apm %{buildroot}/<%= installDir %>/bin/apm -cp atom.sh %{buildroot}/<%= installDir %>/bin/atom -chmod 755 %{buildroot}/<%= installDir %>/bin/atom -mkdir -p %{buildroot}/<%= installDir %>/share/applications/ -cp atom.desktop %{buildroot}/<%= installDir %>/share/applications/ +mkdir -p "%{buildroot}/<%= installDir %>/share/<%= appFileName %>/" +cp -r "<%= appName %>"/* "%{buildroot}/<%= installDir %>/share/<%= appFileName %>/" +mkdir -p "%{buildroot}/<%= installDir %>/bin/" +ln -sf "../share/<%= appFileName %>/resources/app/apm/node_modules/.bin/apm" "%{buildroot}/<%= installDir %>/bin/<%= apmFileName %>" +cp atom.sh "%{buildroot}/<%= installDir %>/bin/<%= appFileName %>" +chmod 755 "%{buildroot}/<%= installDir %>/bin/<%= appFileName %>" +mkdir -p "%{buildroot}/<%= installDir %>/share/applications/" +cp "<%= appFileName %>.desktop" "%{buildroot}/<%= installDir %>/share/applications/" -# copy over icons in sizes that most desktop environments like -for i in 1024 512 256 128 64 48 32 24 16 -do - mkdir -p %{buildroot}/<%= installDir %>/share/icons/hicolor/${i}x${i}/apps - cp icons/${i}.png %{buildroot}/<%= installDir %>/share/icons/hicolor/${i}x${i}/apps/atom.png +for i in 1024 512 256 128 64 48 32 24 16; do + mkdir -p "%{buildroot}/<%= installDir %>/share/icons/hicolor/${i}x${i}/apps" + cp "icons/${i}.png" "%{buildroot}/<%= installDir %>/share/icons/hicolor/${i}x${i}/apps/<%= appFileName %>.png" done %files -<%= installDir %>/bin/atom -<%= installDir %>/bin/apm -<%= installDir %>/share/atom/ -<%= installDir %>/share/applications/atom.desktop +<%= installDir %>/bin/<%= appFileName %> +<%= installDir %>/bin/<%= apmFileName %> +<%= installDir %>/share/<%= appFileName %>/ +<%= installDir %>/share/applications/<%= appFileName %>.desktop <%= installDir %>/share/icons/hicolor/ diff --git a/script/clean b/script/clean index 6a7824756..8b5fdf414 100755 --- a/script/clean +++ b/script/clean @@ -20,6 +20,7 @@ var commands = [ [__dirname, '..', 'build', 'node_modules'], [__dirname, '..', 'apm', 'node_modules'], [__dirname, '..', 'atom-shell'], + [__dirname, '..', 'electron'], [home, '.atom', '.node-gyp'], [home, '.atom', 'storage'], [home, '.atom', '.apm'], diff --git a/script/mkdeb b/script/mkdeb index c39b6d649..22d2c5124 100755 --- a/script/mkdeb +++ b/script/mkdeb @@ -1,5 +1,5 @@ #!/bin/bash -# mkdeb version control-file-path deb-file-path +# mkdeb name version channel arch control-file-path desktop-file-path icon-path deb-file-path set -e @@ -7,20 +7,22 @@ SCRIPT=`readlink -f "$0"` ROOT=`readlink -f $(dirname $SCRIPT)/..` cd $ROOT -VERSION="$1" -ARCH="$2" -CONTROL_FILE="$3" -DESKTOP_FILE="$4" -ICON_FILE="$5" -DEB_PATH="$6" +NAME="$1" +VERSION="$2" +CHANNEL="$3" +ARCH="$4" +CONTROL_FILE="$5" +DESKTOP_FILE="$6" +ICON_FILE="$7" +DEB_PATH="$8" FILE_MODE=755 TARGET_ROOT="`mktemp -d`" chmod $FILE_MODE "$TARGET_ROOT" -TARGET="$TARGET_ROOT/atom-$VERSION-$ARCH" +TARGET="$TARGET_ROOT/$NAME-$VERSION-$ARCH" mkdir -m $FILE_MODE -p "$TARGET/usr" -env INSTALL_PREFIX="$TARGET/usr" script/grunt install +env INSTALL_PREFIX="$TARGET/usr" script/grunt install --channel $CHANNEL mkdir -m $FILE_MODE -p "$TARGET/DEBIAN" cp "$CONTROL_FILE" "$TARGET/DEBIAN/control" @@ -29,19 +31,19 @@ mkdir -m $FILE_MODE -p "$TARGET/usr/share/applications" cp "$DESKTOP_FILE" "$TARGET/usr/share/applications" mkdir -m $FILE_MODE -p "$TARGET/usr/share/pixmaps" -cp "$ICON_FILE" "$TARGET/usr/share/pixmaps" +cp "$ICON_FILE" "$TARGET/usr/share/pixmaps/$NAME.png" # Copy generated LICENSE.md to /usr/share/doc/atom/copyright -mkdir -m $FILE_MODE -p "$TARGET/usr/share/doc/atom" -cp "$TARGET/usr/share/atom/resources/LICENSE.md" "$TARGET/usr/share/doc/atom/copyright" +mkdir -m $FILE_MODE -p "$TARGET/usr/share/doc/$NAME" +cp "$TARGET/usr/share/$NAME/resources/LICENSE.md" "$TARGET/usr/share/doc/$NAME/copyright" # Add lintian overrides mkdir -m $FILE_MODE -p "$TARGET/usr/share/lintian/overrides" -cp "$ROOT/resources/linux/debian/lintian-overrides" "$TARGET/usr/share/lintian/overrides/atom" +cp "$ROOT/resources/linux/debian/lintian-overrides" "$TARGET/usr/share/lintian/overrides/$NAME" # Remove executable bit from .node files find "$TARGET" -type f -name "*.node" -exec chmod a-x {} \; fakeroot dpkg-deb -b "$TARGET" -mv "$TARGET_ROOT/atom-$VERSION-$ARCH.deb" "$DEB_PATH" +mv "$TARGET_ROOT/$NAME-$VERSION-$ARCH.deb" "$DEB_PATH" rm -rf "$TARGET_ROOT" diff --git a/script/mkrpm b/script/mkrpm index c9ba2d7f7..0be8cb981 100755 --- a/script/mkrpm +++ b/script/mkrpm @@ -2,22 +2,24 @@ set -e -SPEC_FILE="$1" -DESKTOP_FILE="$2" -BUILD_DIRECTORY="$3" +APP_NAME="$1" +APP_FILE_NAME="$2" +SPEC_FILE="$3" +DESKTOP_FILE="$4" +BUILD_DIRECTORY="$5" RPM_BUILD_ROOT=~/rpmbuild ARCH=`uname -m` rpmdev-setuptree -cp -r $BUILD_DIRECTORY/Atom $RPM_BUILD_ROOT/BUILD -cp -r $BUILD_DIRECTORY/icons $RPM_BUILD_ROOT/BUILD -cp $SPEC_FILE $RPM_BUILD_ROOT/SPECS -cp ./atom.sh $RPM_BUILD_ROOT/BUILD -cp $DESKTOP_FILE $RPM_BUILD_ROOT/BUILD +cp -r "$BUILD_DIRECTORY/$APP_NAME" "$RPM_BUILD_ROOT/BUILD" +cp -r "$BUILD_DIRECTORY/icons" "$RPM_BUILD_ROOT/BUILD" +cp "$SPEC_FILE" "$RPM_BUILD_ROOT/SPECS" +cp ./atom.sh "$RPM_BUILD_ROOT/BUILD" +cp "$DESKTOP_FILE" "$RPM_BUILD_ROOT/BUILD" -rpmbuild -ba $SPEC_FILE -cp $RPM_BUILD_ROOT/RPMS/$ARCH/atom-*.rpm $BUILD_DIRECTORY/rpm +rpmbuild -ba "$SPEC_FILE" +cp $RPM_BUILD_ROOT/RPMS/$ARCH/$APP_FILE_NAME-*.rpm "$BUILD_DIRECTORY/rpm" -rm -rf $RPM_BUILD_ROOT +rm -rf "$RPM_BUILD_ROOT" diff --git a/spec/command-installer-spec.coffee b/spec/command-installer-spec.coffee index b87a08edc..972494ec3 100644 --- a/spec/command-installer-spec.coffee +++ b/spec/command-installer-spec.coffee @@ -1,34 +1,81 @@ path = require 'path' fs = require 'fs-plus' temp = require 'temp' -installer = require '../src/command-installer' +CommandInstaller = require '../src/command-installer' -describe "install(commandPath, callback)", -> - commandFilePath = temp.openSync("atom-command").path - commandName = path.basename(commandFilePath) - installationPath = temp.mkdirSync("atom-bin") - installationFilePath = path.join(installationPath, commandName) +describe "CommandInstaller on #darwin", -> + [installer, resourcesPath, installationPath, atomBinPath, apmBinPath] = [] beforeEach -> - fs.chmodSync(commandFilePath, '755') - spyOn(installer, 'getInstallDirectory').andReturn installationPath + installationPath = temp.mkdirSync("atom-bin") - describe "on #darwin", -> - it "symlinks the command and makes it executable", -> - expect(fs.isFileSync(commandFilePath)).toBeTruthy() - expect(fs.isFileSync(installationFilePath)).toBeFalsy() + resourcesPath = temp.mkdirSync('atom-app') + atomBinPath = path.join(resourcesPath, 'app', 'atom.sh') + apmBinPath = path.join(resourcesPath, 'app', 'apm', 'node_modules', '.bin', 'apm') + fs.writeFileSync(atomBinPath, "") + fs.writeFileSync(apmBinPath, "") + fs.chmodSync(atomBinPath, '755') + fs.chmodSync(apmBinPath, '755') - installDone = false - installError = null - installer.createSymlink commandFilePath, false, (error) -> - installDone = true - installError = error + spyOn(CommandInstaller::, 'getResourcesDirectory').andReturn(resourcesPath) + spyOn(CommandInstaller::, 'getInstallDirectory').andReturn(installationPath) - waitsFor -> - installDone + describe "when using a stable version of atom", -> + beforeEach -> + installer = new CommandInstaller("2.0.2") + + it "symlinks the atom command as 'atom'", -> + installedAtomPath = path.join(installationPath, 'atom') + + expect(fs.isFileSync(installedAtomPath)).toBeFalsy() + + waitsFor (done) -> + installer.installAtomCommand(false, done) runs -> - expect(installError).toBeNull() - expect(fs.isFileSync(installationFilePath)).toBeTruthy() - expect(fs.realpathSync(installationFilePath)).toBe fs.realpathSync(commandFilePath) - expect(fs.isExecutableSync(installationFilePath)).toBeTruthy() + expect(fs.realpathSync(installedAtomPath)).toBe fs.realpathSync(atomBinPath) + expect(fs.isExecutableSync(installedAtomPath)).toBe true + expect(fs.isFileSync(path.join(installationPath, 'atom-beta'))).toBe false + + it "symlinks the apm command as 'apm'", -> + installedApmPath = path.join(installationPath, 'apm') + + expect(fs.isFileSync(installedApmPath)).toBeFalsy() + + waitsFor (done) -> + installer.installApmCommand(false, done) + + runs -> + expect(fs.realpathSync(installedApmPath)).toBe fs.realpathSync(apmBinPath) + expect(fs.isExecutableSync(installedApmPath)).toBeTruthy() + expect(fs.isFileSync(path.join(installationPath, 'apm-beta'))).toBe false + + describe "when using a beta version of atom", -> + beforeEach -> + installer = new CommandInstaller("2.2.0-beta.0") + + it "symlinks the atom command as 'atom-beta'", -> + installedAtomPath = path.join(installationPath, 'atom-beta') + + expect(fs.isFileSync(installedAtomPath)).toBeFalsy() + + waitsFor (done) -> + installer.installAtomCommand(false, done) + + runs -> + expect(fs.realpathSync(installedAtomPath)).toBe fs.realpathSync(atomBinPath) + expect(fs.isExecutableSync(installedAtomPath)).toBe true + expect(fs.isFileSync(path.join(installationPath, 'atom'))).toBe false + + it "symlinks the apm command as 'apm-beta'", -> + installedApmPath = path.join(installationPath, 'apm-beta') + + expect(fs.isFileSync(installedApmPath)).toBeFalsy() + + waitsFor (done) -> + installer.installApmCommand(false, done) + + runs -> + expect(fs.realpathSync(installedApmPath)).toBe fs.realpathSync(apmBinPath) + expect(fs.isExecutableSync(installedApmPath)).toBeTruthy() + expect(fs.isFileSync(path.join(installationPath, 'apm'))).toBe false diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index 23541bfb5..411135f5d 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -2,7 +2,6 @@ path = require 'path' temp = require 'temp' CSON = require 'season' fs = require 'fs-plus' -Grim = require 'grim' describe "Config", -> dotAtomPath = null @@ -364,16 +363,6 @@ describe "Config", -> expect(atom.config.save).not.toHaveBeenCalled() expect(atom.config.get('foo.bar.baz', scope: ['.source.coffee'])).toBe 55 - it "deprecates passing a scope selector as the first argument", -> - atom.config.setDefaults("foo", bar: baz: 10) - atom.config.set('foo.bar.baz', 55, scopeSelector: '.source.coffee') - - spyOn(Grim, 'deprecate') - atom.config.unset('.source.coffee', 'foo.bar.baz') - expect(Grim.deprecate).toHaveBeenCalled() - - expect(atom.config.get('foo.bar.baz', scope: ['.source.coffee'])).toBe 10 - describe ".onDidChange(keyPath, {scope})", -> [observeHandler, observeSubscription] = [] @@ -458,15 +447,6 @@ describe "Config", -> expect(changeSpy).toHaveBeenCalledWith({oldValue: 12, newValue: undefined}) changeSpy.reset() - it 'deprecates using a scope descriptor as an optional first argument', -> - keyPath = "foo.bar.baz" - spyOn(Grim, 'deprecate') - atom.config.onDidChange [".source.coffee", ".string.quoted.double.coffee"], keyPath, changeSpy = jasmine.createSpy() - expect(Grim.deprecate).toHaveBeenCalled() - - atom.config.set("foo.bar.baz", 12) - expect(changeSpy).toHaveBeenCalledWith({oldValue: undefined, newValue: 12}) - describe ".observe(keyPath, {scope})", -> [observeHandler, observeSubscription] = [] @@ -535,16 +515,6 @@ describe "Config", -> expect(observeHandler).toHaveBeenCalledWith("value 2") expect(otherHandler).not.toHaveBeenCalledWith("value 2") - it "deprecates using a scope descriptor as the first argument", -> - spyOn(Grim, 'deprecate') - atom.config.observe([".some.scope"], "foo.bar.baz", observeHandler) - atom.config.observe([".another.scope"], "foo.bar.baz", otherHandler) - expect(Grim.deprecate).toHaveBeenCalled() - - atom.config.set('foo.bar.baz', "value 2", scopeSelector: ".some") - expect(observeHandler).toHaveBeenCalledWith("value 2") - expect(otherHandler).not.toHaveBeenCalledWith("value 2") - it 'calls the callback when properties with more specific selectors are removed', -> changeSpy = jasmine.createSpy() atom.config.observe("foo.bar.baz", scope: [".source.coffee", ".string.quoted.double.coffee"], changeSpy) @@ -1629,135 +1599,3 @@ describe "Config", -> expect(atom.config.set('foo.bar.arr', ['two', 'three'])).toBe true expect(atom.config.get('foo.bar.arr')).toEqual ['two', 'three'] - - describe "Deprecated Methods", -> - describe ".getDefault(keyPath)", -> - it "returns a clone of the default value", -> - atom.config.setDefaults("foo", same: 1, changes: 1) - - spyOn(Grim, 'deprecate') - expect(atom.config.getDefault('foo.same')).toBe 1 - expect(atom.config.getDefault('foo.changes')).toBe 1 - expect(Grim.deprecate.callCount).toBe 2 - - atom.config.set('foo.same', 2) - atom.config.set('foo.changes', 3) - - expect(atom.config.getDefault('foo.same')).toBe 1 - expect(atom.config.getDefault('foo.changes')).toBe 1 - expect(Grim.deprecate.callCount).toBe 4 - - initialDefaultValue = [1, 2, 3] - atom.config.setDefaults("foo", bar: initialDefaultValue) - expect(atom.config.getDefault('foo.bar')).toEqual initialDefaultValue - expect(atom.config.getDefault('foo.bar')).not.toBe initialDefaultValue - expect(Grim.deprecate.callCount).toBe 6 - - describe "when scoped settings are used", -> - it "returns the global default when no scoped default set", -> - atom.config.setDefaults("foo", bar: baz: 10) - - spyOn(Grim, 'deprecate') - expect(atom.config.getDefault('.source.coffee', 'foo.bar.baz')).toBe 10 - expect(Grim.deprecate).toHaveBeenCalled() - - it "returns the scoped settings not including the user's config file", -> - atom.config.setDefaults("foo", bar: baz: 10) - atom.config.set("foo.bar.baz", 42, scopeSelector: ".source.coffee", source: "some-source") - - spyOn(Grim, 'deprecate') - expect(atom.config.getDefault('.source.coffee', 'foo.bar.baz')).toBe 42 - expect(Grim.deprecate.callCount).toBe 1 - - atom.config.set('foo.bar.baz', 55, scopeSelector: '.source.coffee') - expect(atom.config.getDefault('.source.coffee', 'foo.bar.baz')).toBe 42 - expect(Grim.deprecate.callCount).toBe 2 - - describe ".isDefault(keyPath)", -> - it "returns true when the value of the key path is its default value", -> - atom.config.setDefaults("foo", same: 1, changes: 1) - - spyOn(Grim, 'deprecate') - expect(atom.config.isDefault('foo.same')).toBe true - expect(atom.config.isDefault('foo.changes')).toBe true - expect(Grim.deprecate.callCount).toBe 2 - - atom.config.set('foo.same', 2) - atom.config.set('foo.changes', 3) - - expect(atom.config.isDefault('foo.same')).toBe false - expect(atom.config.isDefault('foo.changes')).toBe false - expect(Grim.deprecate.callCount).toBe 4 - - describe "when scoped settings are used", -> - it "returns false when a scoped setting was set by the user", -> - spyOn(Grim, 'deprecate') - expect(atom.config.isDefault('.source.coffee', 'foo.bar.baz')).toBe true - expect(Grim.deprecate.callCount).toBe 1 - - atom.config.set("foo.bar.baz", 42, scopeSelector: ".source.coffee", source: "something-else") - expect(atom.config.isDefault('.source.coffee', 'foo.bar.baz')).toBe true - expect(Grim.deprecate.callCount).toBe 2 - - atom.config.set('foo.bar.baz', 55, scopeSelector: '.source.coffee') - expect(atom.config.isDefault('.source.coffee', 'foo.bar.baz')).toBe false - expect(Grim.deprecate.callCount).toBe 3 - - describe ".toggle(keyPath)", -> - beforeEach -> - jasmine.snapshotDeprecations() - - afterEach -> - jasmine.restoreDeprecationsSnapshot() - - it "negates the boolean value of the current key path value", -> - atom.config.set('foo.a', 1) - atom.config.toggle('foo.a') - expect(atom.config.get('foo.a')).toBe false - - atom.config.set('foo.a', '') - atom.config.toggle('foo.a') - expect(atom.config.get('foo.a')).toBe true - - atom.config.set('foo.a', null) - atom.config.toggle('foo.a') - expect(atom.config.get('foo.a')).toBe true - - atom.config.set('foo.a', true) - atom.config.toggle('foo.a') - expect(atom.config.get('foo.a')).toBe false - - describe ".getSettings()", -> - it "returns all settings including defaults", -> - atom.config.setDefaults("foo", bar: baz: 10) - atom.config.set("foo.ok", 12) - - jasmine.snapshotDeprecations() - expect(atom.config.getSettings().foo).toEqual - ok: 12 - bar: - baz: 10 - jasmine.restoreDeprecationsSnapshot() - - describe ".getPositiveInt(keyPath, defaultValue)", -> - beforeEach -> - jasmine.snapshotDeprecations() - - afterEach -> - jasmine.restoreDeprecationsSnapshot() - - it "returns the proper coerced value", -> - atom.config.set('editor.preferredLineLength', 0) - expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 1 - - it "returns the proper coerced value", -> - atom.config.set('editor.preferredLineLength', -1234) - expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 1 - - it "returns the default value when a string is passed in", -> - atom.config.set('editor.preferredLineLength', 'abcd') - expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80 - - it "returns the default value when null is passed in", -> - atom.config.set('editor.preferredLineLength', null) - expect(atom.config.getPositiveInt('editor.preferredLineLength', 80)).toBe 80 diff --git a/spec/context-menu-manager-spec.coffee b/spec/context-menu-manager-spec.coffee index 95835d254..4cf33acc6 100644 --- a/spec/context-menu-manager-spec.coffee +++ b/spec/context-menu-manager-spec.coffee @@ -156,32 +156,3 @@ describe "ContextMenuManager", -> catch error addError = error expect(addError.message).toContain('<>') - - describe "when the menus are specified in a legacy format", -> - beforeEach -> - jasmine.snapshotDeprecations() - - afterEach -> - jasmine.restoreDeprecationsSnapshot() - - it "allows items to be specified in the legacy format for now", -> - contextMenu.add '.parent': - 'A': 'a' - 'Separator 1': '-' - 'B': - 'C': 'c' - 'Separator 2': '-' - 'D': 'd' - - expect(contextMenu.templateForElement(parent)).toEqual [ - {label: 'A', command: 'a'} - {type: 'separator'} - { - label: 'B' - submenu: [ - {label: 'C', command: 'c'} - {type: 'separator'} - {label: 'D', command: 'd'} - ] - } - ] diff --git a/spec/display-buffer-spec.coffee b/spec/display-buffer-spec.coffee index c7acbfa26..7ed97f0d0 100644 --- a/spec/display-buffer-spec.coffee +++ b/spec/display-buffer-spec.coffee @@ -47,14 +47,6 @@ describe "DisplayBuffer", -> buffer.insert([0, 0], oneHundredLines) expect(displayBuffer.getLineCount()).toBe 100 + originalLineCount - it "reassigns the scrollTop if it exceeds the max possible value after lines are removed", -> - displayBuffer.setHeight(50) - displayBuffer.setLineHeightInPixels(10) - displayBuffer.setScrollTop(80) - - buffer.delete([[8, 0], [10, 0]]) - expect(displayBuffer.getScrollTop()).toBe 60 - it "updates the display buffer prior to invoking change handlers registered on the buffer", -> buffer.onDidChange -> expect(displayBuffer2.tokenizedLineForScreenRow(0).text).toBe "testing" displayBuffer2 = new DisplayBuffer({buffer, tabLength}) @@ -240,7 +232,8 @@ describe "DisplayBuffer", -> describe "when a newline is inserted, deleted, and re-inserted at the end of a wrapped line (regression)", -> it "correctly renders the original wrapped line", -> buffer = atom.project.buildBufferSync(null, '') - displayBuffer = new DisplayBuffer({buffer, tabLength, editorWidthInChars: 30, softWrapped: true}) + displayBuffer = new DisplayBuffer({buffer, tabLength, editorWidthInChars: 30}) + displayBuffer.setSoftWrapped(true) buffer.insert([0, 0], "the quick brown fox jumps over the lazy dog.") buffer.insert([0, Infinity], '\n') @@ -296,18 +289,6 @@ describe "DisplayBuffer", -> displayBuffer.setEditorWidthInChars(-1) expect(displayBuffer.editorWidthInChars).not.toBe -1 - it "sets ::scrollLeft to 0 and keeps it there when soft wrapping is enabled", -> - displayBuffer.setDefaultCharWidth(10) - displayBuffer.setWidth(85) - - displayBuffer.setSoftWrapped(false) - displayBuffer.setScrollLeft(Infinity) - expect(displayBuffer.getScrollLeft()).toBeGreaterThan 0 - displayBuffer.setSoftWrapped(true) - expect(displayBuffer.getScrollLeft()).toBe 0 - displayBuffer.setScrollLeft(10) - expect(displayBuffer.getScrollLeft()).toBe 0 - describe "primitive folding", -> beforeEach -> displayBuffer.destroy() @@ -737,21 +718,6 @@ describe "DisplayBuffer", -> expect(displayBuffer.clipScreenPosition([0, 1], clip: 'forward')).toEqual [0, tabLength] expect(displayBuffer.clipScreenPosition([0, tabLength], clip: 'forward')).toEqual [0, tabLength] - describe "::screenPositionForPixelPosition(pixelPosition)", -> - it "clips pixel positions above buffer start", -> - displayBuffer.setLineHeightInPixels(20) - expect(displayBuffer.screenPositionForPixelPosition(top: -Infinity, left: -Infinity)).toEqual [0, 0] - expect(displayBuffer.screenPositionForPixelPosition(top: -Infinity, left: Infinity)).toEqual [0, 0] - expect(displayBuffer.screenPositionForPixelPosition(top: -1, left: Infinity)).toEqual [0, 0] - expect(displayBuffer.screenPositionForPixelPosition(top: 0, left: Infinity)).toEqual [0, 29] - - it "clips pixel positions below buffer end", -> - displayBuffer.setLineHeightInPixels(20) - expect(displayBuffer.screenPositionForPixelPosition(top: Infinity, left: -Infinity)).toEqual [12, 2] - expect(displayBuffer.screenPositionForPixelPosition(top: Infinity, left: Infinity)).toEqual [12, 2] - expect(displayBuffer.screenPositionForPixelPosition(top: displayBuffer.getHeight() + 1, left: 0)).toEqual [12, 2] - expect(displayBuffer.screenPositionForPixelPosition(top: displayBuffer.getHeight() - 1, left: 0)).toEqual [12, 0] - describe "::screenPositionForBufferPosition(bufferPosition, options)", -> it "clips the specified buffer position", -> expect(displayBuffer.screenPositionForBufferPosition([0, 2])).toEqual [0, 2] @@ -1185,20 +1151,6 @@ describe "DisplayBuffer", -> expect(marker1.getProperties()).toEqual a: 1, b: 2 expect(marker2.getProperties()).toEqual a: 1, b: 3 - describe "Marker::getPixelRange()", -> - it "returns the start and end positions of the marker based on the line height and character widths assigned to the DisplayBuffer", -> - marker = displayBuffer.markScreenRange([[5, 10], [6, 4]]) - - displayBuffer.setLineHeightInPixels(20) - displayBuffer.setDefaultCharWidth(10) - - for char in ['r', 'e', 't', 'u', 'r', 'n'] - displayBuffer.setScopedCharWidth(["source.js", "keyword.control.js"], char, 11) - - {start, end} = marker.getPixelRange() - expect(start.top).toBe 5 * 20 - expect(start.left).toBe (4 * 10) + (6 * 11) - describe 'when there are multiple DisplayBuffers for a buffer', -> describe 'when a marker is created', -> it 'the second display buffer will not emit a marker-created event when the marker has been deleted in the first marker-created event', -> @@ -1253,157 +1205,18 @@ describe "DisplayBuffer", -> expect(displayBuffer.getDecorations(class: 'two').length).toEqual 0 expect(displayBuffer.getDecorations(class: 'one').length).toEqual 1 - describe "::setScrollTop", -> - beforeEach -> - displayBuffer.setLineHeightInPixels(10) - - it "disallows negative values", -> - displayBuffer.setHeight(displayBuffer.getScrollHeight() + 100) - expect(displayBuffer.setScrollTop(-10)).toBe 0 - expect(displayBuffer.getScrollTop()).toBe 0 - - it "disallows values that would make ::getScrollBottom() exceed ::getScrollHeight()", -> - displayBuffer.setHeight(50) - maxScrollTop = displayBuffer.getScrollHeight() - displayBuffer.getHeight() - - expect(displayBuffer.setScrollTop(maxScrollTop)).toBe maxScrollTop - expect(displayBuffer.getScrollTop()).toBe maxScrollTop - - expect(displayBuffer.setScrollTop(maxScrollTop + 50)).toBe maxScrollTop - expect(displayBuffer.getScrollTop()).toBe maxScrollTop - - describe "editor.scrollPastEnd", -> - describe "when editor.scrollPastEnd is false", -> - beforeEach -> - atom.config.set("editor.scrollPastEnd", false) - displayBuffer.setLineHeightInPixels(10) - - it "does not add the height of the view to the scroll height", -> - lineHeight = displayBuffer.getLineHeightInPixels() - originalScrollHeight = displayBuffer.getScrollHeight() - displayBuffer.setHeight(50) - - expect(displayBuffer.getScrollHeight()).toBe originalScrollHeight - - describe "when editor.scrollPastEnd is true", -> - beforeEach -> - atom.config.set("editor.scrollPastEnd", true) - displayBuffer.setLineHeightInPixels(10) - - it "adds the height of the view to the scroll height", -> - lineHeight = displayBuffer.getLineHeightInPixels() - originalScrollHeight = displayBuffer.getScrollHeight() - displayBuffer.setHeight(50) - - expect(displayBuffer.getScrollHeight()).toEqual(originalScrollHeight + displayBuffer.height - (lineHeight * 3)) - - describe "::setScrollLeft", -> - beforeEach -> - displayBuffer.setLineHeightInPixels(10) - displayBuffer.setDefaultCharWidth(10) - - it "disallows negative values", -> - displayBuffer.setWidth(displayBuffer.getScrollWidth() + 100) - expect(displayBuffer.setScrollLeft(-10)).toBe 0 - expect(displayBuffer.getScrollLeft()).toBe 0 - - it "disallows values that would make ::getScrollRight() exceed ::getScrollWidth()", -> - displayBuffer.setWidth(50) - maxScrollLeft = displayBuffer.getScrollWidth() - displayBuffer.getWidth() - - expect(displayBuffer.setScrollLeft(maxScrollLeft)).toBe maxScrollLeft - expect(displayBuffer.getScrollLeft()).toBe maxScrollLeft - - expect(displayBuffer.setScrollLeft(maxScrollLeft + 50)).toBe maxScrollLeft - expect(displayBuffer.getScrollLeft()).toBe maxScrollLeft - describe "::scrollToScreenPosition(position, [options])", -> - beforeEach -> - displayBuffer.setLineHeightInPixels(10) - displayBuffer.setDefaultCharWidth(10) - displayBuffer.setHorizontalScrollbarHeight(0) - displayBuffer.setHeight(50) - displayBuffer.setWidth(150) - - it "sets the scroll top and scroll left so the given screen position is in view", -> - displayBuffer.scrollToScreenPosition([8, 20]) - expect(displayBuffer.getScrollBottom()).toBe (9 + displayBuffer.getVerticalScrollMargin()) * 10 - expect(displayBuffer.getScrollRight()).toBe (20 + displayBuffer.getHorizontalScrollMargin()) * 10 + it "triggers ::onDidRequestAutoscroll with the logical coordinates along with the options", -> + scrollSpy = jasmine.createSpy("::onDidRequestAutoscroll") + displayBuffer.onDidRequestAutoscroll(scrollSpy) displayBuffer.scrollToScreenPosition([8, 20]) - expect(displayBuffer.getScrollBottom()).toBe (9 + displayBuffer.getVerticalScrollMargin()) * 10 - expect(displayBuffer.getScrollRight()).toBe (20 + displayBuffer.getHorizontalScrollMargin()) * 10 + displayBuffer.scrollToScreenPosition([8, 20], center: true) + displayBuffer.scrollToScreenPosition([8, 20], center: false, reversed: true) - describe "when the 'center' option is true", -> - it "vertically scrolls to center the given position vertically", -> - displayBuffer.scrollToScreenPosition([8, 20], center: true) - expect(displayBuffer.getScrollTop()).toBe (8 * 10) + 5 - (50 / 2) - expect(displayBuffer.getScrollRight()).toBe (20 + displayBuffer.getHorizontalScrollMargin()) * 10 - - it "does not scroll vertically if the position is already in view", -> - displayBuffer.scrollToScreenPosition([4, 20], center: true) - expect(displayBuffer.getScrollTop()).toBe 0 - - describe "scroll width", -> - cursorWidth = 1 - beforeEach -> - displayBuffer.setDefaultCharWidth(10) - - it "recomputes the scroll width when the default character width changes", -> - expect(displayBuffer.getScrollWidth()).toBe 10 * 65 + cursorWidth - - displayBuffer.setDefaultCharWidth(12) - expect(displayBuffer.getScrollWidth()).toBe 12 * 65 + cursorWidth - - it "recomputes the scroll width when the max line length changes", -> - buffer.insert([6, 12], ' ') - expect(displayBuffer.getScrollWidth()).toBe 10 * 66 + cursorWidth - - buffer.delete([[6, 10], [6, 12]], ' ') - expect(displayBuffer.getScrollWidth()).toBe 10 * 64 + cursorWidth - - it "recomputes the scroll width when the scoped character widths change", -> - operatorWidth = 20 - displayBuffer.setScopedCharWidth(['source.js', 'keyword.operator.js'], '<', operatorWidth) - expect(displayBuffer.getScrollWidth()).toBe 10 * 64 + operatorWidth + cursorWidth - - it "recomputes the scroll width when the scoped character widths change in a batch", -> - operatorWidth = 20 - - displayBuffer.onDidChangeCharacterWidths changedSpy = jasmine.createSpy() - - displayBuffer.batchCharacterMeasurement -> - displayBuffer.setScopedCharWidth(['source.js', 'keyword.operator.js'], '<', operatorWidth) - displayBuffer.setScopedCharWidth(['source.js', 'keyword.operator.js'], '?', operatorWidth) - - expect(displayBuffer.getScrollWidth()).toBe 10 * 63 + operatorWidth * 2 + cursorWidth - expect(changedSpy.callCount).toBe 1 - - describe "::getVisibleRowRange()", -> - beforeEach -> - displayBuffer.setLineHeightInPixels(10) - displayBuffer.setHeight(100) - - it "returns the first and the last visible rows", -> - displayBuffer.setScrollTop(0) - - expect(displayBuffer.getVisibleRowRange()).toEqual [0, 9] - - it "includes partially visible rows in the range", -> - displayBuffer.setScrollTop(5) - - expect(displayBuffer.getVisibleRowRange()).toEqual [0, 10] - - it "returns an empty range when lineHeight is 0", -> - displayBuffer.setLineHeightInPixels(0) - - expect(displayBuffer.getVisibleRowRange()).toEqual [0, 0] - - it "ends at last buffer row even if there's more space available", -> - displayBuffer.setHeight(150) - displayBuffer.setScrollTop(60) - - expect(displayBuffer.getVisibleRowRange()).toEqual [0, 13] + expect(scrollSpy).toHaveBeenCalledWith(screenRange: [[8, 20], [8, 20]], options: {}) + expect(scrollSpy).toHaveBeenCalledWith(screenRange: [[8, 20], [8, 20]], options: {center: true}) + expect(scrollSpy).toHaveBeenCalledWith(screenRange: [[8, 20], [8, 20]], options: {center: false, reversed: true}) describe "::decorateMarker", -> describe "when decorating gutters", -> diff --git a/spec/fixtures/task-handler-with-deprecations.coffee b/spec/fixtures/task-handler-with-deprecations.coffee index 6ba8e86e8..9a9b1295a 100644 --- a/spec/fixtures/task-handler-with-deprecations.coffee +++ b/spec/fixtures/task-handler-with-deprecations.coffee @@ -1,3 +1,4 @@ -{Git} = require 'atom' +{deprecate} = require 'grim' +deprecate('Fake task deprecation') module.exports = -> diff --git a/spec/git-spec.coffee b/spec/git-spec.coffee index b4e049db8..a72cd47cd 100644 --- a/spec/git-spec.coffee +++ b/spec/git-spec.coffee @@ -3,6 +3,7 @@ GitRepository = require '../src/git-repository' fs = require 'fs-plus' path = require 'path' Task = require '../src/task' +Project = require '../src/project' copyRepository = -> workingDirPath = temp.mkdirSync('atom-working-dir') @@ -276,7 +277,7 @@ describe "GitRepository", -> atom.workspace.open('file.txt') runs -> - project2 = atom.project.testSerialization() + project2 = Project.deserialize(atom.project.serialize()) buffer = project2.getBuffers()[0] waitsFor -> diff --git a/spec/menu-manager-spec.coffee b/spec/menu-manager-spec.coffee index f86b6e1a0..c047bbcfc 100644 --- a/spec/menu-manager-spec.coffee +++ b/spec/menu-manager-spec.coffee @@ -52,7 +52,7 @@ describe "MenuManager", -> it "sends the current menu template and associated key bindings to the browser process", -> spyOn(menu, 'sendToBrowserProcess') menu.add [{label: "A", submenu: [{label: "B", command: "b"}]}] - atom.keymap.add 'test', 'atom-workspace': 'ctrl-b': 'b' + atom.keymaps.add 'test', 'atom-workspace': 'ctrl-b': 'b' menu.update() waits 1 @@ -64,8 +64,8 @@ describe "MenuManager", -> # more dynamic interaction between the currently focused element and the menu spyOn(menu, 'sendToBrowserProcess') menu.add [{label: "A", submenu: [{label: "B", command: "b"}]}] - atom.keymap.add 'test', 'atom-workspace': 'ctrl-b': 'b' - atom.keymap.add 'test', 'atom-text-editor': 'ctrl-b': 'unset!' + atom.keymaps.add 'test', 'atom-workspace': 'ctrl-b': 'b' + atom.keymaps.add 'test', 'atom-text-editor': 'ctrl-b': 'unset!' waits 1 diff --git a/spec/package-manager-spec.coffee b/spec/package-manager-spec.coffee index 7b714ba6d..d1afd6ade 100644 --- a/spec/package-manager-spec.coffee +++ b/spec/package-manager-spec.coffee @@ -62,17 +62,7 @@ describe "PackageManager", -> expect(console.warn.argsForCall[0][0]).toContain("Could not resolve") describe "when the package is deprecated", -> - grim = require 'grim' - includeDeprecatedAPIs = null - - beforeEach -> - {includeDeprecatedAPIs} = grim - - afterEach -> - grim.includeDeprecatedAPIs = includeDeprecatedAPIs - it "returns null", -> - grim.includeDeprecatedAPIs = false expect(atom.packages.loadPackage(path.join(__dirname, 'fixtures', 'packages', 'wordcount'))).toBeNull() expect(atom.packages.isDeprecatedPackage('wordcount', '2.1.9')).toBe true expect(atom.packages.isDeprecatedPackage('wordcount', '2.2.0')).toBe true @@ -174,24 +164,6 @@ describe "PackageManager", -> expect(atom.config.set('package-with-config-schema.numbers.one', '10')).toBe true expect(atom.config.get('package-with-config-schema.numbers.one')).toBe 10 - describe "when a package has configDefaults", -> - beforeEach -> - jasmine.snapshotDeprecations() - - afterEach -> - jasmine.restoreDeprecationsSnapshot() - - it "still assigns configDefaults from the module though deprecated", -> - - expect(atom.config.get('package-with-config-defaults.numbers.one')).toBeUndefined() - - waitsForPromise -> - atom.packages.activatePackage('package-with-config-defaults') - - runs -> - expect(atom.config.get('package-with-config-defaults.numbers.one')).toBe 1 - expect(atom.config.get('package-with-config-defaults.numbers.two')).toBe 2 - describe "when the package metadata includes `activationCommands`", -> [mainModule, promise, workspaceCommandListener, registration] = [] @@ -914,66 +886,3 @@ describe "PackageManager", -> expect(atom.config.get('core.themes')).not.toContain packageName expect(atom.config.get('core.themes')).not.toContain packageName expect(atom.config.get('core.disabledPackages')).not.toContain packageName - - describe "deleting non-bundled autocomplete packages", -> - [autocompleteCSSPath, autocompletePlusPath] = [] - fs = require 'fs-plus' - path = require 'path' - - beforeEach -> - fixturePath = path.resolve(__dirname, './fixtures/packages') - autocompleteCSSPath = path.join(fixturePath, 'autocomplete-css') - autocompletePlusPath = path.join(fixturePath, 'autocomplete-plus') - - try - fs.mkdirSync(autocompleteCSSPath) - fs.writeFileSync(path.join(autocompleteCSSPath, 'package.json'), '{}') - fs.symlinkSync(path.join(fixturePath, 'package-with-main'), autocompletePlusPath, 'dir') - - expect(fs.isDirectorySync(autocompleteCSSPath)).toBe true - expect(fs.isSymbolicLinkSync(autocompletePlusPath)).toBe true - - jasmine.unspy(atom.packages, 'uninstallAutocompletePlus') - - afterEach -> - try - fs.unlink autocompletePlusPath, -> - - it "removes the packages", -> - atom.packages.loadPackages() - - waitsFor -> - not fs.isDirectorySync(autocompleteCSSPath) - - runs -> - expect(fs.isDirectorySync(autocompleteCSSPath)).toBe false - expect(fs.isSymbolicLinkSync(autocompletePlusPath)).toBe true - - describe "when the deprecated sublime-tabs package is installed", -> - grim = require 'grim' - includeDeprecatedAPIs = null - - beforeEach -> - {includeDeprecatedAPIs} = grim - grim.includeDeprecatedAPIs = false - - afterEach -> - grim.includeDeprecatedAPIs = includeDeprecatedAPIs - - it "enables the tree-view and tabs package", -> - atom.config.pushAtKeyPath('core.disabledPackages', 'tree-view') - atom.config.pushAtKeyPath('core.disabledPackages', 'tabs') - - spyOn(atom.packages, 'getAvailablePackagePaths').andReturn [ - path.join(__dirname, 'fixtures', 'packages', 'sublime-tabs') - path.resolve(__dirname, '..', 'node_modules', 'tree-view') - path.resolve(__dirname, '..', 'node_modules', 'tabs') - ] - atom.packages.loadPackages() - - waitsFor -> - not atom.packages.isPackageDisabled('tree-view') and not atom.packages.isPackageDisabled('tabs') - - runs -> - expect(atom.packages.isPackageLoaded('tree-view')).toBe true - expect(atom.packages.isPackageLoaded('tabs')).toBe true diff --git a/spec/pane-container-spec.coffee b/spec/pane-container-spec.coffee index b32868d8e..58e159279 100644 --- a/spec/pane-container-spec.coffee +++ b/spec/pane-container-spec.coffee @@ -21,7 +21,7 @@ describe "PaneContainer", -> it "preserves the focused pane across serialization", -> expect(pane3A.focused).toBe true - containerB = containerA.testSerialization() + containerB = PaneContainer.deserialize(containerA.serialize()) [pane1B, pane2B, pane3B] = containerB.getPanes() expect(pane3B.focused).toBe true @@ -29,7 +29,7 @@ describe "PaneContainer", -> pane3A.activate() expect(containerA.getActivePane()).toBe pane3A - containerB = containerA.testSerialization() + containerB = PaneContainer.deserialize(containerA.serialize()) [pane1B, pane2B, pane3B] = containerB.getPanes() expect(containerB.getActivePane()).toBe pane3B diff --git a/spec/pane-spec.coffee b/spec/pane-spec.coffee index 13ae25386..851ffe71d 100644 --- a/spec/pane-spec.coffee +++ b/spec/pane-spec.coffee @@ -1,4 +1,4 @@ -{Model} = require 'theorist' +{Emitter} = require 'event-kit' Pane = require '../src/pane' PaneAxis = require '../src/pane-axis' PaneContainer = require '../src/pane-container' @@ -6,13 +6,17 @@ PaneContainer = require '../src/pane-container' describe "Pane", -> deserializerDisposable = null - class Item extends Model + class Item @deserialize: ({name, uri}) -> new this(name, uri) - constructor: (@name, @uri) -> + constructor: (@name, @uri) -> @emitter = new Emitter + destroyed: false getURI: -> @uri getPath: -> @path serialize: -> {deserializer: 'Item', @name, @uri} isEqual: (other) -> @name is other?.name + onDidDestroy: (fn) -> @emitter.on('did-destroy', fn) + destroy: -> @destroyed = true; @emitter.emit('did-destroy') + isDestroyed: -> @destroyed beforeEach -> deserializerDisposable = atom.deserializers.add(Item) @@ -728,12 +732,12 @@ describe "Pane", -> pane = new Pane(params) it "can serialize and deserialize the pane and all its items", -> - newPane = pane.testSerialization() + newPane = Pane.deserialize(pane.serialize()) expect(newPane.getItems()).toEqual pane.getItems() it "restores the active item on deserialization", -> pane.activateItemAtIndex(1) - newPane = pane.testSerialization() + newPane = Pane.deserialize(pane.serialize()) expect(newPane.getActiveItem()).toEqual newPane.itemAtIndex(1) it "does not include items that cannot be deserialized", -> @@ -741,11 +745,11 @@ describe "Pane", -> unserializable = {} pane.activateItem(unserializable) - newPane = pane.testSerialization() + newPane = Pane.deserialize(pane.serialize()) expect(newPane.getActiveItem()).toEqual pane.itemAtIndex(0) expect(newPane.getItems().length).toBe pane.getItems().length - 1 it "includes the pane's focus state in the serialized state", -> pane.focus() - newPane = pane.testSerialization() + newPane = Pane.deserialize(pane.serialize()) expect(newPane.focused).toBe true diff --git a/spec/project-spec.coffee b/spec/project-spec.coffee index 4caadeb62..13dd3e70c 100644 --- a/spec/project-spec.coffee +++ b/spec/project-spec.coffee @@ -66,7 +66,7 @@ describe "Project", -> runs -> expect(atom.project.getBuffers().length).toBe 1 - deserializedProject = atom.project.testSerialization() + deserializedProject = Project.deserialize(atom.project.serialize()) expect(deserializedProject.getBuffers().length).toBe 0 it "listens for destroyed events on deserialized buffers and removes them when they are destroyed", -> @@ -75,7 +75,7 @@ describe "Project", -> runs -> expect(atom.project.getBuffers().length).toBe 1 - deserializedProject = atom.project.testSerialization() + deserializedProject = Project.deserialize(atom.project.serialize()) expect(deserializedProject.getBuffers().length).toBe 1 deserializedProject.getBuffers()[0].destroy() @@ -91,7 +91,7 @@ describe "Project", -> runs -> expect(atom.project.getBuffers().length).toBe 1 fs.mkdirSync(pathToOpen) - deserializedProject = atom.project.testSerialization() + deserializedProject = Project.deserialize(atom.project.serialize()) expect(deserializedProject.getBuffers().length).toBe 0 it "does not deserialize buffers when their path is inaccessible", -> @@ -104,7 +104,7 @@ describe "Project", -> runs -> expect(atom.project.getBuffers().length).toBe 1 fs.chmodSync(pathToOpen, '000') - deserializedProject = atom.project.testSerialization() + deserializedProject = Project.deserialize(atom.project.serialize()) expect(deserializedProject.getBuffers().length).toBe 0 describe "when an editor is saved and the project has no path", -> @@ -508,36 +508,3 @@ describe "Project", -> randomPath = path.join("some", "random", "path") expect(atom.project.contains(randomPath)).toBe false - - describe ".eachBuffer(callback)", -> - beforeEach -> - jasmine.snapshotDeprecations() - atom.project.bufferForPathSync('a') - - afterEach -> - jasmine.restoreDeprecationsSnapshot() - - it "invokes the callback for existing buffer", -> - count = 0 - count = 0 - callbackBuffer = null - callback = (buffer) -> - callbackBuffer = buffer - count++ - atom.project.eachBuffer(callback) - expect(count).toBe 1 - expect(callbackBuffer).toBe atom.project.getBuffers()[0] - - it "invokes the callback for new buffers", -> - count = 0 - callbackBuffer = null - callback = (buffer) -> - callbackBuffer = buffer - count++ - - atom.project.eachBuffer(callback) - count = 0 - callbackBuffer = null - atom.project.bufferForPathSync(require.resolve('./fixtures/sample.txt')) - expect(count).toBe 1 - expect(callbackBuffer).toBe atom.project.getBuffers()[1] diff --git a/spec/spec-helper.coffee b/spec/spec-helper.coffee index 20845d9c1..085db7835 100644 --- a/spec/spec-helper.coffee +++ b/spec/spec-helper.coffee @@ -156,8 +156,6 @@ beforeEach -> spyOn(clipboard, 'writeText').andCallFake (text) -> clipboardContent = text spyOn(clipboard, 'readText').andCallFake -> clipboardContent - spyOn(atom.packages, 'uninstallAutocompletePlus') - addCustomMatchers(this) afterEach -> diff --git a/spec/text-editor-component-spec.coffee b/spec/text-editor-component-spec.coffee index 921e77a23..27e9864aa 100644 --- a/spec/text-editor-component-spec.coffee +++ b/spec/text-editor-component-spec.coffee @@ -285,18 +285,18 @@ describe "TextEditorComponent", -> componentNode.style.width = gutterWidth + (30 * charWidth) + 'px' component.measureDimensions() nextAnimationFrame() - expect(editor.getScrollWidth()).toBeGreaterThan scrollViewNode.offsetWidth + expect(wrapperNode.getScrollWidth()).toBeGreaterThan scrollViewNode.offsetWidth # At the time of writing, using width: 100% to achieve the full-width # lines caused full-screen repaints after switching away from an editor # and back again Please ensure you don't cause a performance regression if # you change this behavior. - editorFullWidth = editor.getScrollWidth() + editor.getVerticalScrollbarWidth() + editorFullWidth = wrapperNode.getScrollWidth() + wrapperNode.getVerticalScrollbarWidth() for lineNode in lineNodes expect(lineNode.getBoundingClientRect().width).toBe(editorFullWidth) - componentNode.style.width = gutterWidth + editor.getScrollWidth() + 100 + 'px' + componentNode.style.width = gutterWidth + wrapperNode.getScrollWidth() + 100 + 'px' component.measureDimensions() nextAnimationFrame() scrollViewWidth = scrollViewNode.offsetWidth @@ -473,7 +473,7 @@ describe "TextEditorComponent", -> editor.setText "a line that wraps \n" editor.setSoftWrapped(true) nextAnimationFrame() - componentNode.style.width = 16 * charWidth + editor.getVerticalScrollbarWidth() + 'px' + componentNode.style.width = 16 * charWidth + wrapperNode.getVerticalScrollbarWidth() + 'px' component.measureDimensions() nextAnimationFrame() @@ -893,7 +893,7 @@ describe "TextEditorComponent", -> beforeEach -> editor.setSoftWrapped(true) nextAnimationFrame() - componentNode.style.width = 16 * charWidth + editor.getVerticalScrollbarWidth() + 'px' + componentNode.style.width = 16 * charWidth + wrapperNode.getVerticalScrollbarWidth() + 'px' component.measureDimensions() nextAnimationFrame() @@ -1336,15 +1336,13 @@ describe "TextEditorComponent", -> expect(lineAndLineNumberHaveClass(6, 'a')).toBe true expect(lineAndLineNumberHaveClass(7, 'a')).toBe false - it "remove decoration classes and unsubscribes from markers decorations are removed", -> - expect(marker.getSubscriptionCount('changed')) + it "remove decoration classes when decorations are removed", -> decoration.destroy() nextAnimationFrame() expect(lineNumberHasClass(1, 'a')).toBe false expect(lineNumberHasClass(2, 'a')).toBe false expect(lineNumberHasClass(3, 'a')).toBe false expect(lineNumberHasClass(4, 'a')).toBe false - expect(marker.getSubscriptionCount('changed')).toBe 0 it "removes decorations when their marker is invalidated", -> editor.getBuffer().insert([3, 2], 'n') @@ -1674,8 +1672,8 @@ describe "TextEditorComponent", -> nextAnimationFrame() expect(editor.getCursorScreenPosition()).toEqual [0, 0] - editor.setScrollTop(3 * lineHeightInPixels) - editor.setScrollLeft(3 * charWidth) + wrapperNode.setScrollTop(3 * lineHeightInPixels) + wrapperNode.setScrollLeft(3 * charWidth) nextAnimationFrame() expect(inputNode.offsetTop).toBe 0 @@ -1690,8 +1688,8 @@ describe "TextEditorComponent", -> # In bounds and focused wrapperNode.focus() # updates via state change nextAnimationFrame() - expect(inputNode.offsetTop).toBe (5 * lineHeightInPixels) - editor.getScrollTop() - expect(inputNode.offsetLeft).toBe (4 * charWidth) - editor.getScrollLeft() + expect(inputNode.offsetTop).toBe (5 * lineHeightInPixels) - wrapperNode.getScrollTop() + expect(inputNode.offsetLeft).toBe (4 * charWidth) - wrapperNode.getScrollLeft() # In bounds, not focused inputNode.blur() # updates via state change @@ -1755,8 +1753,8 @@ describe "TextEditorComponent", -> wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' wrapperNode.style.width = 10 * charWidth + 'px' component.measureDimensions() - editor.setScrollTop(3.5 * lineHeightInPixels) - editor.setScrollLeft(2 * charWidth) + wrapperNode.setScrollTop(3.5 * lineHeightInPixels) + wrapperNode.setScrollLeft(2 * charWidth) nextAnimationFrame() linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([4, 8]))) @@ -1873,33 +1871,33 @@ describe "TextEditorComponent", -> component.measureDimensions() nextAnimationFrame() - expect(editor.getScrollTop()).toBe(0) - expect(editor.getScrollLeft()).toBe(0) + expect(wrapperNode.getScrollTop()).toBe(0) + expect(wrapperNode.getScrollLeft()).toBe(0) linesNode.dispatchEvent(buildMouseEvent('mousedown', {clientX: 0, clientY: 0}, which: 1)) linesNode.dispatchEvent(buildMouseEvent('mousemove', {clientX: 100, clientY: 50}, which: 1)) nextAnimationFrame() - expect(editor.getScrollTop()).toBe(0) - expect(editor.getScrollLeft()).toBeGreaterThan(0) + expect(wrapperNode.getScrollTop()).toBe(0) + expect(wrapperNode.getScrollLeft()).toBeGreaterThan(0) linesNode.dispatchEvent(buildMouseEvent('mousemove', {clientX: 100, clientY: 100}, which: 1)) nextAnimationFrame() - expect(editor.getScrollTop()).toBeGreaterThan(0) + expect(wrapperNode.getScrollTop()).toBeGreaterThan(0) - previousScrollTop = editor.getScrollTop() - previousScrollLeft = editor.getScrollLeft() + previousScrollTop = wrapperNode.getScrollTop() + previousScrollLeft = wrapperNode.getScrollLeft() linesNode.dispatchEvent(buildMouseEvent('mousemove', {clientX: 10, clientY: 50}, which: 1)) nextAnimationFrame() - expect(editor.getScrollTop()).toBe(previousScrollTop) - expect(editor.getScrollLeft()).toBeLessThan(previousScrollLeft) + expect(wrapperNode.getScrollTop()).toBe(previousScrollTop) + expect(wrapperNode.getScrollLeft()).toBeLessThan(previousScrollLeft) linesNode.dispatchEvent(buildMouseEvent('mousemove', {clientX: 10, clientY: 10}, which: 1)) nextAnimationFrame() - expect(editor.getScrollTop()).toBeLessThan(previousScrollTop) + expect(wrapperNode.getScrollTop()).toBeLessThan(previousScrollTop) it "stops selecting if the mouse is dragged into the dev tools", -> linesNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenPosition([2, 4]), which: 1)) @@ -1992,12 +1990,12 @@ describe "TextEditorComponent", -> nextAnimationFrame() expect(editor.getSelectedScreenRange()).toEqual [[5, 6], [12, 2]] - maximalScrollTop = editor.getScrollTop() + maximalScrollTop = wrapperNode.getScrollTop() linesNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenPosition([9, 3]), which: 1)) nextAnimationFrame() expect(editor.getSelectedScreenRange()).toEqual [[5, 6], [9, 4]] - expect(editor.getScrollTop()).toBe maximalScrollTop # does not autoscroll upward (regression) + expect(wrapperNode.getScrollTop()).toBe maximalScrollTop # does not autoscroll upward (regression) linesNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenPosition([9, 3]), which: 1)) @@ -2019,12 +2017,12 @@ describe "TextEditorComponent", -> nextAnimationFrame() expect(editor.getSelectedScreenRange()).toEqual [[5, 0], [12, 2]] - maximalScrollTop = editor.getScrollTop() + maximalScrollTop = wrapperNode.getScrollTop() linesNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenPosition([8, 4]), which: 1)) nextAnimationFrame() expect(editor.getSelectedScreenRange()).toEqual [[5, 0], [8, 0]] - expect(editor.getScrollTop()).toBe maximalScrollTop # does not autoscroll upward (regression) + expect(wrapperNode.getScrollTop()).toBe maximalScrollTop # does not autoscroll upward (regression) linesNode.dispatchEvent(buildMouseEvent('mouseup', clientCoordinatesForScreenPosition([9, 3]), which: 1)) @@ -2119,23 +2117,23 @@ describe "TextEditorComponent", -> component.measureDimensions() nextAnimationFrame() - expect(editor.getScrollTop()).toBe 0 + expect(wrapperNode.getScrollTop()).toBe 0 gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(2))) gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(8))) nextAnimationFrame() - expect(editor.getScrollTop()).toBeGreaterThan 0 - maxScrollTop = editor.getScrollTop() + expect(wrapperNode.getScrollTop()).toBeGreaterThan 0 + maxScrollTop = wrapperNode.getScrollTop() gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(10))) nextAnimationFrame() - expect(editor.getScrollTop()).toBe maxScrollTop + expect(wrapperNode.getScrollTop()).toBe maxScrollTop gutterNode.dispatchEvent(buildMouseEvent('mousemove', clientCoordinatesForScreenRowInGutter(7))) nextAnimationFrame() - expect(editor.getScrollTop()).toBeLessThan maxScrollTop + expect(wrapperNode.getScrollTop()).toBeLessThan maxScrollTop it "stops selecting if a textInput event occurs during the drag", -> gutterNode.dispatchEvent(buildMouseEvent('mousedown', clientCoordinatesForScreenRowInGutter(2))) @@ -2244,7 +2242,7 @@ describe "TextEditorComponent", -> editor.setSoftWrapped(true) nextAnimationFrame() - componentNode.style.width = 21 * charWidth + editor.getVerticalScrollbarWidth() + 'px' + componentNode.style.width = 21 * charWidth + wrapperNode.getVerticalScrollbarWidth() + 'px' component.measureDimensions() nextAnimationFrame() @@ -2416,7 +2414,7 @@ describe "TextEditorComponent", -> expect(verticalScrollbarNode.scrollTop).toBe 0 - editor.setScrollTop(10) + wrapperNode.setScrollTop(10) nextAnimationFrame() expect(verticalScrollbarNode.scrollTop).toBe 10 @@ -2434,7 +2432,7 @@ describe "TextEditorComponent", -> expect(horizontalScrollbarNode.scrollLeft).toBe 0 - editor.setScrollLeft(100) + wrapperNode.setScrollLeft(100) nextAnimationFrame() top = 0 @@ -2449,18 +2447,18 @@ describe "TextEditorComponent", -> component.measureDimensions() nextAnimationFrame() - expect(editor.getScrollLeft()).toBe 0 + expect(wrapperNode.getScrollLeft()).toBe 0 horizontalScrollbarNode.scrollLeft = 100 horizontalScrollbarNode.dispatchEvent(new UIEvent('scroll')) nextAnimationFrame() - expect(editor.getScrollLeft()).toBe 100 + expect(wrapperNode.getScrollLeft()).toBe 100 it "does not obscure the last line with the horizontal scrollbar", -> wrapperNode.style.height = 4.5 * lineHeightInPixels + 'px' wrapperNode.style.width = 10 * charWidth + 'px' component.measureDimensions() - editor.setScrollBottom(editor.getScrollHeight()) + wrapperNode.setScrollBottom(wrapperNode.getScrollHeight()) nextAnimationFrame() lastLineNode = component.lineNodeForScreenRow(editor.getLastScreenRow()) bottomOfLastLine = lastLineNode.getBoundingClientRect().bottom @@ -2479,7 +2477,7 @@ describe "TextEditorComponent", -> wrapperNode.style.height = 7 * lineHeightInPixels + 'px' wrapperNode.style.width = 10 * charWidth + 'px' component.measureDimensions() - editor.setScrollLeft(Infinity) + wrapperNode.setScrollLeft(Infinity) nextAnimationFrame() rightOfLongestLine = component.lineNodeForScreenRow(6).querySelector('.line > span:last-child').getBoundingClientRect().right @@ -2570,7 +2568,7 @@ describe "TextEditorComponent", -> component.measureDimensions() nextAnimationFrame() - expect(horizontalScrollbarNode.scrollWidth).toBe editor.getScrollWidth() + expect(horizontalScrollbarNode.scrollWidth).toBe wrapperNode.getScrollWidth() expect(horizontalScrollbarNode.style.left).toBe '0px' describe "mousewheel events", -> @@ -2649,14 +2647,14 @@ describe "TextEditorComponent", -> expect(component.presenter.mouseWheelScreenRow).toBe null it "clears the mouseWheelScreenRow after a delay even if the event does not cause scrolling", -> - expect(editor.getScrollTop()).toBe 0 + expect(wrapperNode.getScrollTop()).toBe 0 lineNode = componentNode.querySelector('.line') wheelEvent = new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: 10) Object.defineProperty(wheelEvent, 'target', get: -> lineNode) componentNode.dispatchEvent(wheelEvent) - expect(editor.getScrollTop()).toBe 0 + expect(wrapperNode.getScrollTop()).toBe 0 expect(component.presenter.mouseWheelScreenRow).toBe 0 advanceClock(component.presenter.stoppedScrollingDelay) @@ -2701,36 +2699,36 @@ describe "TextEditorComponent", -> # try to scroll past the top, which is impossible componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: 50)) - expect(editor.getScrollTop()).toBe 0 + expect(wrapperNode.getScrollTop()).toBe 0 expect(WheelEvent::preventDefault).not.toHaveBeenCalled() # scroll to the bottom in one huge event componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -3000)) nextAnimationFrame() - maxScrollTop = editor.getScrollTop() + maxScrollTop = wrapperNode.getScrollTop() expect(WheelEvent::preventDefault).toHaveBeenCalled() WheelEvent::preventDefault.reset() # try to scroll past the bottom, which is impossible componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: 0, wheelDeltaY: -30)) - expect(editor.getScrollTop()).toBe maxScrollTop + expect(wrapperNode.getScrollTop()).toBe maxScrollTop expect(WheelEvent::preventDefault).not.toHaveBeenCalled() # try to scroll past the left side, which is impossible componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: 50, wheelDeltaY: 0)) - expect(editor.getScrollLeft()).toBe 0 + expect(wrapperNode.getScrollLeft()).toBe 0 expect(WheelEvent::preventDefault).not.toHaveBeenCalled() # scroll all the way right componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -3000, wheelDeltaY: 0)) nextAnimationFrame() - maxScrollLeft = editor.getScrollLeft() + maxScrollLeft = wrapperNode.getScrollLeft() expect(WheelEvent::preventDefault).toHaveBeenCalled() WheelEvent::preventDefault.reset() # try to scroll past the right side, which is impossible componentNode.dispatchEvent(new WheelEvent('mousewheel', wheelDeltaX: -30, wheelDeltaY: 0)) - expect(editor.getScrollLeft()).toBe maxScrollLeft + expect(wrapperNode.getScrollLeft()).toBe maxScrollLeft expect(WheelEvent::preventDefault).not.toHaveBeenCalled() describe "input events", -> @@ -3056,7 +3054,7 @@ describe "TextEditorComponent", -> expect(componentNode.querySelectorAll('.line')).toHaveLength(6) gutterWidth = componentNode.querySelector('.gutter').offsetWidth - componentNode.style.width = gutterWidth + 14 * charWidth + editor.getVerticalScrollbarWidth() + 'px' + componentNode.style.width = gutterWidth + 14 * charWidth + wrapperNode.getVerticalScrollbarWidth() + 'px' atom.views.performDocumentPoll() nextAnimationFrame() expect(componentNode.querySelector('.line').textContent).toBe "var quicksort " @@ -3326,6 +3324,273 @@ describe "TextEditorComponent", -> expect(line1LeafNodes[0].classList.contains('indent-guide')).toBe false expect(line1LeafNodes[1].classList.contains('indent-guide')).toBe false + describe "autoscroll", -> + beforeEach -> + editor.setVerticalScrollMargin(2) + editor.setHorizontalScrollMargin(2) + component.setLineHeight("10px") + component.setFontSize(17) + component.measureDimensions() + nextAnimationFrame() + + wrapperNode.setWidth(55) + wrapperNode.setHeight(55) + component.measureDimensions() + nextAnimationFrame() + + component.presenter.setHorizontalScrollbarHeight(0) + component.presenter.setVerticalScrollbarWidth(0) + nextAnimationFrame() + + describe "when selecting buffer ranges", -> + it "autoscrolls the selection if it is last unless the 'autoscroll' option is false", -> + expect(wrapperNode.getScrollTop()).toBe 0 + + editor.setSelectedBufferRange([[5, 6], [6, 8]]) + nextAnimationFrame() + expect(wrapperNode.getScrollBottom()).toBe (7 + editor.getVerticalScrollMargin()) * 10 + expect(wrapperNode.getScrollRight()).toBe (8 + editor.getHorizontalScrollMargin()) * 10 + + editor.setSelectedBufferRange([[0, 0], [0, 0]]) + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe 0 + expect(wrapperNode.getScrollLeft()).toBe 0 + + editor.setSelectedBufferRange([[6, 6], [6, 8]]) + nextAnimationFrame() + expect(wrapperNode.getScrollBottom()).toBe (7 + editor.getVerticalScrollMargin()) * 10 + expect(wrapperNode.getScrollRight()).toBe (8 + editor.getHorizontalScrollMargin()) * 10 + + describe "when adding selections for buffer ranges", -> + it "autoscrolls to the added selection if needed", -> + editor.addSelectionForBufferRange([[8, 10], [8, 15]]) + nextAnimationFrame() + expect(wrapperNode.getScrollBottom()).toBe (9 * 10) + (2 * 10) + expect(wrapperNode.getScrollRight()).toBe (15 * 10) + (2 * 10) + + describe "when selecting lines containing cursors", -> + it "autoscrolls to the selection", -> + editor.setCursorScreenPosition([5, 6]) + nextAnimationFrame() + + wrapperNode.scrollToTop() + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe 0 + + editor.selectLinesContainingCursors() + nextAnimationFrame() + expect(wrapperNode.getScrollBottom()).toBe (7 + editor.getVerticalScrollMargin()) * 10 + + describe "when inserting text", -> + describe "when there are multiple empty selections on different lines", -> + it "autoscrolls to the last cursor", -> + editor.setCursorScreenPosition([1, 2], autoscroll: false) + nextAnimationFrame() + + editor.addCursorAtScreenPosition([10, 4], autoscroll: false) + nextAnimationFrame() + + expect(wrapperNode.getScrollTop()).toBe 0 + editor.insertText('a') + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe 75 + + describe "when scrolled to cursor position", -> + it "scrolls the last cursor into view, centering around the cursor if possible and the 'center' option isn't false", -> + editor.setCursorScreenPosition([8, 8], autoscroll: false) + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe 0 + expect(wrapperNode.getScrollLeft()).toBe 0 + + editor.scrollToCursorPosition() + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe (8.8 * 10) - 30 + expect(wrapperNode.getScrollBottom()).toBe (8.3 * 10) + 30 + expect(wrapperNode.getScrollRight()).toBe (9 + editor.getHorizontalScrollMargin()) * 10 + + wrapperNode.setScrollTop(0) + editor.scrollToCursorPosition(center: false) + expect(wrapperNode.getScrollTop()).toBe (7.8 - editor.getVerticalScrollMargin()) * 10 + expect(wrapperNode.getScrollBottom()).toBe (9.3 + editor.getVerticalScrollMargin()) * 10 + + describe "moving cursors", -> + it "scrolls down when the last cursor gets closer than ::verticalScrollMargin to the bottom of the editor", -> + expect(wrapperNode.getScrollTop()).toBe 0 + expect(wrapperNode.getScrollBottom()).toBe 5.5 * 10 + + editor.setCursorScreenPosition([2, 0]) + nextAnimationFrame() + expect(wrapperNode.getScrollBottom()).toBe 5.5 * 10 + + editor.moveDown() + nextAnimationFrame() + expect(wrapperNode.getScrollBottom()).toBe 6 * 10 + + editor.moveDown() + nextAnimationFrame() + expect(wrapperNode.getScrollBottom()).toBe 7 * 10 + + it "scrolls up when the last cursor gets closer than ::verticalScrollMargin to the top of the editor", -> + editor.setCursorScreenPosition([11, 0]) + nextAnimationFrame() + wrapperNode.setScrollBottom(wrapperNode.getScrollHeight()) + nextAnimationFrame() + + editor.moveUp() + nextAnimationFrame() + expect(wrapperNode.getScrollBottom()).toBe wrapperNode.getScrollHeight() + + editor.moveUp() + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe 7 * 10 + + editor.moveUp() + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe 6 * 10 + + it "scrolls right when the last cursor gets closer than ::horizontalScrollMargin to the right of the editor", -> + expect(wrapperNode.getScrollLeft()).toBe 0 + expect(wrapperNode.getScrollRight()).toBe 5.5 * 10 + + editor.setCursorScreenPosition([0, 2]) + nextAnimationFrame() + expect(wrapperNode.getScrollRight()).toBe 5.5 * 10 + + editor.moveRight() + nextAnimationFrame() + expect(wrapperNode.getScrollRight()).toBe 6 * 10 + + editor.moveRight() + nextAnimationFrame() + expect(wrapperNode.getScrollRight()).toBe 7 * 10 + + it "scrolls left when the last cursor gets closer than ::horizontalScrollMargin to the left of the editor", -> + wrapperNode.setScrollRight(wrapperNode.getScrollWidth()) + nextAnimationFrame() + expect(wrapperNode.getScrollRight()).toBe wrapperNode.getScrollWidth() + editor.setCursorScreenPosition([6, 62], autoscroll: false) + nextAnimationFrame() + + editor.moveLeft() + nextAnimationFrame() + expect(wrapperNode.getScrollLeft()).toBe 59 * 10 + + editor.moveLeft() + nextAnimationFrame() + expect(wrapperNode.getScrollLeft()).toBe 58 * 10 + + it "scrolls down when inserting lines makes the document longer than the editor's height", -> + editor.setCursorScreenPosition([13, Infinity]) + editor.insertNewline() + nextAnimationFrame() + + expect(wrapperNode.getScrollBottom()).toBe 14 * 10 + editor.insertNewline() + nextAnimationFrame() + expect(wrapperNode.getScrollBottom()).toBe 15 * 10 + + it "autoscrolls to the cursor when it moves due to undo", -> + editor.insertText('abc') + wrapperNode.setScrollTop(Infinity) + nextAnimationFrame() + + editor.undo() + nextAnimationFrame() + + expect(wrapperNode.getScrollTop()).toBe 0 + + it "doesn't scroll when the cursor moves into the visible area", -> + editor.setCursorBufferPosition([0, 0]) + nextAnimationFrame() + + wrapperNode.setScrollTop(40) + nextAnimationFrame() + + editor.setCursorBufferPosition([6, 0]) + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe 40 + + it "honors the autoscroll option on cursor and selection manipulation methods", -> + expect(wrapperNode.getScrollTop()).toBe 0 + editor.addCursorAtScreenPosition([11, 11], autoscroll: false) + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe 0 + editor.addCursorAtBufferPosition([11, 11], autoscroll: false) + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe 0 + editor.setCursorScreenPosition([11, 11], autoscroll: false) + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe 0 + editor.setCursorBufferPosition([11, 11], autoscroll: false) + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe 0 + editor.addSelectionForBufferRange([[11, 11], [11, 11]], autoscroll: false) + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe 0 + editor.addSelectionForScreenRange([[11, 11], [11, 12]], autoscroll: false) + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe 0 + editor.setSelectedBufferRange([[11, 0], [11, 1]], autoscroll: false) + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe 0 + editor.setSelectedScreenRange([[11, 0], [11, 6]], autoscroll: false) + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe 0 + editor.clearSelections(autoscroll: false) + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe 0 + + editor.addSelectionForScreenRange([[0, 0], [0, 4]]) + nextAnimationFrame() + + editor.getCursors()[0].setScreenPosition([11, 11], autoscroll: true) + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBeGreaterThan 0 + editor.getCursors()[0].setBufferPosition([0, 0], autoscroll: true) + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe 0 + editor.getSelections()[0].setScreenRange([[11, 0], [11, 4]], autoscroll: true) + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBeGreaterThan 0 + editor.getSelections()[0].setBufferRange([[0, 0], [0, 4]], autoscroll: true) + nextAnimationFrame() + expect(wrapperNode.getScrollTop()).toBe 0 + + describe "::screenPositionForPixelPosition(pixelPosition)", -> + it "clips pixel positions above buffer start", -> + expect(component.screenPositionForPixelPosition(top: -Infinity, left: -Infinity)).toEqual [0, 0] + expect(component.screenPositionForPixelPosition(top: -Infinity, left: Infinity)).toEqual [0, 0] + expect(component.screenPositionForPixelPosition(top: -1, left: Infinity)).toEqual [0, 0] + expect(component.screenPositionForPixelPosition(top: 0, left: Infinity)).toEqual [0, 29] + + it "clips pixel positions below buffer end", -> + expect(component.screenPositionForPixelPosition(top: Infinity, left: -Infinity)).toEqual [12, 2] + expect(component.screenPositionForPixelPosition(top: Infinity, left: Infinity)).toEqual [12, 2] + expect(component.screenPositionForPixelPosition(top: component.getScrollHeight() + 1, left: 0)).toEqual [12, 2] + expect(component.screenPositionForPixelPosition(top: component.getScrollHeight() - 1, left: 0)).toEqual [12, 0] + + describe "::getVisibleRowRange()", -> + beforeEach -> + wrapperNode.style.height = lineHeightInPixels * 8 + "px" + component.measureDimensions() + nextAnimationFrame() + + it "returns the first and the last visible rows", -> + component.setScrollTop(0) + nextAnimationFrame() + + expect(component.getVisibleRowRange()).toEqual [0, 9] + + it "ends at last buffer row even if there's more space available", -> + wrapperNode.style.height = lineHeightInPixels * 13 + "px" + component.measureDimensions() + nextAnimationFrame() + + component.setScrollTop(60) + nextAnimationFrame() + + expect(component.getVisibleRowRange()).toEqual [0, 13] + describe "middle mouse paste on Linux", -> originalPlatform = null @@ -3370,15 +3635,15 @@ describe "TextEditorComponent", -> clientCoordinatesForScreenPosition = (screenPosition) -> positionOffset = wrapperNode.pixelPositionForScreenPosition(screenPosition) scrollViewClientRect = componentNode.querySelector('.scroll-view').getBoundingClientRect() - clientX = scrollViewClientRect.left + positionOffset.left - editor.getScrollLeft() - clientY = scrollViewClientRect.top + positionOffset.top - editor.getScrollTop() + clientX = scrollViewClientRect.left + positionOffset.left - wrapperNode.getScrollLeft() + clientY = scrollViewClientRect.top + positionOffset.top - wrapperNode.getScrollTop() {clientX, clientY} clientCoordinatesForScreenRowInGutter = (screenRow) -> positionOffset = wrapperNode.pixelPositionForScreenPosition([screenRow, Infinity]) gutterClientRect = componentNode.querySelector('.gutter').getBoundingClientRect() - clientX = gutterClientRect.left + positionOffset.left - editor.getScrollLeft() - clientY = gutterClientRect.top + positionOffset.top - editor.getScrollTop() + clientX = gutterClientRect.left + positionOffset.left - wrapperNode.getScrollLeft() + clientY = gutterClientRect.top + positionOffset.top - wrapperNode.getScrollTop() {clientX, clientY} lineAndLineNumberHaveClass = (screenRow, klass) -> diff --git a/spec/text-editor-presenter-spec.coffee b/spec/text-editor-presenter-spec.coffee index 7a7709e7f..a57356604 100644 --- a/spec/text-editor-presenter-spec.coffee +++ b/spec/text-editor-presenter-spec.coffee @@ -386,6 +386,18 @@ describe "TextEditorPresenter", -> expectStateUpdate presenter, -> presenter.setScrollLeft(-300) expect(presenter.getState().horizontalScrollbar.scrollLeft).toBe 0 + it "is always 0 when soft wrapping is enabled", -> + presenter = buildPresenter(scrollLeft: 0, verticalScrollbarWidth: 0, contentFrameWidth: 85, baseCharacterWidth: 10) + + editor.setSoftWrapped(false) + presenter.setScrollLeft(Infinity) + expect(presenter.getState().content.scrollLeft).toBeGreaterThan 0 + + editor.setSoftWrapped(true) + expect(presenter.getState().content.scrollLeft).toBe 0 + presenter.setScrollLeft(10) + expect(presenter.getState().content.scrollLeft).toBe 0 + describe ".verticalScrollbar", -> describe ".visible", -> it "is true if the scrollHeight exceeds the computed client height", -> @@ -533,11 +545,11 @@ describe "TextEditorPresenter", -> expectValues presenter.getState().hiddenInput, {top: 0, left: 0} expectStateUpdate presenter, -> editor.setCursorBufferPosition([11, 43]) - expectValues presenter.getState().hiddenInput, {top: 11 * 10 - editor.getScrollTop(), left: 43 * 10 - editor.getScrollLeft()} + expectValues presenter.getState().hiddenInput, {top: 11 * 10 - presenter.getScrollTop(), left: 43 * 10 - presenter.getScrollLeft()} newCursor = null expectStateUpdate presenter, -> newCursor = editor.addCursorAtBufferPosition([6, 10]) - expectValues presenter.getState().hiddenInput, {top: (6 * 10) - editor.getScrollTop(), left: (10 * 10) - editor.getScrollLeft()} + expectValues presenter.getState().hiddenInput, {top: (6 * 10) - presenter.getScrollTop(), left: (10 * 10) - presenter.getScrollLeft()} expectStateUpdate presenter, -> newCursor.destroy() expectValues presenter.getState().hiddenInput, {top: 50 - 10, left: 300 - 10} @@ -582,6 +594,7 @@ describe "TextEditorPresenter", -> advanceClock(100) expect(presenter.getState().content.scrollingVertically).toBe true presenter.setScrollTop(10) + presenter.getState() # commits scroll position advanceClock(100) expect(presenter.getState().content.scrollingVertically).toBe true expectStateUpdate presenter, -> advanceClock(100) @@ -685,12 +698,55 @@ describe "TextEditorPresenter", -> expect(presenter.getState().content.scrollWidth).toBe 10 * editor.getMaxScreenLineLength() + 1 describe ".scrollTop", -> + it "changes based on the scroll operation that was performed last", -> + presenter = buildPresenter(scrollTop: 0, lineHeight: 10, explicitHeight: 20) + expect(presenter.getState().content.scrollTop).toBe(0) + + presenter.setScrollTop(20) + editor.setCursorBufferPosition([5, 0]) + + expect(presenter.getState().content.scrollTop).toBe(50) + + editor.setCursorBufferPosition([8, 0]) + presenter.setScrollTop(10) + + expect(presenter.getState().content.scrollTop).toBe(10) + + it "corresponds to the passed logical coordinates when building the presenter", -> + presenter = buildPresenter(scrollRow: 4, lineHeight: 10, explicitHeight: 20) + expect(presenter.getState().content.scrollTop).toBe(40) + it "tracks the value of ::scrollTop", -> presenter = buildPresenter(scrollTop: 10, lineHeight: 10, explicitHeight: 20) expect(presenter.getState().content.scrollTop).toBe 10 expectStateUpdate presenter, -> presenter.setScrollTop(50) expect(presenter.getState().content.scrollTop).toBe 50 + it "keeps the model up to date with the corresponding logical coordinates", -> + presenter = buildPresenter(scrollTop: 0, explicitHeight: 20, horizontalScrollbarHeight: 10, lineHeight: 10) + + expectStateUpdate presenter, -> presenter.setScrollTop(50) + presenter.getState() # commits scroll position + expect(editor.getScrollRow()).toBe(5) + + expectStateUpdate presenter, -> presenter.setScrollTop(57) + presenter.getState() # commits scroll position + expect(editor.getScrollRow()).toBe(6) + + it "reassigns the scrollTop if it exceeds the max possible value after lines are removed", -> + presenter = buildPresenter(scrollTop: 80, lineHeight: 10, explicitHeight: 50, horizontalScrollbarHeight: 0) + expect(presenter.getState().content.scrollTop).toBe(80) + buffer.deleteRows(10, 9, 8) + expect(presenter.getState().content.scrollTop).toBe(60) + + it "is always rounded to the nearest integer", -> + presenter = buildPresenter(scrollTop: 10, lineHeight: 10, explicitHeight: 20) + expect(presenter.getState().content.scrollTop).toBe 10 + expectStateUpdate presenter, -> presenter.setScrollTop(11.4) + expect(presenter.getState().content.scrollTop).toBe 11 + expectStateUpdate presenter, -> presenter.setScrollTop(12.6) + expect(presenter.getState().content.scrollTop).toBe 13 + it "scrolls down automatically when the model is changed", -> presenter = buildPresenter(scrollTop: 0, lineHeight: 10, explicitHeight: 20) @@ -708,17 +764,21 @@ describe "TextEditorPresenter", -> expectStateUpdate presenter, -> presenter.setExplicitHeight(60) expect(presenter.getState().content.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + expect(presenter.getRealScrollTop()).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> presenter.setHorizontalScrollbarHeight(5) expect(presenter.getState().content.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + expect(presenter.getRealScrollTop()).toBe presenter.scrollHeight - presenter.clientHeight expectStateUpdate presenter, -> editor.getBuffer().delete([[8, 0], [12, 0]]) expect(presenter.getState().content.scrollTop).toBe presenter.scrollHeight - presenter.clientHeight + expect(presenter.getRealScrollTop()).toBe presenter.scrollHeight - presenter.clientHeight # Scroll top only gets smaller when needed as dimensions change, never bigger scrollTopBefore = presenter.getState().verticalScrollbar.scrollTop expectStateUpdate presenter, -> editor.getBuffer().insert([9, Infinity], '\n\n\n') expect(presenter.getState().content.scrollTop).toBe scrollTopBefore + expect(presenter.getRealScrollTop()).toBe scrollTopBefore it "never goes negative", -> presenter = buildPresenter(scrollTop: 10, explicitHeight: 50, horizontalScrollbarHeight: 10) @@ -738,30 +798,72 @@ describe "TextEditorPresenter", -> expect(presenter.getState().content.scrollTop).toBe presenter.contentHeight - presenter.clientHeight describe ".scrollLeft", -> + it "changes based on the scroll operation that was performed last", -> + presenter = buildPresenter(scrollLeft: 0, lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 10) + expect(presenter.getState().content.scrollLeft).toBe(0) + + presenter.setScrollLeft(20) + editor.setCursorBufferPosition([0, 9]) + + expect(presenter.getState().content.scrollLeft).toBe(90) + + editor.setCursorBufferPosition([0, 18]) + presenter.setScrollLeft(50) + + expect(presenter.getState().content.scrollLeft).toBe(50) + + it "corresponds to the passed logical coordinates when building the presenter", -> + presenter = buildPresenter(scrollColumn: 3, lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500) + expect(presenter.getState().content.scrollLeft).toBe(30) + it "tracks the value of ::scrollLeft", -> presenter = buildPresenter(scrollLeft: 10, lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500) expect(presenter.getState().content.scrollLeft).toBe 10 expectStateUpdate presenter, -> presenter.setScrollLeft(50) expect(presenter.getState().content.scrollLeft).toBe 50 + it "keeps the model up to date with the corresponding logical coordinates", -> + presenter = buildPresenter(scrollLeft: 0, lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500) + + expectStateUpdate presenter, -> presenter.setScrollLeft(50) + presenter.getState() # commits scroll position + expect(editor.getScrollColumn()).toBe(5) + + expectStateUpdate presenter, -> presenter.setScrollLeft(57) + presenter.getState() # commits scroll position + expect(editor.getScrollColumn()).toBe(6) + + it "is always rounded to the nearest integer", -> + presenter = buildPresenter(scrollLeft: 10, lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500) + expect(presenter.getState().content.scrollLeft).toBe 10 + expectStateUpdate presenter, -> presenter.setScrollLeft(11.4) + expect(presenter.getState().content.scrollLeft).toBe 11 + expectStateUpdate presenter, -> presenter.setScrollLeft(12.6) + expect(presenter.getState().content.scrollLeft).toBe 13 + it "never exceeds the computed scrollWidth minus the computed clientWidth", -> presenter = buildPresenter(scrollLeft: 10, lineHeight: 10, baseCharacterWidth: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500) expectStateUpdate presenter, -> presenter.setScrollLeft(300) expect(presenter.getState().content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth + expect(presenter.getRealScrollLeft()).toBe presenter.scrollWidth - presenter.clientWidth expectStateUpdate presenter, -> presenter.setContentFrameWidth(600) expect(presenter.getState().content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth + expect(presenter.getRealScrollLeft()).toBe presenter.scrollWidth - presenter.clientWidth expectStateUpdate presenter, -> presenter.setVerticalScrollbarWidth(5) expect(presenter.getState().content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth + expect(presenter.getRealScrollLeft()).toBe presenter.scrollWidth - presenter.clientWidth expectStateUpdate presenter, -> editor.getBuffer().delete([[6, 0], [6, Infinity]]) expect(presenter.getState().content.scrollLeft).toBe presenter.scrollWidth - presenter.clientWidth + expect(presenter.getRealScrollLeft()).toBe presenter.scrollWidth - presenter.clientWidth # Scroll top only gets smaller when needed as dimensions change, never bigger scrollLeftBefore = presenter.getState().content.scrollLeft expectStateUpdate presenter, -> editor.getBuffer().insert([6, 0], new Array(100).join('x')) expect(presenter.getState().content.scrollLeft).toBe scrollLeftBefore + expect(presenter.getRealScrollLeft()).toBe scrollLeftBefore it "never goes negative", -> presenter = buildPresenter(scrollLeft: 10, verticalScrollbarWidth: 10, contentFrameWidth: 500) @@ -1807,7 +1909,8 @@ describe "TextEditorPresenter", -> expectStateUpdate presenter, -> editor.insertNewline() - editor.setScrollTop(scrollTop) # I'm fighting the editor + presenter.setScrollTop(scrollTop) # I'm fighting the editor + expectValues stateForOverlay(presenter, decoration), { item: item pixelPosition: {top: 6 * 10 - scrollTop - itemHeight, left: gutterWidth} @@ -2084,6 +2187,12 @@ describe "TextEditorPresenter", -> expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 3, softWrapped: true} expectValues lineNumberStateForScreenRow(presenter, 7), {screenRow: 7, bufferRow: 4, softWrapped: false} + presenter.setContentFrameWidth(500) + + expectValues lineNumberStateForScreenRow(presenter, 5), {screenRow: 5, bufferRow: 4, softWrapped: false} + expectValues lineNumberStateForScreenRow(presenter, 6), {screenRow: 6, bufferRow: 5, softWrapped: false} + expectValues lineNumberStateForScreenRow(presenter, 7), {screenRow: 7, bufferRow: 6, softWrapped: false} + describe ".decorationClasses", -> it "adds decoration classes to the relevant line number state objects, both initially and when decorations change", -> marker1 = editor.markBufferRange([[4, 0], [6, 2]], invalidate: 'touch', maintainHistory: true) diff --git a/spec/text-editor-spec.coffee b/spec/text-editor-spec.coffee index 203b138cd..59b8a218e 100644 --- a/spec/text-editor-spec.coffee +++ b/spec/text-editor-spec.coffee @@ -31,7 +31,7 @@ describe "TextEditor", -> runs -> fs.mkdirSync(pathToOpen) - expect(editor1.testSerialization()).toBeUndefined() + expect(TextEditor.deserialize(editor1.serialize())).toBeUndefined() it "restores selections and folds based on markers in the buffer", -> editor.setSelectedBufferRange([[1, 2], [3, 4]]) @@ -39,7 +39,7 @@ describe "TextEditor", -> editor.foldBufferRow(4) expect(editor.isFoldedAtBufferRow(4)).toBeTruthy() - editor2 = editor.testSerialization() + editor2 = TextEditor.deserialize(editor.serialize()) expect(editor2.id).toBe editor.id expect(editor2.getBuffer().getPath()).toBe editor.getBuffer().getPath() @@ -52,7 +52,7 @@ describe "TextEditor", -> atom.config.set('editor.showInvisibles', true) previousInvisibles = editor.tokenizedLineForScreenRow(0).invisibles - editor2 = editor.testSerialization() + editor2 = TextEditor.deserialize(editor.serialize()) expect(previousInvisibles).toBeDefined() expect(editor2.displayBuffer.tokenizedLineForScreenRow(0).invisibles).toEqual previousInvisibles @@ -909,118 +909,6 @@ describe "TextEditor", -> cursor2 = editor.addCursorAtBufferPosition([1, 4]) expect(cursor2.marker).toBe cursor1.marker - describe "autoscroll", -> - beforeEach -> - editor.setVerticalScrollMargin(2) - editor.setHorizontalScrollMargin(2) - editor.setLineHeightInPixels(10) - editor.setDefaultCharWidth(10) - editor.setHorizontalScrollbarHeight(0) - editor.setHeight(5.5 * 10) - editor.setWidth(5.5 * 10) - - it "scrolls down when the last cursor gets closer than ::verticalScrollMargin to the bottom of the editor", -> - expect(editor.getScrollTop()).toBe 0 - expect(editor.getScrollBottom()).toBe 5.5 * 10 - - editor.setCursorScreenPosition([2, 0]) - expect(editor.getScrollBottom()).toBe 5.5 * 10 - - editor.moveDown() - expect(editor.getScrollBottom()).toBe 6 * 10 - - editor.moveDown() - expect(editor.getScrollBottom()).toBe 7 * 10 - - it "scrolls up when the last cursor gets closer than ::verticalScrollMargin to the top of the editor", -> - editor.setCursorScreenPosition([11, 0]) - editor.setScrollBottom(editor.getScrollHeight()) - - editor.moveUp() - expect(editor.getScrollBottom()).toBe editor.getScrollHeight() - - editor.moveUp() - expect(editor.getScrollTop()).toBe 7 * 10 - - editor.moveUp() - expect(editor.getScrollTop()).toBe 6 * 10 - - it "scrolls right when the last cursor gets closer than ::horizontalScrollMargin to the right of the editor", -> - expect(editor.getScrollLeft()).toBe 0 - expect(editor.getScrollRight()).toBe 5.5 * 10 - - editor.setCursorScreenPosition([0, 2]) - expect(editor.getScrollRight()).toBe 5.5 * 10 - - editor.moveRight() - expect(editor.getScrollRight()).toBe 6 * 10 - - editor.moveRight() - expect(editor.getScrollRight()).toBe 7 * 10 - - it "scrolls left when the last cursor gets closer than ::horizontalScrollMargin to the left of the editor", -> - editor.setScrollRight(editor.getScrollWidth()) - expect(editor.getScrollRight()).toBe editor.getScrollWidth() - editor.setCursorScreenPosition([6, 62], autoscroll: false) - - editor.moveLeft() - expect(editor.getScrollLeft()).toBe 59 * 10 - - editor.moveLeft() - expect(editor.getScrollLeft()).toBe 58 * 10 - - it "scrolls down when inserting lines makes the document longer than the editor's height", -> - editor.setCursorScreenPosition([13, Infinity]) - editor.insertNewline() - expect(editor.getScrollBottom()).toBe 14 * 10 - editor.insertNewline() - expect(editor.getScrollBottom()).toBe 15 * 10 - - it "autoscrolls to the cursor when it moves due to undo", -> - editor.insertText('abc') - editor.setScrollTop(Infinity) - editor.undo() - expect(editor.getScrollTop()).toBe 0 - - it "doesn't scroll when the cursor moves into the visible area", -> - editor.setCursorBufferPosition([0, 0]) - editor.setScrollTop(40) - expect(editor.getVisibleRowRange()).toEqual([4, 9]) - editor.setCursorBufferPosition([6, 0]) - expect(editor.getScrollTop()).toBe 40 - - it "honors the autoscroll option on cursor and selection manipulation methods", -> - expect(editor.getScrollTop()).toBe 0 - editor.addCursorAtScreenPosition([11, 11], autoscroll: false) - expect(editor.getScrollTop()).toBe 0 - editor.addCursorAtBufferPosition([11, 11], autoscroll: false) - expect(editor.getScrollTop()).toBe 0 - editor.setCursorScreenPosition([11, 11], autoscroll: false) - expect(editor.getScrollTop()).toBe 0 - editor.setCursorBufferPosition([11, 11], autoscroll: false) - expect(editor.getScrollTop()).toBe 0 - editor.addSelectionForBufferRange([[11, 11], [11, 11]], autoscroll: false) - expect(editor.getScrollTop()).toBe 0 - editor.addSelectionForScreenRange([[11, 11], [11, 12]], autoscroll: false) - expect(editor.getScrollTop()).toBe 0 - editor.setSelectedBufferRange([[11, 0], [11, 1]], autoscroll: false) - expect(editor.getScrollTop()).toBe 0 - editor.setSelectedScreenRange([[11, 0], [11, 6]], autoscroll: false) - expect(editor.getScrollTop()).toBe 0 - editor.clearSelections(autoscroll: false) - expect(editor.getScrollTop()).toBe 0 - - editor.addSelectionForScreenRange([[0, 0], [0, 4]]) - - editor.getCursors()[0].setScreenPosition([11, 11], autoscroll: true) - expect(editor.getScrollTop()).toBeGreaterThan 0 - editor.getCursors()[0].setBufferPosition([0, 0], autoscroll: true) - expect(editor.getScrollTop()).toBe 0 - editor.getSelections()[0].setScreenRange([[11, 0], [11, 4]], autoscroll: true) - expect(editor.getScrollTop()).toBeGreaterThan 0 - editor.getSelections()[0].setBufferRange([[0, 0], [0, 4]], autoscroll: true) - expect(editor.getScrollTop()).toBe 0 - describe '.logCursorScope()', -> beforeEach -> spyOn(atom.notifications, 'addInfo') @@ -1306,20 +1194,6 @@ describe "TextEditor", -> editor.selectLinesContainingCursors() expect(editor.getSelectedBufferRange()).toEqual [[1, 0], [4, 0]] - it "autoscrolls to the selection", -> - editor.setLineHeightInPixels(10) - editor.setDefaultCharWidth(10) - editor.setHeight(50) - editor.setWidth(50) - editor.setHorizontalScrollbarHeight(0) - editor.setCursorScreenPosition([5, 6]) - - editor.scrollToTop() - expect(editor.getScrollTop()).toBe 0 - - editor.selectLinesContainingCursors() - expect(editor.getScrollBottom()).toBe (7 + editor.getVerticalScrollMargin()) * 10 - describe ".selectToBeginningOfWord()", -> it "selects text from cusor position to beginning of word", -> editor.setCursorScreenPosition [0, 13] @@ -1572,30 +1446,6 @@ describe "TextEditor", -> expect(selection1).toBe selection expect(selection1.getScreenRange()).toEqual [[2, 2], [3, 4]] - describe ".setSelectedBufferRange(range)", -> - it "autoscrolls the selection if it is last unless the 'autoscroll' option is false", -> - editor.setVerticalScrollMargin(2) - editor.setHorizontalScrollMargin(2) - editor.setLineHeightInPixels(10) - editor.setDefaultCharWidth(10) - editor.setHeight(70) - editor.setWidth(100) - editor.setHorizontalScrollbarHeight(0) - - expect(editor.getScrollTop()).toBe 0 - - editor.setSelectedBufferRange([[5, 6], [6, 8]]) - expect(editor.getScrollBottom()).toBe (7 + editor.getVerticalScrollMargin()) * 10 - expect(editor.getScrollRight()).toBe (8 + editor.getHorizontalScrollMargin()) * 10 - - editor.setSelectedBufferRange([[0, 0], [0, 0]]) - expect(editor.getScrollTop()).toBe 0 - expect(editor.getScrollLeft()).toBe 0 - - editor.setSelectedBufferRange([[6, 6], [6, 8]]) - expect(editor.getScrollBottom()).toBe (7 + editor.getVerticalScrollMargin()) * 10 - expect(editor.getScrollRight()).toBe (8 + editor.getHorizontalScrollMargin()) * 10 - describe ".selectMarker(marker)", -> describe "if the marker is valid", -> it "selects the marker's range and returns the selected range", -> @@ -1615,17 +1465,6 @@ describe "TextEditor", -> editor.addSelectionForBufferRange([[3, 4], [5, 6]]) expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 0]], [[3, 4], [5, 6]]] - it "autoscrolls to the added selection if needed", -> - editor.setVerticalScrollMargin(2) - editor.setHorizontalScrollMargin(2) - editor.setLineHeightInPixels(10) - editor.setDefaultCharWidth(10) - editor.setHeight(80) - editor.setWidth(100) - editor.addSelectionForBufferRange([[8, 10], [8, 15]]) - expect(editor.getScrollBottom()).toBe (9 * 10) + (2 * 10) - expect(editor.getScrollRight()).toBe (15 * 10) + (2 * 10) - describe ".addSelectionBelow()", -> describe "when the selection is non-empty", -> it "selects the same region of the line below current selections if possible", -> @@ -1890,7 +1729,7 @@ describe "TextEditor", -> expect(editor.getSelectedBufferRanges()).toEqual [[[0, 0], [0, 3]]] describe ".consolidateSelections()", -> - it "destroys all selections but the most recent, returning true if any selections were destroyed", -> + it "destroys all selections but the least recent, returning true if any selections were destroyed", -> editor.setSelectedBufferRange([[3, 16], [3, 21]]) selection1 = editor.getLastSelection() selection2 = editor.addSelectionForBufferRange([[3, 25], [3, 34]]) @@ -1898,10 +1737,10 @@ describe "TextEditor", -> expect(editor.getSelections()).toEqual [selection1, selection2, selection3] expect(editor.consolidateSelections()).toBeTruthy() - expect(editor.getSelections()).toEqual [selection3] - expect(selection3.isEmpty()).toBeFalsy() + expect(editor.getSelections()).toEqual [selection1] + expect(selection1.isEmpty()).toBeFalsy() expect(editor.consolidateSelections()).toBeFalsy() - expect(editor.getSelections()).toEqual [selection3] + expect(editor.getSelections()).toEqual [selection1] describe "when the cursor is moved while there is a selection", -> makeSelection = -> selection.setBufferRange [[1, 2], [1, 5]] @@ -1976,16 +1815,6 @@ describe "TextEditor", -> expect(cursor1.getBufferPosition()).toEqual [1, 5] expect(cursor2.getBufferPosition()).toEqual [2, 7] - it "autoscrolls to the last cursor", -> - editor.setCursorScreenPosition([1, 2]) - editor.addCursorAtScreenPosition([10, 4]) - editor.setLineHeightInPixels(10) - editor.setHeight(50) - - expect(editor.getScrollTop()).toBe 0 - editor.insertText('a') - expect(editor.getScrollTop()).toBe 80 - describe "when there are multiple non-empty selections", -> describe "when the selections are on the same line", -> it "replaces each selection range with the inserted characters", -> @@ -3855,19 +3684,6 @@ describe "TextEditor", -> runs -> expect(editor.softTabs).toBe false - it "uses hard tabs in Makefile files", -> - # FIXME remove once this is handled by a scoped setting in the - # language-make package - - waitsForPromise -> - atom.packages.activatePackage('language-make') - - waitsForPromise -> - atom.project.open('Makefile').then (o) -> editor = o - - runs -> - expect(editor.softTabs).toBe false - describe "when editor.tabType is 'hard'", -> beforeEach -> atom.config.set('editor.tabType', 'hard') @@ -4547,30 +4363,10 @@ describe "TextEditor", -> editor.normalizeTabsInBufferRange([[0, 0], [Infinity, Infinity]]) expect(editor.getText()).toBe ' ' - describe ".scrollToCursorPosition()", -> - it "scrolls the last cursor into view, centering around the cursor if possible and the 'center' option isn't false", -> - editor.setCursorScreenPosition([8, 8]) - editor.setLineHeightInPixels(10) - editor.setDefaultCharWidth(10) - editor.setHeight(60) - editor.setWidth(130) - editor.setHorizontalScrollbarHeight(0) - expect(editor.getScrollTop()).toBe 0 - expect(editor.getScrollLeft()).toBe 0 - - editor.scrollToCursorPosition() - expect(editor.getScrollTop()).toBe (8.5 * 10) - 30 - expect(editor.getScrollBottom()).toBe (8.5 * 10) + 30 - expect(editor.getScrollRight()).toBe (9 + editor.getHorizontalScrollMargin()) * 10 - - editor.setScrollTop(0) - editor.scrollToCursorPosition(center: false) - expect(editor.getScrollBottom()).toBe (9 + editor.getVerticalScrollMargin()) * 10 - describe ".pageUp/Down()", -> it "moves the cursor down one page length", -> editor.setLineHeightInPixels(10) - editor.setHeight(50) + editor.setHeight(50, true) expect(editor.getCursorBufferPosition().row).toBe 0 editor.pageDown() @@ -4588,8 +4384,7 @@ describe "TextEditor", -> describe ".selectPageUp/Down()", -> it "selects one screen height of text up or down", -> editor.setLineHeightInPixels(10) - editor.setHeight(50) - expect(editor.getScrollHeight()).toBe 130 + editor.setHeight(50, true) expect(editor.getCursorBufferPosition().row).toBe 0 editor.selectPageDown() @@ -4979,18 +4774,6 @@ describe "TextEditor", -> beforeEach -> marker = editor.markBufferRange([[1, 0], [1, 0]]) - it "casts 'gutter' type to 'line-number' unless a gutter name is specified.", -> - jasmine.snapshotDeprecations() - - lineNumberDecoration = editor.decorateMarker(marker, {type: 'gutter'}) - customGutterDecoration = editor.decorateMarker(marker, {type: 'gutter', gutterName: 'custom'}) - expect(lineNumberDecoration.getProperties().type).toBe 'line-number' - expect(lineNumberDecoration.getProperties().gutterName).toBe 'line-number' - expect(customGutterDecoration.getProperties().type).toBe 'gutter' - expect(customGutterDecoration.getProperties().gutterName).toBe 'custom' - - jasmine.restoreDeprecationsSnapshot() - it 'reflects an added decoration when one of its custom gutters is decorated.', -> gutter = editor.addGutter {'name': 'custom-gutter'} decoration = gutter.decorateMarker marker, {class: 'custom-class'} diff --git a/spec/theme-manager-spec.coffee b/spec/theme-manager-spec.coffee index 1e5653fbe..ea0ca19e6 100644 --- a/spec/theme-manager-spec.coffee +++ b/spec/theme-manager-spec.coffee @@ -2,19 +2,17 @@ path = require 'path' fs = require 'fs-plus' temp = require 'temp' -ThemeManager = require '../src/theme-manager' Package = require '../src/package' -describe "ThemeManager", -> - themeManager = null +describe "atom.themes", -> resourcePath = atom.getLoadSettings().resourcePath configDirPath = atom.getConfigDirPath() beforeEach -> - themeManager = new ThemeManager({packageManager: atom.packages, resourcePath, configDirPath}) + spyOn(console, 'warn') afterEach -> - themeManager.deactivateThemes() + atom.themes.deactivateThemes() describe "theme getters and setters", -> beforeEach -> @@ -26,17 +24,17 @@ describe "ThemeManager", -> describe 'getLoadedThemes', -> it 'gets all the loaded themes', -> - themes = themeManager.getLoadedThemes() + themes = atom.themes.getLoadedThemes() expect(themes.length).toBeGreaterThan(2) describe "getActiveThemes", -> it 'gets all the active themes', -> - waitsForPromise -> themeManager.activateThemes() + waitsForPromise -> atom.themes.activateThemes() runs -> names = atom.config.get('core.themes') expect(names.length).toBeGreaterThan(0) - themes = themeManager.getActiveThemes() + themes = atom.themes.getActiveThemes() expect(themes).toHaveLength(names.length) describe "when the core.themes config value contains invalid entry", -> @@ -53,13 +51,13 @@ describe "ThemeManager", -> 'atom-dark-ui' ] - expect(themeManager.getEnabledThemeNames()).toEqual ['atom-dark-ui', 'atom-light-ui'] + expect(atom.themes.getEnabledThemeNames()).toEqual ['atom-dark-ui', 'atom-light-ui'] describe "::getImportPaths()", -> it "returns the theme directories before the themes are loaded", -> atom.config.set('core.themes', ['theme-with-index-less', 'atom-dark-ui', 'atom-light-ui']) - paths = themeManager.getImportPaths() + paths = atom.themes.getImportPaths() # syntax theme is not a dir at this time, so only two. expect(paths.length).toBe 2 @@ -68,21 +66,21 @@ describe "ThemeManager", -> it "ignores themes that cannot be resolved to a directory", -> atom.config.set('core.themes', ['definitely-not-a-theme']) - expect(-> themeManager.getImportPaths()).not.toThrow() + expect(-> atom.themes.getImportPaths()).not.toThrow() describe "when the core.themes config value changes", -> it "add/removes stylesheets to reflect the new config value", -> - themeManager.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy() + atom.themes.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy() spyOn(atom.styles, 'getUserStyleSheetPath').andCallFake -> null waitsForPromise -> - themeManager.activateThemes() + atom.themes.activateThemes() runs -> didChangeActiveThemesHandler.reset() atom.config.set('core.themes', []) - waitsFor -> + waitsFor 'a', -> didChangeActiveThemesHandler.callCount is 1 runs -> @@ -90,7 +88,7 @@ describe "ThemeManager", -> expect(document.querySelectorAll('style.theme')).toHaveLength 0 atom.config.set('core.themes', ['atom-dark-ui']) - waitsFor -> + waitsFor 'b', -> didChangeActiveThemesHandler.callCount is 1 runs -> @@ -99,7 +97,7 @@ describe "ThemeManager", -> expect(document.querySelector('style[priority="1"]').getAttribute('source-path')).toMatch /atom-dark-ui/ atom.config.set('core.themes', ['atom-light-ui', 'atom-dark-ui']) - waitsFor -> + waitsFor 'c', -> didChangeActiveThemesHandler.callCount is 1 runs -> @@ -123,22 +121,22 @@ describe "ThemeManager", -> runs -> expect(document.querySelectorAll('style[priority="1"]')).toHaveLength 2 - importPaths = themeManager.getImportPaths() + importPaths = atom.themes.getImportPaths() expect(importPaths.length).toBe 1 expect(importPaths[0]).toContain 'atom-dark-ui' it 'adds theme-* classes to the workspace for each active theme', -> atom.config.set('core.themes', ['atom-dark-ui', 'atom-dark-syntax']) workspaceElement = atom.views.getView(atom.workspace) - themeManager.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy() + atom.themes.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy() waitsForPromise -> - themeManager.activateThemes() + atom.themes.activateThemes() runs -> expect(workspaceElement).toHaveClass 'theme-atom-dark-ui' - themeManager.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy() + atom.themes.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy() atom.config.set('core.themes', ['theme-with-ui-variables', 'theme-with-syntax-variables']) waitsFor -> @@ -153,8 +151,8 @@ describe "ThemeManager", -> describe "when a theme fails to load", -> it "logs a warning", -> - spyOn(console, 'warn') - atom.packages.activatePackage('a-theme-that-will-not-be-found') + console.warn.reset() + atom.packages.activatePackage('a-theme-that-will-not-be-found').then((->), (->)) expect(console.warn.callCount).toBe 1 expect(console.warn.argsForCall[0][0]).toContain "Could not resolve 'a-theme-that-will-not-be-found'" @@ -167,27 +165,22 @@ describe "ThemeManager", -> it "synchronously loads css at the given path and installs a style tag for it in the head", -> atom.styles.onDidAddStyleElement styleElementAddedHandler = jasmine.createSpy("styleElementAddedHandler") - themeManager.onDidChangeStylesheets stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler") - themeManager.onDidAddStylesheet stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler") cssPath = atom.project.getDirectories()[0]?.resolve('css.css') lengthBefore = document.querySelectorAll('head style').length - themeManager.requireStylesheet(cssPath) + atom.themes.requireStylesheet(cssPath) expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1 expect(styleElementAddedHandler).toHaveBeenCalled() - expect(stylesheetAddedHandler).toHaveBeenCalled() - expect(stylesheetsChangedHandler).toHaveBeenCalled() element = document.querySelector('head style[source-path*="css.css"]') - expect(element.getAttribute('source-path')).toBe themeManager.stringToId(cssPath) + expect(element.getAttribute('source-path')).toBe atom.themes.stringToId(cssPath) expect(element.textContent).toBe fs.readFileSync(cssPath, 'utf8') - expect(element.sheet).toBe stylesheetAddedHandler.argsForCall[0][0] # doesn't append twice styleElementAddedHandler.reset() - themeManager.requireStylesheet(cssPath) + atom.themes.requireStylesheet(cssPath) expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1 expect(styleElementAddedHandler).not.toHaveBeenCalled() @@ -197,11 +190,11 @@ describe "ThemeManager", -> it "synchronously loads and parses less files at the given path and installs a style tag for it in the head", -> lessPath = atom.project.getDirectories()[0]?.resolve('sample.less') lengthBefore = document.querySelectorAll('head style').length - themeManager.requireStylesheet(lessPath) + atom.themes.requireStylesheet(lessPath) expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1 element = document.querySelector('head style[source-path*="sample.less"]') - expect(element.getAttribute('source-path')).toBe themeManager.stringToId(lessPath) + expect(element.getAttribute('source-path')).toBe atom.themes.stringToId(lessPath) expect(element.textContent).toBe """ #header { color: #4d926f; @@ -213,16 +206,16 @@ describe "ThemeManager", -> """ # doesn't append twice - themeManager.requireStylesheet(lessPath) + atom.themes.requireStylesheet(lessPath) expect(document.querySelectorAll('head style').length).toBe lengthBefore + 1 for styleElement in document.querySelectorAll('head style[id*="sample.less"]') styleElement.remove() it "supports requiring css and less stylesheets without an explicit extension", -> - themeManager.requireStylesheet path.join(__dirname, 'fixtures', 'css') - expect(document.querySelector('head style[source-path*="css.css"]').getAttribute('source-path')).toBe themeManager.stringToId(atom.project.getDirectories()[0]?.resolve('css.css')) - themeManager.requireStylesheet path.join(__dirname, 'fixtures', 'sample') - expect(document.querySelector('head style[source-path*="sample.less"]').getAttribute('source-path')).toBe themeManager.stringToId(atom.project.getDirectories()[0]?.resolve('sample.less')) + atom.themes.requireStylesheet path.join(__dirname, 'fixtures', 'css') + expect(document.querySelector('head style[source-path*="css.css"]').getAttribute('source-path')).toBe atom.themes.stringToId(atom.project.getDirectories()[0]?.resolve('css.css')) + atom.themes.requireStylesheet path.join(__dirname, 'fixtures', 'sample') + expect(document.querySelector('head style[source-path*="sample.less"]').getAttribute('source-path')).toBe atom.themes.stringToId(atom.project.getDirectories()[0]?.resolve('sample.less')) document.querySelector('head style[source-path*="css.css"]').remove() document.querySelector('head style[source-path*="sample.less"]').remove() @@ -231,24 +224,17 @@ describe "ThemeManager", -> cssPath = require.resolve('./fixtures/css.css') expect(getComputedStyle(document.body).fontWeight).not.toBe("bold") - disposable = themeManager.requireStylesheet(cssPath) + disposable = atom.themes.requireStylesheet(cssPath) expect(getComputedStyle(document.body).fontWeight).toBe("bold") atom.styles.onDidRemoveStyleElement styleElementRemovedHandler = jasmine.createSpy("styleElementRemovedHandler") - themeManager.onDidRemoveStylesheet stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler") - themeManager.onDidChangeStylesheets stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler") disposable.dispose() expect(getComputedStyle(document.body).fontWeight).not.toBe("bold") expect(styleElementRemovedHandler).toHaveBeenCalled() - expect(stylesheetRemovedHandler).toHaveBeenCalled() - stylesheet = stylesheetRemovedHandler.argsForCall[0][0] - expect(stylesheet instanceof CSSStyleSheet).toBe true - expect(stylesheet.cssRules[0].selectorText).toBe 'body' - expect(stylesheetsChangedHandler).toHaveBeenCalled() describe "base style sheet loading", -> workspaceElement = null @@ -258,10 +244,10 @@ describe "ThemeManager", -> workspaceElement.appendChild document.createElement('atom-text-editor') waitsForPromise -> - themeManager.activateThemes() + atom.themes.activateThemes() it "loads the correct values from the theme's ui-variables file", -> - themeManager.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy() + atom.themes.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy() atom.config.set('core.themes', ['theme-with-ui-variables', 'theme-with-syntax-variables']) waitsFor -> @@ -278,7 +264,7 @@ describe "ThemeManager", -> describe "when there is a theme with incomplete variables", -> it "loads the correct values from the fallback ui-variables", -> - themeManager.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy() + atom.themes.onDidChangeActiveThemes didChangeActiveThemesHandler = jasmine.createSpy() atom.config.set('core.themes', ['theme-with-incomplete-ui-variables', 'theme-with-syntax-variables']) waitsFor -> @@ -307,67 +293,52 @@ describe "ThemeManager", -> it "reloads it", -> [styleElementAddedHandler, styleElementRemovedHandler] = [] - [stylesheetRemovedHandler, stylesheetAddedHandler, stylesheetsChangedHandler] = [] waitsForPromise -> - themeManager.activateThemes() + atom.themes.activateThemes() runs -> atom.styles.onDidRemoveStyleElement styleElementRemovedHandler = jasmine.createSpy("styleElementRemovedHandler") atom.styles.onDidAddStyleElement styleElementAddedHandler = jasmine.createSpy("styleElementAddedHandler") - themeManager.onDidChangeStylesheets stylesheetsChangedHandler = jasmine.createSpy("stylesheetsChangedHandler") - themeManager.onDidRemoveStylesheet stylesheetRemovedHandler = jasmine.createSpy("stylesheetRemovedHandler") - themeManager.onDidAddStylesheet stylesheetAddedHandler = jasmine.createSpy("stylesheetAddedHandler") - spyOn(themeManager, 'loadUserStylesheet').andCallThrough() + spyOn(atom.themes, 'loadUserStylesheet').andCallThrough() expect(getComputedStyle(document.body).borderStyle).toBe 'dotted' fs.writeFileSync(userStylesheetPath, 'body {border-style: dashed}') waitsFor -> - themeManager.loadUserStylesheet.callCount is 1 + atom.themes.loadUserStylesheet.callCount is 1 runs -> expect(getComputedStyle(document.body).borderStyle).toBe 'dashed' expect(styleElementRemovedHandler).toHaveBeenCalled() expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain 'dotted' - expect(stylesheetRemovedHandler).toHaveBeenCalled() - expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dotted' expect(styleElementAddedHandler).toHaveBeenCalled() expect(styleElementAddedHandler.argsForCall[0][0].textContent).toContain 'dashed' - expect(stylesheetAddedHandler).toHaveBeenCalled() - expect(stylesheetAddedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed' - - expect(stylesheetsChangedHandler).toHaveBeenCalled() styleElementRemovedHandler.reset() - stylesheetRemovedHandler.reset() - stylesheetsChangedHandler.reset() fs.removeSync(userStylesheetPath) waitsFor -> - themeManager.loadUserStylesheet.callCount is 2 + atom.themes.loadUserStylesheet.callCount is 2 runs -> expect(styleElementRemovedHandler).toHaveBeenCalled() expect(styleElementRemovedHandler.argsForCall[0][0].textContent).toContain 'dashed' - expect(stylesheetRemovedHandler).toHaveBeenCalled() - expect(stylesheetRemovedHandler.argsForCall[0][0].cssRules[0].style.border).toBe 'dashed' expect(getComputedStyle(document.body).borderStyle).toBe 'none' - expect(stylesheetsChangedHandler).toHaveBeenCalled() describe "when there is an error reading the stylesheet", -> addErrorHandler = null beforeEach -> - themeManager.loadUserStylesheet() - spyOn(themeManager.lessCache, 'cssForFile').andCallFake -> + atom.themes.loadUserStylesheet() + spyOn(atom.themes.lessCache, 'cssForFile').andCallFake -> throw new Error('EACCES permission denied "styles.less"') atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy() it "creates an error notification and does not add the stylesheet", -> - themeManager.loadUserStylesheet() + atom.themes.loadUserStylesheet() expect(addErrorHandler).toHaveBeenCalled() note = addErrorHandler.mostRecentCall.args[0] expect(note.getType()).toBe 'error' @@ -381,11 +352,11 @@ describe "ThemeManager", -> spyOn(File::, 'on').andCallFake (event) -> if event.indexOf('contents-changed') > -1 throw new Error('Unable to watch path') - spyOn(themeManager, 'loadStylesheet').andReturn '' + spyOn(atom.themes, 'loadStylesheet').andReturn '' atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy() it "creates an error notification", -> - themeManager.loadUserStylesheet() + atom.themes.loadUserStylesheet() expect(addErrorHandler).toHaveBeenCalled() note = addErrorHandler.mostRecentCall.args[0] expect(note.getType()).toBe 'error' @@ -394,38 +365,35 @@ describe "ThemeManager", -> it "adds a notification when a theme's stylesheet is invalid", -> addErrorHandler = jasmine.createSpy() atom.notifications.onDidAddNotification(addErrorHandler) - expect(-> atom.packages.activatePackage('theme-with-invalid-styles')).not.toThrow() + expect(-> atom.packages.activatePackage('theme-with-invalid-styles').then((->), (->))).not.toThrow() expect(addErrorHandler.callCount).toBe 2 expect(addErrorHandler.argsForCall[1][0].message).toContain("Failed to activate the theme-with-invalid-styles theme") describe "when a non-existent theme is present in the config", -> beforeEach -> - spyOn(console, 'warn') + console.warn.reset() atom.config.set('core.themes', ['non-existent-dark-ui', 'non-existent-dark-syntax']) waitsForPromise -> - themeManager.activateThemes() + atom.themes.activateThemes() it 'uses the default dark UI and syntax themes and logs a warning', -> - activeThemeNames = themeManager.getActiveThemeNames() + activeThemeNames = atom.themes.getActiveThemeNames() expect(console.warn.callCount).toBe 2 expect(activeThemeNames.length).toBe(2) expect(activeThemeNames).toContain('atom-dark-ui') expect(activeThemeNames).toContain('atom-dark-syntax') describe "when in safe mode", -> - beforeEach -> - themeManager = new ThemeManager({packageManager: atom.packages, resourcePath, configDirPath, safeMode: true}) - describe 'when the enabled UI and syntax themes are bundled with Atom', -> beforeEach -> atom.config.set('core.themes', ['atom-light-ui', 'atom-dark-syntax']) waitsForPromise -> - themeManager.activateThemes() + atom.themes.activateThemes() it 'uses the enabled themes', -> - activeThemeNames = themeManager.getActiveThemeNames() + activeThemeNames = atom.themes.getActiveThemeNames() expect(activeThemeNames.length).toBe(2) expect(activeThemeNames).toContain('atom-light-ui') expect(activeThemeNames).toContain('atom-dark-syntax') @@ -435,10 +403,10 @@ describe "ThemeManager", -> atom.config.set('core.themes', ['installed-dark-ui', 'installed-dark-syntax']) waitsForPromise -> - themeManager.activateThemes() + atom.themes.activateThemes() it 'uses the default dark UI and syntax themes', -> - activeThemeNames = themeManager.getActiveThemeNames() + activeThemeNames = atom.themes.getActiveThemeNames() expect(activeThemeNames.length).toBe(2) expect(activeThemeNames).toContain('atom-dark-ui') expect(activeThemeNames).toContain('atom-dark-syntax') @@ -448,10 +416,10 @@ describe "ThemeManager", -> atom.config.set('core.themes', ['installed-dark-ui', 'atom-light-syntax']) waitsForPromise -> - themeManager.activateThemes() + atom.themes.activateThemes() it 'uses the default dark UI theme', -> - activeThemeNames = themeManager.getActiveThemeNames() + activeThemeNames = atom.themes.getActiveThemeNames() expect(activeThemeNames.length).toBe(2) expect(activeThemeNames).toContain('atom-dark-ui') expect(activeThemeNames).toContain('atom-light-syntax') @@ -461,10 +429,10 @@ describe "ThemeManager", -> atom.config.set('core.themes', ['atom-light-ui', 'installed-dark-syntax']) waitsForPromise -> - themeManager.activateThemes() + atom.themes.activateThemes() it 'uses the default dark syntax theme', -> - activeThemeNames = themeManager.getActiveThemeNames() + activeThemeNames = atom.themes.getActiveThemeNames() expect(activeThemeNames.length).toBe(2) expect(activeThemeNames).toContain('atom-light-ui') expect(activeThemeNames).toContain('atom-dark-syntax') diff --git a/spec/workspace-spec.coffee b/spec/workspace-spec.coffee index a7d562533..c60508640 100644 --- a/spec/workspace-spec.coffee +++ b/spec/workspace-spec.coffee @@ -6,7 +6,6 @@ platform = require './spec-helper-platform' _ = require 'underscore-plus' fstream = require 'fstream' fs = require 'fs-plus' -Grim = require 'grim' describe "Workspace", -> workspace = null @@ -383,21 +382,6 @@ describe "Workspace", -> runs -> expect(newEditorHandler.argsForCall[0][0].textEditor).toBe editor - it "records a deprecation warning on the appropriate package if the item has a ::getUri method instead of ::getURI", -> - jasmine.snapshotDeprecations() - - waitsForPromise -> atom.packages.activatePackage('package-with-deprecated-pane-item-method') - - waitsForPromise -> - atom.workspace.open("test") - - runs -> - deprecations = Grim.getDeprecations() - expect(deprecations.length).toBe 1 - expect(deprecations[0].message).toBe "Pane item with class `TestItem` should implement `::getURI` instead of `::getUri`." - expect(deprecations[0].getStacks()[0].metadata.packageName).toBe "package-with-deprecated-pane-item-method" - jasmine.restoreDeprecationsSnapshot() - describe "when there is an error opening the file", -> notificationSpy = null beforeEach -> @@ -682,7 +666,7 @@ describe "Workspace", -> it "updates the title to contain the project's path", -> document.title = null - workspace2 = atom.workspace.testSerialization() + workspace2 = Workspace.deserialize(atom.workspace.serialize()) item = atom.workspace.getActivePaneItem() expect(document.title).toBe "#{item.getTitle()} - #{atom.project.getPaths()[0]} - Atom" workspace2.destroy() diff --git a/src/atom.coffee b/src/atom.coffee index 3b4d8b6a6..59d7765ac 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -6,7 +6,7 @@ remote = require 'remote' shell = require 'shell' _ = require 'underscore-plus' -{deprecate, includeDeprecatedAPIs} = require 'grim' +{deprecate} = require 'grim' {CompositeDisposable, Emitter} = require 'event-kit' fs = require 'fs-plus' {mapSourcePosition} = require 'source-map-support' @@ -32,22 +32,6 @@ class Atom extends Model startTime = Date.now() atom = @deserialize(@loadState(mode)) ? new this({mode, @version}) atom.deserializeTimings.atom = Date.now() - startTime - - if includeDeprecatedAPIs - serviceHubDeprecationMessage = """ - atom.services is no longer available. To register service providers and - consumers, use the `providedServices` and `consumedServices` fields in - your package's package.json. - """ - - Object.defineProperty atom, 'services', - get: -> - deprecate(serviceHubDeprecationMessage) - atom.packages.serviceHub - set: (newValue) -> - deprecate(serviceHubDeprecationMessage) - atom.packages.serviceHub = newValue - atom # Deserializes the Atom environment from a state object @@ -196,7 +180,6 @@ class Atom extends Model @openDevTools() @executeJavaScriptInDevTools('DevToolsAPI.showConsole()') - @emit 'uncaught-error', arguments... if includeDeprecatedAPIs @emitter.emit 'did-throw-error', {message, url, line, column, originalError} @disposables?.dispose() @@ -235,10 +218,6 @@ class Atom extends Model @config = new Config({configDirPath, resourcePath}) @keymaps = new KeymapManager({configDirPath, resourcePath}) - - if includeDeprecatedAPIs - @keymap = @keymaps # Deprecated - @keymaps.subscribeToFileReadFailure() @tooltips = new TooltipManager @notifications = new NotificationManager @@ -252,14 +231,7 @@ class Atom extends Model @contextMenu = new ContextMenuManager({resourcePath, devMode}) @menu = new MenuManager({resourcePath}) @clipboard = new Clipboard() - @grammars = @deserializers.deserialize(@state.grammars ? @state.syntax) ? new GrammarRegistry() - - if includeDeprecatedAPIs - Object.defineProperty this, 'syntax', get: -> - deprecate "The atom.syntax global is deprecated. Use atom.grammars instead." - @grammars - @disposables.add @packages.onDidActivateInitialPackages => @watchThemes() Project = require './project' @@ -485,10 +457,6 @@ class Atom extends Model isMaximized: -> @getCurrentWindow().isMaximized() - isMaximixed: -> - deprecate "Use atom.isMaximized() instead" - @isMaximized() - maximize: -> ipc.send('call-window-method', 'maximize') @@ -605,9 +573,11 @@ class Atom extends Model {safeMode} = @getLoadSettings() CommandInstaller = require './command-installer' - CommandInstaller.installAtomCommand false, (error) -> + + commandInstaller = new CommandInstaller(@getVersion()) + commandInstaller.installAtomCommand false, (error) -> console.warn error.message if error? - CommandInstaller.installApmCommand false, (error) -> + commandInstaller.installApmCommand false, (error) -> console.warn error.message if error? @loadConfig() @@ -748,11 +718,9 @@ class Atom extends Model startTime = Date.now() @workspace = Workspace.deserialize(@state.workspace) ? new Workspace - - workspaceElement = @views.getView(@workspace) - @deserializeTimings.workspace = Date.now() - startTime + workspaceElement = @views.getView(@workspace) @keymaps.defaultTarget = workspaceElement document.querySelector(@workspaceParentSelectorctor).appendChild(workspaceElement) @@ -877,12 +845,3 @@ class Atom extends Model Promise.prototype.done = (callback) -> deprecate("Atom now uses ES6 Promises instead of Q. Call promise.then instead of promise.done") @then(callback) - -if includeDeprecatedAPIs - # Deprecated: Callers should be converted to use atom.deserializers - Atom::registerRepresentationClass = -> - deprecate("Callers should be converted to use atom.deserializers") - - # Deprecated: Callers should be converted to use atom.deserializers - Atom::registerRepresentationClasses = -> - deprecate("Callers should be converted to use atom.deserializers") diff --git a/src/browser/application-menu.coffee b/src/browser/application-menu.coffee index 74da80e43..27b9df8e1 100644 --- a/src/browser/application-menu.coffee +++ b/src/browser/application-menu.coffee @@ -103,6 +103,8 @@ class ApplicationMenu downloadingUpdateItem.visible = false installUpdateItem.visible = false + return if @autoUpdateManager.isDisabled() + switch state when 'idle', 'error', 'no-update-available' checkForUpdateItem.visible = true @@ -117,10 +119,9 @@ class ApplicationMenu # # Returns an Array of menu item Objects. getDefaultTemplate: -> - [ + template = [ label: "Atom" submenu: [ - {label: "Check for Update", metadata: {autoUpdate: true}} {label: 'Reload', accelerator: 'Command+R', click: => @focusedWindow()?.reload()} {label: 'Close Window', accelerator: 'Command+Shift+W', click: => @focusedWindow()?.close()} {label: 'Toggle Dev Tools', accelerator: 'Command+Alt+I', click: => @focusedWindow()?.toggleDevTools()} @@ -128,6 +129,10 @@ class ApplicationMenu ] ] + # Add `Check for Update` button if autoUpdateManager is enabled. + template[0].submenu.unshift({label: "Check for Update", metadata: {autoUpdate: true}}) unless @autoUpdateManager.isDisabled() + template + focusedWindow: -> _.find global.atomApplication.windows, (atomWindow) -> atomWindow.isFocused() diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index 226d87d07..53af171ea 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -72,7 +72,8 @@ class AtomApplication @pidsToOpenWindows = {} @windows = [] - @autoUpdateManager = new AutoUpdateManager(@version, options.test) + disableAutoUpdate = require(path.join(@resourcePath, 'package.json'))._disableAutoUpdate ? false + @autoUpdateManager = new AutoUpdateManager(@version, options.test, disableAutoUpdate) @applicationMenu = new ApplicationMenu(@version, @autoUpdateManager) @atomProtocolHandler = new AtomProtocolHandler(@resourcePath, @safeMode) diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index a2c239789..ca711eb83 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -15,7 +15,7 @@ module.exports = class AutoUpdateManager _.extend @prototype, EventEmitter.prototype - constructor: (@version, @testMode) -> + constructor: (@version, @testMode, @disabled) -> @state = IdleState if process.platform is 'win32' # Squirrel for Windows can't handle query params @@ -52,8 +52,9 @@ class AutoUpdateManager @setState(UpdateAvailableState) @emitUpdateAvailableEvent(@getWindows()...) - # Only released versions should check for updates. - @scheduleUpdateCheck() unless /\w{7}/.test(@version) + # Only check for updates periodically if enabled and running in release + # version. + @scheduleUpdateCheck() unless /\w{7}/.test(@version) or @disabled switch process.platform when 'win32' @@ -61,6 +62,9 @@ class AutoUpdateManager when 'linux' @setState(UnsupportedState) + isDisabled: -> + @disabled + emitUpdateAvailableEvent: (windows...) -> return unless @releaseVersion? for atomWindow in windows diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 6912391fa..f0709037d 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -16,8 +16,8 @@ start = -> setupCompileCache() return if handleStartupEventWithSquirrel() - # NB: This prevents Win10 from showing dupe items in the taskbar - app.setAppUserModelId('com.squirrel.atom.atom') + # NB: This prevents Win10 from showing dupe items in the taskbar + app.setAppUserModelId('com.squirrel.atom.atom') args = parseCommandLine() diff --git a/src/command-installer.coffee b/src/command-installer.coffee index afd5000c1..729aef84c 100644 --- a/src/command-installer.coffee +++ b/src/command-installer.coffee @@ -25,9 +25,15 @@ symlinkCommandWithPrivilegeSync = (sourcePath, destinationPath) -> throw new Error("Failed to symlink '#{sourcePath}' to '#{destinationPath}'") module.exports = +class CommandInstaller + constructor: (@appVersion) -> + getInstallDirectory: -> "/usr/local/bin" + getResourcesDirectory: -> + process.resourcesPath + installShellCommandsInteractively: -> showErrorDialog = (error) -> atom.confirm @@ -47,17 +53,26 @@ module.exports = detailedMessage: "The shell commands `atom` and `apm` are installed." installAtomCommand: (askForPrivilege, callback) -> - commandPath = path.join(process.resourcesPath, 'app', 'atom.sh') - @createSymlink commandPath, askForPrivilege, callback + programName = if @appVersion.includes("beta") + "atom-beta" + else + "atom" + + commandPath = path.join(@getResourcesDirectory(), 'app', 'atom.sh') + @createSymlink commandPath, programName, askForPrivilege, callback installApmCommand: (askForPrivilege, callback) -> - commandPath = path.join(process.resourcesPath, 'app', 'apm', 'node_modules', '.bin', 'apm') - @createSymlink commandPath, askForPrivilege, callback + programName = if @appVersion.includes("beta") + "apm-beta" + else + "apm" - createSymlink: (commandPath, askForPrivilege, callback) -> + commandPath = path.join(@getResourcesDirectory(), 'app', 'apm', 'node_modules', '.bin', 'apm') + @createSymlink commandPath, programName, askForPrivilege, callback + + createSymlink: (commandPath, commandName, askForPrivilege, callback) -> return unless process.platform is 'darwin' - commandName = path.basename(commandPath, path.extname(commandPath)) destinationPath = path.join(@getInstallDirectory(), commandName) fs.readlink destinationPath, (error, realpath) -> @@ -70,6 +85,7 @@ module.exports = try error = null symlinkCommandWithPrivilegeSync(commandPath, destinationPath) - catch error + catch err + error = err callback?(error) diff --git a/src/config.coffee b/src/config.coffee index 15aa664bb..7b9266f7e 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -5,7 +5,6 @@ CSON = require 'season' path = require 'path' async = require 'async' pathWatcher = require 'pathwatcher' -Grim = require 'grim' Color = require './color' ScopedPropertyStore = require 'scoped-property-store' @@ -387,21 +386,9 @@ class Config observe: -> if arguments.length is 2 [keyPath, callback] = arguments - else if Grim.includeDeprecatedAPIs and arguments.length is 3 and (_.isArray(arguments[0]) or arguments[0] instanceof ScopeDescriptor) - Grim.deprecate """ - Passing a scope descriptor as the first argument to Config::observe is deprecated. - Pass a `scope` in an options hash as the third argument instead. - """ - [scopeDescriptor, keyPath, callback] = arguments else if arguments.length is 3 and (_.isString(arguments[0]) and _.isObject(arguments[1])) [keyPath, options, callback] = arguments scopeDescriptor = options.scope - if Grim.includeDeprecatedAPIs and options.callNow? - Grim.deprecate """ - Config::observe no longer takes a `callNow` option. Use ::onDidChange instead. - Note that ::onDidChange passes its callback different arguments. - See https://atom.io/docs/api/latest/Config - """ else console.error 'An unsupported form of Config::observe is being used. See https://atom.io/docs/api/latest/Config for details' return @@ -435,12 +422,6 @@ class Config [callback] = arguments else if arguments.length is 2 [keyPath, callback] = arguments - else if Grim.includeDeprecatedAPIs and _.isArray(arguments[0]) or arguments[0] instanceof ScopeDescriptor - Grim.deprecate """ - Passing a scope descriptor as the first argument to Config::onDidChange is deprecated. - Pass a `scope` in an options hash as the third argument instead. - """ - [scopeDescriptor, keyPath, callback] = arguments else [keyPath, options, callback] = arguments scopeDescriptor = options.scope @@ -514,12 +495,6 @@ class Config if typeof arguments[0] is 'string' or not arguments[0]? [keyPath, options] = arguments {scope} = options - else if Grim.includeDeprecatedAPIs - Grim.deprecate """ - Passing a scope descriptor as the first argument to Config::get is deprecated. - Pass a `scope` in an options hash as the final argument instead. - """ - [scope, keyPath] = arguments else [keyPath] = arguments @@ -594,18 +569,10 @@ class Config # * `true` if the value was set. # * `false` if the value was not able to be coerced to the type specified in the setting's schema. set: -> - if Grim.includeDeprecatedAPIs and arguments[0]?[0] is '.' - Grim.deprecate """ - Passing a scope selector as the first argument to Config::set is deprecated. - Pass a `scopeSelector` in an options hash as the final argument instead. - """ - [scopeSelector, keyPath, value] = arguments - shouldSave = true - else - [keyPath, value, options] = arguments - scopeSelector = options?.scopeSelector - source = options?.source - shouldSave = options?.save ? true + [keyPath, value, options] = arguments + scopeSelector = options?.scopeSelector + source = options?.source + shouldSave = options?.save ? true if source and not scopeSelector throw new Error("::set with a 'source' and no 'sourceSelector' is not yet implemented!") @@ -633,16 +600,7 @@ class Config # * `scopeSelector` (optional) {String}. See {::set} # * `source` (optional) {String}. See {::set} unset: (keyPath, options) -> - if Grim.includeDeprecatedAPIs and typeof options is 'string' - Grim.deprecate """ - Passing a scope selector as the first argument to Config::unset is deprecated. - Pass a `scopeSelector` in an options hash as the second argument instead. - """ - scopeSelector = keyPath - keyPath = options - else - {scopeSelector, source} = options ? {} - + {scopeSelector, source} = options ? {} source ?= @getUserConfigPath() if scopeSelector? @@ -1206,71 +1164,3 @@ withoutEmptyObjects = (object) -> else resultObject = object resultObject - -# TODO remove in 1.0 API -Config::unobserve = (keyPath) -> - Grim.deprecate 'Config::unobserve no longer does anything. Call `.dispose()` on the object returned by Config::observe instead.' - -if Grim.includeDeprecatedAPIs - EmitterMixin = require('emissary').Emitter - EmitterMixin.includeInto(Config) - - Config::restoreDefault = (scopeSelector, keyPath) -> - Grim.deprecate("Use ::unset instead.") - @unset(scopeSelector, keyPath) - @get(keyPath) - - Config::getDefault = -> - Grim.deprecate("Use `::get(keyPath, {scope, excludeSources: [atom.config.getUserConfigPath()]})` instead") - if arguments.length is 1 - [keyPath] = arguments - else - [scopeSelector, keyPath] = arguments - scope = [scopeSelector] - @get(keyPath, {scope, excludeSources: [@getUserConfigPath()]}) - - Config::isDefault = -> - Grim.deprecate("Use `not ::get(keyPath, {scope, sources: [atom.config.getUserConfigPath()]})?` instead") - if arguments.length is 1 - [keyPath] = arguments - else - [scopeSelector, keyPath] = arguments - scope = [scopeSelector] - not @get(keyPath, {scope, sources: [@getUserConfigPath()]})? - - Config::getSettings = -> - Grim.deprecate "Use ::get(keyPath) instead" - _.deepExtend({}, @settings, @defaultSettings) - - Config::getInt = (keyPath) -> - Grim.deprecate '''Config::getInt is no longer necessary. Use ::get instead. - Make sure the config option you are accessing has specified an `integer` - schema. See the schema section of - https://atom.io/docs/api/latest/Config for more info.''' - parseInt(@get(keyPath)) - - Config::getPositiveInt = (keyPath, defaultValue=0) -> - Grim.deprecate '''Config::getPositiveInt is no longer necessary. Use ::get instead. - Make sure the config option you are accessing has specified an `integer` - schema with `minimum: 1`. See the schema section of - https://atom.io/docs/api/latest/Config for more info.''' - Math.max(@getInt(keyPath), 0) or defaultValue - - Config::toggle = (keyPath) -> - Grim.deprecate 'Config::toggle is no longer supported. Please remove from your code.' - @set(keyPath, not @get(keyPath)) - - Config::addScopedSettings = (source, selector, value, options) -> - Grim.deprecate("Use ::set instead") - settingsBySelector = {} - settingsBySelector[selector] = value - disposable = @scopedSettingsStore.addProperties(source, settingsBySelector, options) - @emitChangeEvent() - new Disposable => - disposable.dispose() - @emitChangeEvent() - - Config::settingsForScopeDescriptor = (scopeDescriptor, keyPath) -> - Grim.deprecate("Use Config::getAll instead") - entries = @getAll(null, scope: scopeDescriptor) - value for {value} in entries when _.valueForKeyPath(value, keyPath)? diff --git a/src/context-menu-manager.coffee b/src/context-menu-manager.coffee index 0258fedc7..0ca005d8a 100644 --- a/src/context-menu-manager.coffee +++ b/src/context-menu-manager.coffee @@ -4,7 +4,6 @@ CSON = require 'season' fs = require 'fs-plus' {calculateSpecificity, validateSelector} = require 'clear-cut' {Disposable} = require 'event-kit' -Grim = require 'grim' MenuHelpers = require './menu-helpers' platformContextMenu = require('../package.json')?._atomMenu?['context-menu'] @@ -83,25 +82,25 @@ class ContextMenuManager # # * `itemsBySelector` An {Object} whose keys are CSS selectors and whose # values are {Array}s of item {Object}s containing the following keys: - # * `label` (Optional) A {String} containing the menu item's label. - # * `command` (Optional) A {String} containing the command to invoke on the + # * `label` (optional) A {String} containing the menu item's label. + # * `command` (optional) A {String} containing the command to invoke on the # target of the right click that invoked the context menu. - # * `enabled` (Optional) A {Boolean} indicating whether the menu item + # * `enabled` (optional) A {Boolean} indicating whether the menu item # should be clickable. Disabled menu items typically appear grayed out. # Defaults to `true`. - # * `submenu` (Optional) An {Array} of additional items. - # * `type` (Optional) If you want to create a separator, provide an item + # * `submenu` (optional) An {Array} of additional items. + # * `type` (optional) If you want to create a separator, provide an item # with `type: 'separator'` and no other keys. - # * `visible` (Optional) A {Boolean} indicating whether the menu item + # * `visible` (optional) A {Boolean} indicating whether the menu item # should appear in the menu. Defaults to `true`. - # * `created` (Optional) A {Function} that is called on the item each time a + # * `created` (optional) A {Function} that is called on the item each time a # context menu is created via a right click. You can assign properties to # `this` to dynamically compute the command, label, etc. This method is # actually called on a clone of the original item template to prevent state # from leaking across context menu deployments. Called with the following # argument: # * `event` The click event that deployed the context menu. - # * `shouldDisplay` (Optional) A {Function} that is called to determine + # * `shouldDisplay` (optional) A {Function} that is called to determine # whether to display this item on a given context menu deployment. Called # with the following argument: # * `event` The click event that deployed the context menu. @@ -109,27 +108,6 @@ class ContextMenuManager # Returns a {Disposable} on which `.dispose()` can be called to remove the # added menu items. add: (itemsBySelector) -> - if Grim.includeDeprecatedAPIs - # Detect deprecated file path as first argument - if itemsBySelector? and typeof itemsBySelector isnt 'object' - Grim.deprecate """ - `ContextMenuManager::add` has changed to take a single object as its - argument. Please see - https://atom.io/docs/api/latest/ContextMenuManager#context-menu-cson-format for more info. - """ - itemsBySelector = arguments[1] - devMode = arguments[2]?.devMode - - # Detect deprecated format for items object - for key, value of itemsBySelector - unless _.isArray(value) - Grim.deprecate """ - `ContextMenuManager::add` has changed to take a single object as its - argument. Please see - https://atom.io/docs/api/latest/ContextMenuManager#context-menu-cson-format for more info. - """ - itemsBySelector = @convertLegacyItemsBySelector(itemsBySelector, devMode) - addedItemSets = [] for selector, items of itemsBySelector diff --git a/src/cursor.coffee b/src/cursor.coffee index 443e81f2c..84396448c 100644 --- a/src/cursor.coffee +++ b/src/cursor.coffee @@ -1,7 +1,6 @@ {Point, Range} = require 'text-buffer' {Emitter} = require 'event-kit' _ = require 'underscore-plus' -Grim = require 'grim' Model = require './model' # Extended: The `Cursor` class represents the little blinking line identifying @@ -62,18 +61,6 @@ class Cursor extends Model onDidChangeVisibility: (callback) -> @emitter.on 'did-change-visibility', callback - on: (eventName) -> - return unless Grim.includeDeprecatedAPIs - - switch eventName - when 'moved' - Grim.deprecate("Use Cursor::onDidChangePosition instead") - when 'destroyed' - Grim.deprecate("Use Cursor::onDidDestroy instead") - else - Grim.deprecate("::on is no longer supported. Use the event subscription methods instead") - super - ### Section: Managing Cursor Position ### @@ -578,7 +565,6 @@ class Cursor extends Model setVisible: (visible) -> if @visible isnt visible @visible = visible - @emit 'visibility-changed', @visible if Grim.includeDeprecatedAPIs @emitter.emit 'did-change-visibility', @visible # Public: Returns the visibility of the cursor. @@ -698,12 +684,3 @@ class Cursor extends Model position = range.start stop() position - -if Grim.includeDeprecatedAPIs - Cursor::getScopes = -> - Grim.deprecate 'Use Cursor::getScopeDescriptor() instead' - @getScopeDescriptor().getScopesArray() - - Cursor::getMoveNextWordBoundaryBufferPosition = (options) -> - Grim.deprecate 'Use `::getNextWordBoundaryBufferPosition(options)` instead' - @getNextWordBoundaryBufferPosition(options) diff --git a/src/decoration.coffee b/src/decoration.coffee index d8f0b5edf..154900ce5 100644 --- a/src/decoration.coffee +++ b/src/decoration.coffee @@ -1,6 +1,5 @@ _ = require 'underscore-plus' {Emitter} = require 'event-kit' -Grim = require 'grim' idCounter = 0 nextId = -> idCounter++ @@ -82,7 +81,6 @@ class Decoration @markerDestroyDisposable.dispose() @markerDestroyDisposable = null @destroyed = true - @emit 'destroyed' if Grim.includeDeprecatedAPIs @emitter.emit 'did-destroy' @emitter.dispose() @@ -155,7 +153,6 @@ class Decoration @properties.id = @id if newProperties.type? @displayBuffer.decorationDidChangeType(this) - @emit 'updated', {oldParams: oldProperties, newParams: newProperties} if Grim.includeDeprecatedAPIs @emitter.emit 'did-change-properties', {oldProperties, newProperties} ### @@ -175,34 +172,8 @@ class Decoration flashObject = {class: klass, duration} @flashQueue ?= [] @flashQueue.push(flashObject) - @emit 'flash' if Grim.includeDeprecatedAPIs @emitter.emit 'did-flash' consumeNextFlash: -> return @flashQueue.shift() if @flashQueue?.length > 0 null - -if Grim.includeDeprecatedAPIs - EmitterMixin = require('emissary').Emitter - EmitterMixin.includeInto(Decoration) - - Decoration::on = (eventName) -> - switch eventName - when 'updated' - Grim.deprecate 'Use Decoration::onDidChangeProperties instead' - when 'destroyed' - Grim.deprecate 'Use Decoration::onDidDestroy instead' - when 'flash' - Grim.deprecate 'Use Decoration::onDidFlash instead' - else - Grim.deprecate 'Decoration::on is deprecated. Use event subscription methods instead.' - - EmitterMixin::on.apply(this, arguments) - - Decoration::getParams = -> - Grim.deprecate 'Use Decoration::getProperties instead' - @getProperties() - - Decoration::update = (newProperties) -> - Grim.deprecate 'Use Decoration::setProperties instead' - @setProperties(newProperties) diff --git a/src/delegated-listener.js b/src/delegated-listener.js index 5a28069fa..b24384ba2 100644 --- a/src/delegated-listener.js +++ b/src/delegated-listener.js @@ -5,7 +5,7 @@ function listen (element, eventName, selector, handler) { var innerHandler = function (event) { if (selector) { var currentTarget = event.target - while (true) { + while (currentTarget) { if (currentTarget.matches && currentTarget.matches(selector)) { handler({ type: event.type, diff --git a/src/deserializer-manager.coffee b/src/deserializer-manager.coffee index fab84539c..62c7e4401 100644 --- a/src/deserializer-manager.coffee +++ b/src/deserializer-manager.coffee @@ -1,5 +1,4 @@ {Disposable} = require 'event-kit' -Grim = require 'grim' # Extended: Manages the deserializers used for serialized state # @@ -60,9 +59,3 @@ class DeserializerManager name = state.get?('deserializer') ? state.deserializer @deserializers[name] - -if Grim.includeDeprecatedAPIs - DeserializerManager::remove = (classes...) -> - Grim.deprecate("Call .dispose() on the Disposable return from ::add instead") - delete @deserializers[name] for {name} in classes - return diff --git a/src/display-buffer.coffee b/src/display-buffer.coffee index 5d5628689..27d1b44cf 100644 --- a/src/display-buffer.coffee +++ b/src/display-buffer.coffee @@ -1,8 +1,6 @@ _ = require 'underscore-plus' -Serializable = require 'serializable' {CompositeDisposable, Emitter} = require 'event-kit' {Point, Range} = require 'text-buffer' -Grim = require 'grim' TokenizedBuffer = require './tokenized-buffer' RowMap = require './row-map' Fold = require './fold' @@ -18,12 +16,20 @@ class BufferToScreenConversionError extends Error module.exports = class DisplayBuffer extends Model - Serializable.includeInto(this) - verticalScrollMargin: 2 horizontalScrollMargin: 6 scopedCharacterWidthsChangeCount: 0 changeCount: 0 + softWrapped: null + editorWidthInChars: null + lineHeightInPixels: null + defaultCharWidth: null + height: null + width: null + + @deserialize: (state) -> + state.tokenizedBuffer = TokenizedBuffer.deserialize(state.tokenizedBuffer) + new this(state) constructor: ({tabLength, @editorWidthInChars, @tokenizedBuffer, buffer, ignoreInvisibles, @largeFileMode}={}) -> super @@ -83,23 +89,16 @@ class DisplayBuffer extends Model @updateWrappedScreenLines() if oldConfigSettings? and not _.isEqual(oldConfigSettings, @configSettings) - serializeParams: -> + serialize: -> + deserializer: 'DisplayBuffer' id: @id softWrapped: @isSoftWrapped() editorWidthInChars: @editorWidthInChars - scrollTop: @scrollTop - scrollLeft: @scrollLeft tokenizedBuffer: @tokenizedBuffer.serialize() largeFileMode: @largeFileMode - deserializeParams: (params) -> - params.tokenizedBuffer = TokenizedBuffer.deserialize(params.tokenizedBuffer) - params - copy: -> newDisplayBuffer = new DisplayBuffer({@buffer, tabLength: @getTabLength(), @largeFileMode}) - newDisplayBuffer.setScrollTop(@getScrollTop()) - newDisplayBuffer.setScrollLeft(@getScrollLeft()) for marker in @findMarkers(displayBufferId: @id) marker.copy(displayBufferId: newDisplayBuffer.id) @@ -126,19 +125,8 @@ class DisplayBuffer extends Model onDidChangeCharacterWidths: (callback) -> @emitter.on 'did-change-character-widths', callback - onDidChangeScrollTop: (callback) -> - @emitter.on 'did-change-scroll-top', callback - - onDidChangeScrollLeft: (callback) -> - @emitter.on 'did-change-scroll-left', callback - - observeScrollTop: (callback) -> - callback(@scrollTop) - @onDidChangeScrollTop(callback) - - observeScrollLeft: (callback) -> - callback(@scrollLeft) - @onDidChangeScrollLeft(callback) + onDidRequestAutoscroll: (callback) -> + @emitter.on 'did-request-autoscroll', callback observeDecorations: (callback) -> callback(decoration) for decoration in @getDecorations() @@ -157,11 +145,9 @@ class DisplayBuffer extends Model @emitter.on 'did-update-markers', callback emitDidChange: (eventProperties, refreshMarkers=true) -> - @emit 'changed', eventProperties if Grim.includeDeprecatedAPIs @emitter.emit 'did-change', eventProperties if refreshMarkers @refreshMarkerScreenPositions() - @emit 'markers-updated' if Grim.includeDeprecatedAPIs @emitter.emit 'did-update-markers' updateWrappedScreenLines: -> @@ -183,105 +169,24 @@ class DisplayBuffer extends Model setVerticalScrollMargin: (@verticalScrollMargin) -> @verticalScrollMargin - getVerticalScrollMarginInPixels: -> @getVerticalScrollMargin() * @getLineHeightInPixels() - getHorizontalScrollMargin: -> Math.min(@horizontalScrollMargin, Math.floor(((@getWidth() / @getDefaultCharWidth()) - 1) / 2)) setHorizontalScrollMargin: (@horizontalScrollMargin) -> @horizontalScrollMargin - getHorizontalScrollMarginInPixels: -> scrollMarginInPixels = @getHorizontalScrollMargin() * @getDefaultCharWidth() - - getHorizontalScrollbarHeight: -> @horizontalScrollbarHeight - setHorizontalScrollbarHeight: (@horizontalScrollbarHeight) -> @horizontalScrollbarHeight - - getVerticalScrollbarWidth: -> @verticalScrollbarWidth - setVerticalScrollbarWidth: (@verticalScrollbarWidth) -> @verticalScrollbarWidth - getHeight: -> - if @height? - @height - else - if @horizontallyScrollable() - @getScrollHeight() + @getHorizontalScrollbarHeight() - else - @getScrollHeight() + @height - setHeight: (@height) -> @height - - getClientHeight: (reentrant) -> - if @horizontallyScrollable(reentrant) - @getHeight() - @getHorizontalScrollbarHeight() - else - @getHeight() - - getClientWidth: (reentrant) -> - if @verticallyScrollable(reentrant) - @getWidth() - @getVerticalScrollbarWidth() - else - @getWidth() - - horizontallyScrollable: (reentrant) -> - return false unless @width? - return false if @isSoftWrapped() - if reentrant - @getScrollWidth() > @getWidth() - else - @getScrollWidth() > @getClientWidth(true) - - verticallyScrollable: (reentrant) -> - return false unless @height? - if reentrant - @getScrollHeight() > @getHeight() - else - @getScrollHeight() > @getClientHeight(true) + setHeight: (@height) -> + @height getWidth: -> - if @width? - @width - else - if @verticallyScrollable() - @getScrollWidth() + @getVerticalScrollbarWidth() - else - @getScrollWidth() + @width setWidth: (newWidth) -> oldWidth = @width @width = newWidth @updateWrappedScreenLines() if newWidth isnt oldWidth and @isSoftWrapped() - @setScrollTop(@getScrollTop()) # Ensure scrollTop is still valid in case horizontal scrollbar disappeared @width - getScrollTop: -> @scrollTop - setScrollTop: (scrollTop) -> - scrollTop = Math.round(Math.max(0, Math.min(@getMaxScrollTop(), scrollTop))) - unless scrollTop is @scrollTop - @scrollTop = scrollTop - @emitter.emit 'did-change-scroll-top', @scrollTop - @scrollTop - - getMaxScrollTop: -> - @getScrollHeight() - @getClientHeight() - - getScrollBottom: -> @scrollTop + @getClientHeight() - setScrollBottom: (scrollBottom) -> - @setScrollTop(scrollBottom - @getClientHeight()) - @getScrollBottom() - - getScrollLeft: -> @scrollLeft - setScrollLeft: (scrollLeft) -> - scrollLeft = Math.round(Math.max(0, Math.min(@getScrollWidth() - @getClientWidth(), scrollLeft))) - unless scrollLeft is @scrollLeft - @scrollLeft = scrollLeft - @emitter.emit 'did-change-scroll-left', @scrollLeft - @scrollLeft - - getMaxScrollLeft: -> - @getScrollWidth() - @getClientWidth() - - getScrollRight: -> @scrollLeft + @width - setScrollRight: (scrollRight) -> - @setScrollLeft(scrollRight - @width) - @getScrollRight() - getLineHeightInPixels: -> @lineHeightInPixels setLineHeightInPixels: (@lineHeightInPixels) -> @lineHeightInPixels @@ -289,7 +194,6 @@ class DisplayBuffer extends Model setDefaultCharWidth: (defaultCharWidth) -> if defaultCharWidth isnt @defaultCharWidth @defaultCharWidth = defaultCharWidth - @computeScrollWidth() defaultCharWidth getCursorWidth: -> 1 @@ -318,85 +222,14 @@ class DisplayBuffer extends Model @characterWidthsChanged() unless @batchingCharacterMeasurement characterWidthsChanged: -> - @computeScrollWidth() - @emit 'character-widths-changed', @scopedCharacterWidthsChangeCount if Grim.includeDeprecatedAPIs @emitter.emit 'did-change-character-widths', @scopedCharacterWidthsChangeCount clearScopedCharWidths: -> @charWidthsByScope = {} - getScrollHeight: -> - lineHeight = @getLineHeightInPixels() - return 0 unless lineHeight > 0 - - scrollHeight = @getLineCount() * lineHeight - if @height? and @configSettings.scrollPastEnd - scrollHeight = scrollHeight + @height - (lineHeight * 3) - - scrollHeight - - getScrollWidth: -> - @scrollWidth - - # Returns an {Array} of two numbers representing the first and the last visible rows. - getVisibleRowRange: -> - return [0, 0] unless @getLineHeightInPixels() > 0 - - startRow = Math.floor(@getScrollTop() / @getLineHeightInPixels()) - endRow = Math.ceil((@getScrollTop() + @getHeight()) / @getLineHeightInPixels()) - 1 - endRow = Math.min(@getLineCount(), endRow) - - [startRow, endRow] - - intersectsVisibleRowRange: (startRow, endRow) -> - [visibleStart, visibleEnd] = @getVisibleRowRange() - not (endRow <= visibleStart or visibleEnd <= startRow) - - selectionIntersectsVisibleRowRange: (selection) -> - {start, end} = selection.getScreenRange() - @intersectsVisibleRowRange(start.row, end.row + 1) - - scrollToScreenRange: (screenRange, options) -> - verticalScrollMarginInPixels = @getVerticalScrollMarginInPixels() - horizontalScrollMarginInPixels = @getHorizontalScrollMarginInPixels() - - {top, left} = @pixelRectForScreenRange(new Range(screenRange.start, screenRange.start)) - {top: endTop, left: endLeft, height: endHeight} = @pixelRectForScreenRange(new Range(screenRange.end, screenRange.end)) - bottom = endTop + endHeight - right = endLeft - - if options?.center - desiredScrollCenter = (top + bottom) / 2 - unless @getScrollTop() < desiredScrollCenter < @getScrollBottom() - desiredScrollTop = desiredScrollCenter - @getHeight() / 2 - desiredScrollBottom = desiredScrollCenter + @getHeight() / 2 - else - desiredScrollTop = top - verticalScrollMarginInPixels - desiredScrollBottom = bottom + verticalScrollMarginInPixels - - desiredScrollLeft = left - horizontalScrollMarginInPixels - desiredScrollRight = right + horizontalScrollMarginInPixels - - if options?.reversed ? true - if desiredScrollBottom > @getScrollBottom() - @setScrollBottom(desiredScrollBottom) - if desiredScrollTop < @getScrollTop() - @setScrollTop(desiredScrollTop) - - if desiredScrollRight > @getScrollRight() - @setScrollRight(desiredScrollRight) - if desiredScrollLeft < @getScrollLeft() - @setScrollLeft(desiredScrollLeft) - else - if desiredScrollTop < @getScrollTop() - @setScrollTop(desiredScrollTop) - if desiredScrollBottom > @getScrollBottom() - @setScrollBottom(desiredScrollBottom) - - if desiredScrollLeft < @getScrollLeft() - @setScrollLeft(desiredScrollLeft) - if desiredScrollRight > @getScrollRight() - @setScrollRight(desiredScrollRight) + scrollToScreenRange: (screenRange, options = {}) -> + scrollEvent = {screenRange, options} + @emitter.emit "did-request-autoscroll", scrollEvent scrollToScreenPosition: (screenPosition, options) -> @scrollToScreenRange(new Range(screenPosition, screenPosition), options) @@ -404,19 +237,6 @@ class DisplayBuffer extends Model scrollToBufferPosition: (bufferPosition, options) -> @scrollToScreenPosition(@screenPositionForBufferPosition(bufferPosition), options) - pixelRectForScreenRange: (screenRange) -> - if screenRange.end.row > screenRange.start.row - top = @pixelPositionForScreenPosition(screenRange.start).top - left = 0 - height = (screenRange.end.row - screenRange.start.row + 1) * @getLineHeightInPixels() - width = @getScrollWidth() - else - {top, left} = @pixelPositionForScreenPosition(screenRange.start, false) - height = @getLineHeightInPixels() - width = @pixelPositionForScreenPosition(screenRange.end, false).left - left - - {top, left, width, height} - # Retrieves the current tab length. # # Returns a {Number}. @@ -437,7 +257,6 @@ class DisplayBuffer extends Model @softWrapped = softWrapped @updateWrappedScreenLines() softWrapped = @isSoftWrapped() - @emit 'soft-wrap-changed', softWrapped if Grim.includeDeprecatedAPIs @emitter.emit 'did-change-soft-wrapped', softWrapped softWrapped else @@ -461,8 +280,7 @@ class DisplayBuffer extends Model # Returns the editor width in characters for soft wrap. getEditorWidthInChars: -> - width = @width ? @getScrollWidth() - width -= @getVerticalScrollbarWidth() + width = @getWidth() if width? and @defaultCharWidth > 0 Math.max(0, Math.floor(width / @defaultCharWidth)) else @@ -674,80 +492,6 @@ class DisplayBuffer extends Model end = @bufferPositionForScreenPosition(screenRange.end) new Range(start, end) - pixelRangeForScreenRange: (screenRange, clip=true) -> - {start, end} = Range.fromObject(screenRange) - {start: @pixelPositionForScreenPosition(start, clip), end: @pixelPositionForScreenPosition(end, clip)} - - pixelPositionForScreenPosition: (screenPosition, clip=true) -> - screenPosition = Point.fromObject(screenPosition) - screenPosition = @clipScreenPosition(screenPosition) if clip - - targetRow = screenPosition.row - targetColumn = screenPosition.column - defaultCharWidth = @defaultCharWidth - - top = targetRow * @lineHeightInPixels - left = 0 - column = 0 - - iterator = @tokenizedLineForScreenRow(targetRow).getTokenIterator() - while iterator.next() - charWidths = @getScopedCharWidths(iterator.getScopes()) - valueIndex = 0 - value = iterator.getText() - while valueIndex < value.length - if iterator.isPairedCharacter() - char = value - charLength = 2 - valueIndex += 2 - else - char = value[valueIndex] - charLength = 1 - valueIndex++ - - return {top, left} if column is targetColumn - left += charWidths[char] ? defaultCharWidth unless char is '\0' - column += charLength - {top, left} - - screenPositionForPixelPosition: (pixelPosition) -> - targetTop = pixelPosition.top - targetLeft = pixelPosition.left - defaultCharWidth = @defaultCharWidth - row = Math.floor(targetTop / @getLineHeightInPixels()) - targetLeft = 0 if row < 0 - targetLeft = Infinity if row > @getLastRow() - row = Math.min(row, @getLastRow()) - row = Math.max(0, row) - - left = 0 - column = 0 - - iterator = @tokenizedLineForScreenRow(row).getTokenIterator() - while iterator.next() - charWidths = @getScopedCharWidths(iterator.getScopes()) - value = iterator.getText() - valueIndex = 0 - while valueIndex < value.length - if iterator.isPairedCharacter() - char = value - charLength = 2 - valueIndex += 2 - else - char = value[valueIndex] - charLength = 1 - valueIndex++ - - charWidth = charWidths[char] ? defaultCharWidth - break if targetLeft <= left + (charWidth / 2) - left += charWidth - column += charLength - - new Point(row, column) - - pixelPositionForBufferPosition: (bufferPosition) -> - @pixelPositionForScreenPosition(@screenPositionForBufferPosition(bufferPosition)) - # Gets the number of screen lines. # # Returns a {Number}. @@ -999,7 +743,6 @@ class DisplayBuffer extends Model @decorationsByMarkerId[marker.id].push(decoration) @overlayDecorationsById[decoration.id] = decoration if decoration.isType('overlay') @decorationsById[decoration.id] = decoration - @emit 'decoration-added', decoration if Grim.includeDeprecatedAPIs @emitter.emit 'did-add-decoration', decoration decoration @@ -1011,7 +754,6 @@ class DisplayBuffer extends Model if index > -1 decorations.splice(index, 1) delete @decorationsById[decoration.id] - @emit 'decoration-removed', decoration if Grim.includeDeprecatedAPIs @emitter.emit 'did-remove-decoration', decoration delete @decorationsByMarkerId[marker.id] if decorations.length is 0 delete @overlayDecorationsById[decoration.id] @@ -1189,7 +931,6 @@ class DisplayBuffer extends Model @changeCount = @tokenizedBuffer.changeCount {start, end, delta, bufferChange} = tokenizedBufferChange @updateScreenLines(start, end + 1, delta, refreshMarkers: false) - @setScrollTop(Math.min(@getScrollTop(), @getMaxScrollTop())) if delta < 0 updateScreenLines: (startBufferRow, endBufferRow, bufferDelta=0, options={}) -> return if @largeFileMode @@ -1294,13 +1035,6 @@ class DisplayBuffer extends Model @longestScreenRow = screenRow @maxLineLength = length - @computeScrollWidth() if oldMaxLineLength isnt @maxLineLength - - computeScrollWidth: -> - @scrollWidth = @pixelPositionForScreenPosition([@longestScreenRow, @maxLineLength]).left - @scrollWidth += 1 unless @isSoftWrapped() - @setScrollLeft(Math.min(@getScrollLeft(), @getMaxScrollLeft())) - handleBufferMarkerCreated: (textBufferMarker) => if textBufferMarker.matchesParams(@getFoldMarkerAttributes()) fold = new Fold(this, textBufferMarker) @@ -1310,7 +1044,6 @@ class DisplayBuffer extends Model if marker = @getMarker(textBufferMarker.id) # The marker might have been removed in some other handler called before # this one. Only emit when the marker still exists. - @emit 'marker-created', marker if Grim.includeDeprecatedAPIs @emitter.emit 'did-create-marker', marker decorateFold: (fold) -> @@ -1338,58 +1071,3 @@ class DisplayBuffer extends Model atom.assert screenLinesCount is bufferLinesCount, "Display buffer line count out of sync with buffer", (error) -> error.metadata = {screenLinesCount, tokenizedLinesCount, bufferLinesCount} - -if Grim.includeDeprecatedAPIs - DisplayBuffer.properties - softWrapped: null - editorWidthInChars: null - lineHeightInPixels: null - defaultCharWidth: null - height: null - width: null - scrollTop: 0 - scrollLeft: 0 - scrollWidth: 0 - verticalScrollbarWidth: 15 - horizontalScrollbarHeight: 15 - - EmitterMixin = require('emissary').Emitter - - DisplayBuffer::on = (eventName) -> - switch eventName - when 'changed' - Grim.deprecate("Use DisplayBuffer::onDidChange instead") - when 'grammar-changed' - Grim.deprecate("Use DisplayBuffer::onDidChangeGrammar instead") - when 'soft-wrap-changed' - Grim.deprecate("Use DisplayBuffer::onDidChangeSoftWrap instead") - when 'character-widths-changed' - Grim.deprecate("Use DisplayBuffer::onDidChangeCharacterWidths instead") - when 'decoration-added' - Grim.deprecate("Use DisplayBuffer::onDidAddDecoration instead") - when 'decoration-removed' - Grim.deprecate("Use DisplayBuffer::onDidRemoveDecoration instead") - when 'decoration-changed' - Grim.deprecate("Use decoration.getMarker().onDidChange() instead") - when 'decoration-updated' - Grim.deprecate("Use Decoration::onDidChangeProperties instead") - when 'marker-created' - Grim.deprecate("Use Decoration::onDidCreateMarker instead") - when 'markers-updated' - Grim.deprecate("Use Decoration::onDidUpdateMarkers instead") - else - Grim.deprecate("DisplayBuffer::on is deprecated. Use event subscription methods instead.") - - EmitterMixin::on.apply(this, arguments) -else - DisplayBuffer::softWrapped = null - DisplayBuffer::editorWidthInChars = null - DisplayBuffer::lineHeightInPixels = null - DisplayBuffer::defaultCharWidth = null - DisplayBuffer::height = null - DisplayBuffer::width = null - DisplayBuffer::scrollTop = 0 - DisplayBuffer::scrollLeft = 0 - DisplayBuffer::scrollWidth = 0 - DisplayBuffer::verticalScrollbarWidth = 15 - DisplayBuffer::horizontalScrollbarHeight = 15 diff --git a/src/git-repository.coffee b/src/git-repository.coffee index 09ae42dcf..b3a404a6e 100644 --- a/src/git-repository.coffee +++ b/src/git-repository.coffee @@ -4,7 +4,6 @@ _ = require 'underscore-plus' {Emitter, Disposable, CompositeDisposable} = require 'event-kit' fs = require 'fs-plus' GitUtils = require 'git-utils' -{includeDeprecatedAPIs, deprecate} = require 'grim' Task = require './task' @@ -327,7 +326,6 @@ class GitRepository else delete @statuses[relativePath] if currentPathStatus isnt pathStatus - @emit 'status-changed', path, pathStatus if includeDeprecatedAPIs @emitter.emit 'did-change-status', {path, pathStatus} pathStatus @@ -496,23 +494,4 @@ class GitRepository submoduleRepo.upstream = submodules[submodulePath]?.upstream ? {ahead: 0, behind: 0} unless statusesUnchanged - @emit 'statuses-changed' if includeDeprecatedAPIs @emitter.emit 'did-change-statuses' - -if includeDeprecatedAPIs - EmitterMixin = require('emissary').Emitter - EmitterMixin.includeInto(GitRepository) - - GitRepository::on = (eventName) -> - switch eventName - when 'status-changed' - deprecate 'Use GitRepository::onDidChangeStatus instead' - when 'statuses-changed' - deprecate 'Use GitRepository::onDidChangeStatuses instead' - else - deprecate 'GitRepository::on is deprecated. Use event subscription methods instead.' - EmitterMixin::on.apply(this, arguments) - - GitRepository::getOriginUrl = (path) -> - deprecate 'Use ::getOriginURL instead.' - @getOriginURL(path) diff --git a/src/grammar-registry.coffee b/src/grammar-registry.coffee index 033595dad..109e719fc 100644 --- a/src/grammar-registry.coffee +++ b/src/grammar-registry.coffee @@ -1,6 +1,5 @@ _ = require 'underscore-plus' {Emitter} = require 'event-kit' -{includeDeprecatedAPIs, deprecate} = require 'grim' FirstMate = require 'first-mate' Token = require './token' fs = require 'fs-plus' @@ -136,37 +135,4 @@ class GrammarRegistry extends FirstMate.GrammarRegistry undefined clearObservers: -> - @off() if includeDeprecatedAPIs @emitter = new Emitter - -if includeDeprecatedAPIs - PropertyAccessors = require 'property-accessors' - PropertyAccessors.includeInto(GrammarRegistry) - - {Subscriber} = require 'emissary' - Subscriber.includeInto(GrammarRegistry) - - # Support old serialization - atom.deserializers.add(name: 'Syntax', deserialize: GrammarRegistry.deserialize) - - # Deprecated: Used by settings-view to display snippets for packages - GrammarRegistry::accessor 'propertyStore', -> - deprecate("Do not use this. Use a public method on Config") - atom.config.scopedSettingsStore - - GrammarRegistry::addProperties = (args...) -> - args.unshift(null) if args.length is 2 - deprecate 'Consider using atom.config.set() instead. A direct (but private) replacement is available at atom.config.addScopedSettings().' - atom.config.addScopedSettings(args...) - - GrammarRegistry::removeProperties = (name) -> - deprecate 'atom.config.addScopedSettings() now returns a disposable you can call .dispose() on' - atom.config.scopedSettingsStore.removeProperties(name) - - GrammarRegistry::getProperty = (scope, keyPath) -> - deprecate 'A direct (but private) replacement is available at atom.config.getRawScopedValue().' - atom.config.getRawScopedValue(scope, keyPath) - - GrammarRegistry::propertiesForScope = (scope, keyPath) -> - deprecate 'Use atom.config.getAll instead.' - atom.config.settingsForScopeDescriptor(scope, keyPath) diff --git a/src/keymap-extensions.coffee b/src/keymap-extensions.coffee index 580c6bbbf..da77f0156 100644 --- a/src/keymap-extensions.coffee +++ b/src/keymap-extensions.coffee @@ -2,7 +2,6 @@ fs = require 'fs-plus' path = require 'path' KeymapManager = require 'atom-keymap' CSON = require 'season' -Grim = require 'grim' bundledKeymaps = require('../package.json')?._atomKeymaps diff --git a/src/marker.coffee b/src/marker.coffee index fc60ec98f..16f644027 100644 --- a/src/marker.coffee +++ b/src/marker.coffee @@ -1,6 +1,5 @@ _ = require 'underscore-plus' {CompositeDisposable, Emitter} = require 'event-kit' -Grim = require 'grim' # Essential: Represents a buffer annotation that remains logically stationary # even as the buffer changes. This is used to represent cursors, folds, snippet @@ -335,7 +334,6 @@ class Marker destroyed: -> delete @displayBuffer.markers[@id] - @emit 'destroyed' if Grim.includeDeprecatedAPIs @emitter.emit 'did-destroy' @emitter.dispose() @@ -369,35 +367,4 @@ class Marker @oldTailScreenPosition = newTailScreenPosition @wasValid = isValid - @emit 'changed', changeEvent if Grim.includeDeprecatedAPIs @emitter.emit 'did-change', changeEvent - - getPixelRange: -> - @displayBuffer.pixelRangeForScreenRange(@getScreenRange(), false) - -if Grim.includeDeprecatedAPIs - EmitterMixin = require('emissary').Emitter - EmitterMixin.includeInto(Marker) - - Marker::on = (eventName) -> - switch eventName - when 'changed' - Grim.deprecate("Use Marker::onDidChange instead") - when 'destroyed' - Grim.deprecate("Use Marker::onDidDestroy instead") - else - Grim.deprecate("Marker::on is deprecated. Use documented event subscription methods instead.") - - EmitterMixin::on.apply(this, arguments) - - Marker::getAttributes = -> - Grim.deprecate 'Use Marker::getProperties instead' - @getProperties() - - Marker::setAttributes = (properties) -> - Grim.deprecate 'Use Marker::setProperties instead' - @setProperties(properties) - - Marker::matchesAttributes = (attributes) -> - Grim.deprecate 'Use Marker::matchesProperties instead' - @matchesProperties(attributes) diff --git a/src/model.coffee b/src/model.coffee index 7b38c0eef..94c06a76f 100644 --- a/src/model.coffee +++ b/src/model.coffee @@ -1,16 +1,7 @@ -Grim = require 'grim' -if Grim.includeDeprecatedAPIs - module.exports = require('theorist').Model - return - -PropertyAccessors = require 'property-accessors' - nextInstanceId = 1 module.exports = class Model - PropertyAccessors.includeInto(this) - @resetNextInstanceId: -> nextInstanceId = 1 alive: true @@ -20,9 +11,7 @@ class Model assignId: (id) -> @id ?= id ? nextInstanceId++ - - @::advisedAccessor 'id', - set: (id) -> nextInstanceId = id + 1 if id >= nextInstanceId + nextInstanceId = id + 1 if id >= nextInstanceId destroy: -> return unless @isAlive() diff --git a/src/package-manager.coffee b/src/package-manager.coffee index 0978a3e95..7e4a19470 100644 --- a/src/package-manager.coffee +++ b/src/package-manager.coffee @@ -3,7 +3,6 @@ path = require 'path' _ = require 'underscore-plus' {Emitter} = require 'event-kit' fs = require 'fs-plus' -Grim = require 'grim' ServiceHub = require 'service-hub' Package = require './package' @@ -327,18 +326,10 @@ class PackageManager # of the first package isn't skewed by being the first to require atom require '../exports/atom' - # TODO: remove after a few atom versions. - @uninstallAutocompletePlus() - packagePaths = @getAvailablePackagePaths() - - # TODO: remove after a few atom versions. - @migrateSublimeTabsSettings(packagePaths) - packagePaths = packagePaths.filter (packagePath) => not @isPackageDisabled(path.basename(packagePath)) packagePaths = _.uniq packagePaths, (packagePath) -> path.basename(packagePath) @loadPackage(packagePath) for packagePath in packagePaths - @emit 'loaded' if Grim.includeDeprecatedAPIs @emitter.emit 'did-load-initial-packages' loadPackage: (nameOrPath) -> @@ -354,7 +345,7 @@ class PackageManager @handleMetadataError(error, packagePath) return null - unless @isBundledPackage(metadata.name) or Grim.includeDeprecatedAPIs + unless @isBundledPackage(metadata.name) if @isDeprecatedPackage(metadata.name, metadata.version) console.warn "Could not load #{metadata.name}@#{metadata.version} because it uses deprecated APIs that have been removed." return null @@ -392,7 +383,6 @@ class PackageManager packages = @getLoadedPackagesForTypes(types) promises = promises.concat(activator.activatePackages(packages)) Promise.all(promises).then => - @emit 'activated' if Grim.includeDeprecatedAPIs @emitter.emit 'did-activate-initial-packages' # another type of package manager can handle other package types. @@ -455,35 +445,6 @@ class PackageManager message = "Failed to load the #{path.basename(packagePath)} package" atom.notifications.addError(message, {stack, detail, dismissable: true}) - # TODO: remove these autocomplete-plus specific helpers after a few versions. - uninstallAutocompletePlus: -> - packageDir = null - devDir = path.join("dev", "packages") - for packageDirPath in @packageDirPaths - if not packageDirPath.endsWith(devDir) - packageDir = packageDirPath - break - - if packageDir? - dirsToRemove = [ - path.join(packageDir, 'autocomplete-plus') - path.join(packageDir, 'autocomplete-atom-api') - path.join(packageDir, 'autocomplete-css') - path.join(packageDir, 'autocomplete-html') - path.join(packageDir, 'autocomplete-snippets') - ] - for dirToRemove in dirsToRemove - @uninstallDirectory(dirToRemove) - return - - # TODO: remove this after a few versions - migrateSublimeTabsSettings: (packagePaths) -> - return if Grim.includeDeprecatedAPIs - for packagePath in packagePaths when path.basename(packagePath) is 'sublime-tabs' - atom.config.removeAtKeyPath('core.disabledPackages', 'tree-view') - atom.config.removeAtKeyPath('core.disabledPackages', 'tabs') - return - uninstallDirectory: (directory) -> symlinkPromise = new Promise (resolve) -> fs.isSymbolicLink directory, (isSymLink) -> resolve(isSymLink) @@ -495,25 +456,3 @@ class PackageManager [isSymLink, isDir] = values if not isSymLink and isDir fs.remove directory, -> - -if Grim.includeDeprecatedAPIs - EmitterMixin = require('emissary').Emitter - EmitterMixin.includeInto(PackageManager) - - PackageManager::on = (eventName) -> - switch eventName - when 'loaded' - Grim.deprecate 'Use PackageManager::onDidLoadInitialPackages instead' - when 'activated' - Grim.deprecate 'Use PackageManager::onDidActivateInitialPackages instead' - else - Grim.deprecate 'PackageManager::on is deprecated. Use event subscription methods instead.' - EmitterMixin::on.apply(this, arguments) - - PackageManager::onDidLoadAll = (callback) -> - Grim.deprecate("Use `::onDidLoadInitialPackages` instead.") - @onDidLoadInitialPackages(callback) - - PackageManager::onDidActivateAll = (callback) -> - Grim.deprecate("Use `::onDidActivateInitialPackages` instead.") - @onDidActivateInitialPackages(callback) diff --git a/src/package.coffee b/src/package.coffee index f10ce51ba..0163f8bcd 100644 --- a/src/package.coffee +++ b/src/package.coffee @@ -6,7 +6,6 @@ async = require 'async' CSON = require 'season' fs = require 'fs-plus' {Emitter, CompositeDisposable} = require 'event-kit' -{includeDeprecatedAPIs, deprecate} = require 'grim' ModuleCache = require './module-cache' ScopedProperties = require './scoped-properties' @@ -14,7 +13,7 @@ BufferedProcess = require './buffered-process' packagesCache = require('../package.json')?._atomPackages ? {} -# Loads and activates a package's main module and resources such as +# Extended: Loads and activates a package's main module and resources such as # stylesheets, keymaps, grammar, editor properties, and menus. module.exports = class Package @@ -48,14 +47,6 @@ class Package unless typeof metadata.name is 'string' and metadata.name.length > 0 metadata.name = packageName - if includeDeprecatedAPIs and metadata.stylesheetMain? - deprecate("Use the `mainStyleSheet` key instead of `stylesheetMain` in the `package.json` of `#{packageName}`", {packageName}) - metadata.mainStyleSheet = metadata.stylesheetMain - - if includeDeprecatedAPIs and metadata.stylesheets? - deprecate("Use the `styleSheets` key instead of `stylesheets` in the `package.json` of `#{packageName}`", {packageName}) - metadata.styleSheets = metadata.stylesheets - metadata keymaps: null @@ -174,11 +165,6 @@ class Package if @mainModule? if @mainModule.config? and typeof @mainModule.config is 'object' atom.config.setSchema @name, {type: 'object', properties: @mainModule.config} - else if @mainModule.configDefaults? and typeof @mainModule.configDefaults is 'object' - deprecate("""Use a config schema instead. See the configuration section - of https://atom.io/docs/latest/hacking-atom-package-word-count and - https://atom.io/docs/api/latest/Config for more details""", {packageName: @name}) - atom.config.setDefaults(@name, @mainModule.configDefaults) @mainModule.activateConfig?() @configActivated = true @@ -211,17 +197,6 @@ class Package for [menuPath, map] in @menus when map['context-menu']? try itemsBySelector = map['context-menu'] - - # Detect deprecated format for items object - for key, value of itemsBySelector - unless _.isArray(value) - deprecate(""" - The context menu CSON format has changed. Please see - https://atom.io/docs/api/latest/ContextMenuManager#context-menu-cson-format - for more info. - """, {packageName: @name}) - itemsBySelector = atom.contextMenu.convertLegacyItemsBySelector(itemsBySelector) - @activationDisposables.add(atom.contextMenu.add(itemsBySelector)) catch error if error.code is 'EBADSELECTOR' @@ -309,11 +284,7 @@ class Package [stylesheetPath, atom.themes.loadStylesheet(stylesheetPath, true)] getStylesheetsPath: -> - if fs.isDirectorySync(path.join(@path, 'stylesheets')) - deprecate("Store package style sheets in the `styles/` directory instead of `stylesheets/` in the `#{@name}` package", packageName: @name) - path.join(@path, 'stylesheets') - else - path.join(@path, 'styles') + path.join(@path, 'styles') getStylesheetPaths: -> stylesheetDirPath = @getStylesheetsPath() @@ -383,11 +354,7 @@ class Package callback() new Promise (resolve) => - if fs.isDirectorySync(path.join(@path, 'scoped-properties')) - settingsDirPath = path.join(@path, 'scoped-properties') - deprecate("Store package settings files in the `settings/` directory instead of `scoped-properties/`", packageName: @name) - else - settingsDirPath = path.join(@path, 'settings') + settingsDirPath = path.join(@path, 'settings') fs.exists settingsDirPath, (settingsDirExists) -> return resolve() unless settingsDirExists @@ -417,7 +384,6 @@ class Package @mainActivated = false catch e console.error "Error deactivating package '#{@name}'", e.stack - @emit 'deactivated' if includeDeprecatedAPIs @emitter.emit 'did-deactivate' deactivateConfig: -> @@ -533,31 +499,6 @@ class Package else if _.isArray(commands) @activationCommands[selector].push(commands...) - if @metadata.activationEvents? - deprecate(""" - Use `activationCommands` instead of `activationEvents` in your package.json - Commands should be grouped by selector as follows: - ```json - "activationCommands": { - "atom-workspace": ["foo:bar", "foo:baz"], - "atom-text-editor": ["foo:quux"] - } - ``` - """, {packageName: @name}) - if _.isArray(@metadata.activationEvents) - for eventName in @metadata.activationEvents - @activationCommands['atom-workspace'] ?= [] - @activationCommands['atom-workspace'].push(eventName) - else if _.isString(@metadata.activationEvents) - eventName = @metadata.activationEvents - @activationCommands['atom-workspace'] ?= [] - @activationCommands['atom-workspace'].push(eventName) - else - for eventName, selector of @metadata.activationEvents - selector ?= 'atom-workspace' - @activationCommands[selector] ?= [] - @activationCommands[selector].push(eventName) - @activationCommands subscribeToActivationHooks: -> @@ -726,15 +667,3 @@ class Package stack = error.stack ? error atom.notifications.addFatalError(message, {stack, detail, dismissable: true}) - -if includeDeprecatedAPIs - EmitterMixin = require('emissary').Emitter - EmitterMixin.includeInto(Package) - - Package::on = (eventName) -> - switch eventName - when 'deactivated' - deprecate 'Use Package::onDidDeactivate instead' - else - deprecate 'Package::on is deprecated. Use event subscription methods instead.' - EmitterMixin::on.apply(this, arguments) diff --git a/src/pane-axis.coffee b/src/pane-axis.coffee index 1fba48d37..57d07d8a9 100644 --- a/src/pane-axis.coffee +++ b/src/pane-axis.coffee @@ -1,17 +1,21 @@ {Emitter, CompositeDisposable} = require 'event-kit' {flatten} = require 'underscore-plus' -Serializable = require 'serializable' Model = require './model' module.exports = class PaneAxis extends Model atom.deserializers.add(this) - Serializable.includeInto(this) parent: null container: null orientation: null + @deserialize: (state, params) -> + container = params?.container + state.container = container + state.children = state.children.map (childState) -> atom.deserializers.deserialize(childState, {container}) + new this(state) + constructor: ({@container, @orientation, children, flexScale}={}) -> @emitter = new Emitter @subscriptionsByChild = new WeakMap @@ -21,12 +25,8 @@ class PaneAxis extends Model @addChild(child) for child in children @flexScale = flexScale ? 1 - deserializeParams: (params) -> - {container} = params - params.children = params.children.map (childState) -> atom.deserializers.deserialize(childState, {container}) - params - - serializeParams: -> + serialize: -> + deserializer: 'PaneAxis' children: @children.map (child) -> child.serialize() orientation: @orientation flexScale: @flexScale diff --git a/src/pane-container-element.coffee b/src/pane-container-element.coffee index ba97f68f1..b3d4e0036 100644 --- a/src/pane-container-element.coffee +++ b/src/pane-container-element.coffee @@ -1,5 +1,4 @@ {CompositeDisposable} = require 'event-kit' -Grim = require 'grim' _ = require 'underscore-plus' module.exports = diff --git a/src/pane-container.coffee b/src/pane-container.coffee index ca61b49b0..9fbf14519 100644 --- a/src/pane-container.coffee +++ b/src/pane-container.coffee @@ -1,7 +1,5 @@ {find, flatten} = require 'underscore-plus' -Grim = require 'grim' {Emitter, CompositeDisposable} = require 'event-kit' -Serializable = require 'serializable' Gutter = require './gutter' Model = require './model' Pane = require './pane' @@ -10,17 +8,23 @@ ItemRegistry = require './item-registry' module.exports = class PaneContainer extends Model atom.deserializers.add(this) - Serializable.includeInto(this) @version: 1 root: null + @deserialize: (state) -> + container = Object.create(@prototype) # allows us to pass a self reference to our child before invoking constructor + state.root = atom.deserializers.deserialize(state.root, {container}) + state.destroyEmptyPanes = atom.config.get('core.destroyEmptyPanes') + state.activePane = find state.root.getPanes(), (pane) -> pane.id is state.activePaneId + @call(container, state) # run constructor + container + constructor: (params) -> super - unless Grim.includeDeprecatedAPIs - @activePane = params?.activePane + @activePane = params?.activePane @emitter = new Emitter @subscriptions = new CompositeDisposable @@ -35,13 +39,9 @@ class PaneContainer extends Model @monitorActivePaneItem() @monitorPaneItems() - deserializeParams: (params) -> - params.root = atom.deserializers.deserialize(params.root, container: this) - params.destroyEmptyPanes = atom.config.get('core.destroyEmptyPanes') - params.activePane = find params.root.getPanes(), (pane) -> pane.id is params.activePaneId - params - - serializeParams: (params) -> + serialize: (params) -> + deserializer: 'PaneContainer' + version: @constructor.version root: @root?.serialize() activePaneId: @activePane.id @@ -222,12 +222,3 @@ class PaneContainer extends Model removedPaneItem: (item) -> @itemRegistry.removeItem(item) - -if Grim.includeDeprecatedAPIs - PaneContainer.properties - activePane: null - - PaneContainer.behavior 'activePaneItem', -> - @$activePane - .switch((activePane) -> activePane?.$activeItem) - .distinctUntilChanged() diff --git a/src/pane-element.coffee b/src/pane-element.coffee index d4055b0ef..c6ded79c4 100644 --- a/src/pane-element.coffee +++ b/src/pane-element.coffee @@ -1,6 +1,5 @@ path = require 'path' {CompositeDisposable} = require 'event-kit' -Grim = require 'grim' class PaneElement extends HTMLElement attached: false diff --git a/src/pane.coffee b/src/pane.coffee index 06ea88e47..6cf505b54 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -1,7 +1,5 @@ {find, compact, extend, last} = require 'underscore-plus' {Emitter} = require 'event-kit' -Serializable = require 'serializable' -Grim = require 'grim' Model = require './model' PaneAxis = require './pane-axis' TextEditor = require './text-editor' @@ -13,14 +11,29 @@ TextEditor = require './text-editor' module.exports = class Pane extends Model atom.deserializers.add(this) - Serializable.includeInto(this) + + container: undefined + activeItem: undefined + focused: false + + @deserialize: (state, params) -> + {items, activeItemURI, activeItemUri} = state + state.container = params?.container + activeItemURI ?= activeItemUri + state.items = compact(items.map (itemState) -> atom.deserializers.deserialize(itemState)) + state.activeItem = find state.items, (item) -> + if typeof item.getURI is 'function' + itemURI = item.getURI() + itemURI is activeItemURI + + new this(state) constructor: (params) -> super - unless Grim.includeDeprecatedAPIs - @container = params?.container - @activeItem = params?.activeItem + @container = params?.container + @activeItem = params?.activeItem + @focused = params?.focused @emitter = new Emitter @itemSubscriptions = new WeakMap @@ -30,33 +43,17 @@ class Pane extends Model @setActiveItem(@items[0]) unless @getActiveItem()? @setFlexScale(params?.flexScale ? 1) - # Called by the Serializable mixin during serialization. - serializeParams: -> + serialize: -> if typeof @activeItem?.getURI is 'function' activeItemURI = @activeItem.getURI() - else if Grim.includeDeprecatedAPIs and typeof @activeItem?.getUri is 'function' - activeItemURI = @activeItem.getUri() + deserializer: 'Pane' id: @id items: compact(@items.map((item) -> item.serialize?())) activeItemURI: activeItemURI focused: @focused flexScale: @flexScale - # Called by the Serializable mixin during deserialization. - deserializeParams: (params) -> - {items, activeItemURI, activeItemUri} = params - activeItemURI ?= activeItemUri - params.items = compact(items.map (itemState) -> atom.deserializers.deserialize(itemState)) - params.activeItem = find params.items, (item) -> - if typeof item.getURI is 'function' - itemURI = item.getURI() - else if Grim.includeDeprecatedAPIs and typeof item.getUri is 'function' - itemURI = item.getUri() - - itemURI is activeItemURI - params - getParent: -> @parent setParent: (@parent) -> @parent @@ -355,16 +352,14 @@ class Pane extends Model # Returns the added item. addItem: (item, index=@getActiveItemIndex() + 1) -> throw new Error("Pane items must be objects. Attempted to add item #{item}.") unless item? and typeof item is 'object' + throw new Error("Adding a pane item with URI '#{item.getURI?()}' that has already been destroyed") if item.isDestroyed?() return if item in @items if typeof item.onDidDestroy is 'function' @itemSubscriptions.set item, item.onDidDestroy => @removeItem(item, true) - else if Grim.includeDeprecatedAPIs and typeof item.on is 'function' - @subscribe item, 'destroyed', => @removeItem(item, true) @items.splice(index, 0, item) - @emit 'item-added', item, index if Grim.includeDeprecatedAPIs @emitter.emit 'did-add-item', {item, index} @setActiveItem(item) unless @getActiveItem()? item @@ -388,9 +383,6 @@ class Pane extends Model return if index is -1 @emitter.emit 'will-remove-item', {item, index, destroyed} - - if Grim.includeDeprecatedAPIs and typeof item.on is 'function' - @unsubscribe item @unsubscribeFromItem(item) if item is @activeItem @@ -401,7 +393,6 @@ class Pane extends Model else @activatePreviousItem() @items.splice(index, 1) - @emit 'item-removed', item, index, destroyed if Grim.includeDeprecatedAPIs @emitter.emit 'did-remove-item', {item, index, destroyed} @container?.didDestroyPaneItem({item, index, pane: this}) if destroyed @destroy() if @items.length is 0 and atom.config.get('core.destroyEmptyPanes') @@ -414,7 +405,6 @@ class Pane extends Model oldIndex = @items.indexOf(item) @items.splice(oldIndex, 1) @items.splice(newIndex, 0, item) - @emit 'item-moved', item, newIndex if Grim.includeDeprecatedAPIs @emitter.emit 'did-move-item', {item, oldIndex, newIndex} # Public: Move the given item to the given index on another pane. @@ -442,7 +432,6 @@ class Pane extends Model destroyItem: (item) -> index = @items.indexOf(item) if index isnt -1 - @emit 'before-item-destroyed', item if Grim.includeDeprecatedAPIs @emitter.emit 'will-destroy-item', {item, index} @container?.willDestroyPaneItem({item, index, pane: this}) if @promptToSaveItem(item) @@ -582,7 +571,6 @@ class Pane extends Model throw new Error("Pane has been destroyed") if @isDestroyed() @container?.setActivePane(this) - @emit 'activated' if Grim.includeDeprecatedAPIs @emitter.emit 'did-activate' # Public: Close the pane and destroy all its items. @@ -731,60 +719,3 @@ class Pane extends Model atom.notifications.addWarning("Unable to save file: A directory in the path '#{fileName}' could not be written to") else throw error - -if Grim.includeDeprecatedAPIs - Pane.properties - container: undefined - activeItem: undefined - focused: false - - Pane.behavior 'active', -> - @$container - .switch((container) -> container?.$activePane) - .map((activePane) => activePane is this) - .distinctUntilChanged() - - Pane::on = (eventName) -> - switch eventName - when 'activated' - Grim.deprecate("Use Pane::onDidActivate instead") - when 'destroyed' - Grim.deprecate("Use Pane::onDidDestroy instead") - when 'item-added' - Grim.deprecate("Use Pane::onDidAddItem instead") - when 'item-removed' - Grim.deprecate("Use Pane::onDidRemoveItem instead") - when 'item-moved' - Grim.deprecate("Use Pane::onDidMoveItem instead") - when 'before-item-destroyed' - Grim.deprecate("Use Pane::onWillDestroyItem instead") - else - Grim.deprecate("Subscribing via ::on is deprecated. Use documented event subscription methods instead.") - super - - Pane::behavior = (behaviorName) -> - switch behaviorName - when 'active' - Grim.deprecate("The $active behavior property is deprecated. Use ::observeActive or ::onDidChangeActive instead.") - when 'container' - Grim.deprecate("The $container behavior property is deprecated.") - when 'activeItem' - Grim.deprecate("The $activeItem behavior property is deprecated. Use ::observeActiveItem or ::onDidChangeActiveItem instead.") - when 'focused' - Grim.deprecate("The $focused behavior property is deprecated.") - else - Grim.deprecate("Pane::behavior is deprecated. Use event subscription methods instead.") - - super - - Pane::itemForUri = (uri) -> - Grim.deprecate("Use `::itemForURI` instead.") - @itemForURI(uri) - - Pane::activateItemForUri = (uri) -> - Grim.deprecate("Use `::activateItemForURI` instead.") - @activateItemForURI(uri) -else - Pane::container = undefined - Pane::activeItem = undefined - Pane::focused = undefined diff --git a/src/project.coffee b/src/project.coffee index f5168dd9b..b3f05a942 100644 --- a/src/project.coffee +++ b/src/project.coffee @@ -3,11 +3,8 @@ url = require 'url' _ = require 'underscore-plus' fs = require 'fs-plus' -{includeDeprecatedAPIs, deprecate} = require 'grim' {Emitter} = require 'event-kit' -Serializable = require 'serializable' TextBuffer = require 'text-buffer' -Grim = require 'grim' DefaultDirectoryProvider = require './default-directory-provider' Model = require './model' @@ -21,12 +18,25 @@ GitRepositoryProvider = require './git-repository-provider' module.exports = class Project extends Model atom.deserializers.add(this) - Serializable.includeInto(this) ### Section: Construction and Destruction ### + @deserialize: (state) -> + state.buffers = _.compact state.buffers.map (bufferState) -> + # Check that buffer's file path is accessible + return if fs.isDirectorySync(bufferState.filePath) + if bufferState.filePath + try + fs.closeSync(fs.openSync(bufferState.filePath, 'r')) + catch error + return unless error.code is 'ENOENT' + + atom.deserializers.deserialize(bufferState) + + new this(state) + constructor: ({path, paths, @buffers}={}) -> @emitter = new Emitter @buffers ?= [] @@ -62,9 +72,6 @@ class Project extends Model @subscribeToBuffer(buffer) for buffer in @buffers - if Grim.includeDeprecatedAPIs and path? - Grim.deprecate("Pass 'paths' array instead of 'path' to project constructor") - paths ?= _.compact([path]) @setPaths(paths) @@ -80,23 +87,11 @@ class Project extends Model Section: Serialization ### - serializeParams: -> + serialize: -> + deserializer: 'Project' paths: @getPaths() buffers: _.compact(@buffers.map (buffer) -> buffer.serialize() if buffer.isRetained()) - deserializeParams: (params) -> - params.buffers = _.compact params.buffers.map (bufferState) -> - # Check that buffer's file path is accessible - return if fs.isDirectorySync(bufferState.filePath) - if bufferState.filePath - try - fs.closeSync(fs.openSync(bufferState.filePath, 'r')) - catch error - return unless error.code is 'ENOENT' - - atom.deserializers.deserialize(bufferState) - params - ### Section: Event Subscription ### @@ -166,16 +161,12 @@ class Project extends Model # # * `projectPaths` {Array} of {String} paths. setPaths: (projectPaths) -> - if includeDeprecatedAPIs - rootDirectory.off() for rootDirectory in @rootDirectories - repository?.destroy() for repository in @repositories @rootDirectories = [] @repositories = [] @addPath(projectPath, emitEvent: false) for projectPath in projectPaths - @emit "path-changed" if includeDeprecatedAPIs @emitter.emit 'did-change-paths', projectPaths # Public: Add a path to the project's list of root paths @@ -200,7 +191,6 @@ class Project extends Model @repositories.push(repo ? null) unless options?.emitEvent is false - @emit "path-changed" if includeDeprecatedAPIs @emitter.emit 'did-change-paths', @getPaths() # Public: remove a path from the project's list of root paths. @@ -220,9 +210,7 @@ class Project extends Model if indexToRemove? [removedDirectory] = @rootDirectories.splice(indexToRemove, 1) [removedRepository] = @repositories.splice(indexToRemove, 1) - removedDirectory.off() if includeDeprecatedAPIs removedRepository?.destroy() unless removedRepository in @repositories - @emit "path-changed" if includeDeprecatedAPIs @emitter.emit "did-change-paths", @getPaths() true else @@ -402,7 +390,6 @@ class Project extends Model addBufferAtIndex: (buffer, index, options={}) -> @buffers.splice(index, 0, buffer) @subscribeToBuffer(buffer) - @emit 'buffer-created', buffer if includeDeprecatedAPIs @emitter.emit 'did-add-buffer', buffer buffer @@ -442,66 +429,3 @@ class Project extends Model """, detail: error.message dismissable: true - -if includeDeprecatedAPIs - Project.pathForRepositoryUrl = (repoUrl) -> - deprecate '::pathForRepositoryUrl will be removed. Please remove from your code.' - [repoName] = url.parse(repoUrl).path.split('/')[-1..] - repoName = repoName.replace(/\.git$/, '') - path.join(atom.config.get('core.projectHome'), repoName) - - Project::registerOpener = (opener) -> - deprecate("Use Workspace::addOpener instead") - atom.workspace.addOpener(opener) - - Project::unregisterOpener = (opener) -> - deprecate("Call .dispose() on the Disposable returned from ::addOpener instead") - atom.workspace.unregisterOpener(opener) - - Project::eachEditor = (callback) -> - deprecate("Use Workspace::observeTextEditors instead") - atom.workspace.observeTextEditors(callback) - - Project::getEditors = -> - deprecate("Use Workspace::getTextEditors instead") - atom.workspace.getTextEditors() - - Project::on = (eventName) -> - if eventName is 'path-changed' - Grim.deprecate("Use Project::onDidChangePaths instead") - else - Grim.deprecate("Project::on is deprecated. Use documented event subscription methods instead.") - super - - Project::getRepo = -> - Grim.deprecate("Use ::getRepositories instead") - @getRepositories()[0] - - Project::getPath = -> - Grim.deprecate("Use ::getPaths instead") - @getPaths()[0] - - Project::setPath = (path) -> - Grim.deprecate("Use ::setPaths instead") - @setPaths([path]) - - Project::getRootDirectory = -> - Grim.deprecate("Use ::getDirectories instead") - @getDirectories()[0] - - Project::resolve = (uri) -> - Grim.deprecate("Use `Project::getDirectories()[0]?.resolve()` instead") - @resolvePath(uri) - - Project::scan = (regex, options={}, iterator) -> - Grim.deprecate("Use atom.workspace.scan instead of atom.project.scan") - atom.workspace.scan(regex, options, iterator) - - Project::replace = (regex, replacementText, filePaths, iterator) -> - Grim.deprecate("Use atom.workspace.replace instead of atom.project.replace") - atom.workspace.replace(regex, replacementText, filePaths, iterator) - - Project::openSync = (filePath, options={}) -> - deprecate("Use Project::open instead") - filePath = @resolvePath(filePath) - @buildEditorForBuffer(@bufferForPathSync(filePath), options) diff --git a/src/selection.coffee b/src/selection.coffee index abf8640dc..80929cac7 100644 --- a/src/selection.coffee +++ b/src/selection.coffee @@ -1,7 +1,6 @@ {Point, Range} = require 'text-buffer' {pick} = _ = require 'underscore-plus' {Emitter} = require 'event-kit' -Grim = require 'grim' Model = require './model' NonWhitespaceRegExp = /\S/ @@ -836,25 +835,3 @@ class Selection extends Model getGoalScreenRange: -> if goalScreenRange = @marker.getProperties().goalScreenRange Range.fromObject(goalScreenRange) - -if Grim.includeDeprecatedAPIs - Selection::on = (eventName) -> - switch eventName - when 'screen-range-changed' - Grim.deprecate("Use Selection::onDidChangeRange instead. Call ::getScreenRange() yourself in your callback if you need the range.") - when 'destroyed' - Grim.deprecate("Use Selection::onDidDestroy instead.") - else - Grim.deprecate("Selection::on is deprecated. Use documented event subscription methods instead.") - - super - - # Deprecated: Use {::deleteToBeginningOfWord} instead. - Selection::backspaceToBeginningOfWord = -> - deprecate("Use Selection::deleteToBeginningOfWord() instead") - @deleteToBeginningOfWord() - - # Deprecated: Use {::deleteToBeginningOfLine} instead. - Selection::backspaceToBeginningOfLine = -> - deprecate("Use Selection::deleteToBeginningOfLine() instead") - @deleteToBeginningOfLine() diff --git a/src/subscriber-mixin.coffee b/src/subscriber-mixin.coffee deleted file mode 100644 index b6817ce53..000000000 --- a/src/subscriber-mixin.coffee +++ /dev/null @@ -1,4 +0,0 @@ -{Subscriber} = require 'emissary' -SubscriberMixin = componentDidUnmount: -> @unsubscribe() -Subscriber.extend(SubscriberMixin) -module.exports = SubscriberMixin diff --git a/src/task.coffee b/src/task.coffee index 6b1162396..fc8c5bd6b 100644 --- a/src/task.coffee +++ b/src/task.coffee @@ -1,6 +1,6 @@ _ = require 'underscore-plus' ChildProcess = require 'child_process' -{Emitter} = require 'emissary' +{Emitter} = require 'event-kit' Grim = require 'grim' # Extended: Run a node script in a separate process. @@ -38,8 +38,6 @@ Grim = require 'grim' # ``` module.exports = class Task - Emitter.includeInto(this) - # Public: A helper method to easily launch and run a task once. # # * `taskPath` The {String} path to the CoffeeScript/JavaScript file which @@ -66,6 +64,8 @@ class Task # * `taskPath` The {String} path to the CoffeeScript/JavaScript file that # exports a single {Function} to execute. constructor: (taskPath) -> + @emitter = new Emitter + compileCacheRequire = "require('#{require.resolve('./compile-cache')}')" compileCachePath = require('./compile-cache').getCacheDirectory() taskBootstrapRequire = "require('#{require.resolve('./task-bootstrap')}');" @@ -95,7 +95,7 @@ class Task handleEvents: -> @childProcess.removeAllListeners() @childProcess.on 'message', ({event, args}) => - @emit(event, args...) if @childProcess? + @emitter.emit(event, args) if @childProcess? # Catch the errors that happened before task-bootstrap. if @childProcess.stdout? @@ -143,7 +143,12 @@ class Task # * `callback` The {Function} to call when the event is emitted. # # Returns a {Disposable} that can be used to stop listening for the event. - on: (eventName, callback) -> Emitter::on.call(this, eventName, callback) + on: (eventName, callback) -> @emitter.on eventName, (args) -> callback(args...) + + once: (eventName, callback) -> + disposable = @on eventName, (args...) -> + disposable.dispose() + callback(args...) # Public: Forcefully stop the running task. # @@ -162,5 +167,5 @@ class Task cancel: -> didForcefullyTerminate = @terminate() if didForcefullyTerminate - @emit('task:cancelled') + @emitter.emit('task:cancelled') didForcefullyTerminate diff --git a/src/text-editor-component.coffee b/src/text-editor-component.coffee index 73ae2f636..fcd443151 100644 --- a/src/text-editor-component.coffee +++ b/src/text-editor-component.coffee @@ -1,7 +1,6 @@ _ = require 'underscore-plus' scrollbarStyle = require 'scrollbar-style' {Range, Point} = require 'text-buffer' -grim = require 'grim' {CompositeDisposable} = require 'event-kit' ipc = require 'ipc' @@ -28,8 +27,6 @@ class TextEditorComponent updatesPaused: false updateRequestedWhilePaused: false heightAndWidthMeasurementRequested: false - cursorMoved: false - selectionChanged: false inputEnabled: true measureScrollbarsWhenShown: true measureLineHeightAndDefaultCharWidthWhenShown: true @@ -37,6 +34,13 @@ class TextEditorComponent stylingChangeAnimationFrameRequested: false gutterComponent: null mounted: true + initialized: false + + Object.defineProperty @prototype, "domNode", + get: -> @domNodeValue + set: (domNode) -> + atom.assert domNode?, "TextEditorComponent::domNode was set to null." + @domNodeValue = domNode constructor: ({@editor, @hostElement, @rootElement, @stylesElement, @useShadowDOM, tileSize}) -> @tileSize = tileSize if tileSize? @@ -47,8 +51,10 @@ class TextEditorComponent @presenter = new TextEditorPresenter model: @editor - scrollTop: @editor.getScrollTop() - scrollLeft: @editor.getScrollLeft() + scrollTop: 0 + scrollLeft: 0 + scrollRow: @editor.getScrollRow() + scrollColumn: @editor.getScrollColumn() tileSize: tileSize cursorBlinkPeriod: @cursorBlinkPeriod cursorBlinkResumeDelay: @cursorBlinkResumeDelay @@ -106,6 +112,7 @@ class TextEditorComponent @updateSync() @checkForVisibilityChange() + @initialized = true destroy: -> @mounted = false @@ -121,11 +128,6 @@ class TextEditorComponent @oldState ?= {} @newState = @presenter.getState() - cursorMoved = @cursorMoved - selectionChanged = @selectionChanged - @cursorMoved = false - @selectionChanged = false - if @editor.getLastSelection()? and not @editor.getLastSelection().isEmpty() @domNode.classList.add('has-selection') else @@ -221,8 +223,6 @@ class TextEditorComponent observeEditor: -> @disposables.add @editor.observeGrammar(@onGrammarChanged) - @disposables.add @editor.observeCursors(@onCursorAdded) - @disposables.add @editor.observeSelections(@onSelectionAdded) listenForDOMEvents: -> @domNode.addEventListener 'mousewheel', @onMouseWheel @@ -318,7 +318,7 @@ class TextEditorComponent inputNode.value = event.data if insertedRange onVerticalScroll: (scrollTop) => - return if @updateRequested or scrollTop is @editor.getScrollTop() + return if @updateRequested or scrollTop is @presenter.getScrollTop() animationFramePending = @pendingScrollTop? @pendingScrollTop = scrollTop @@ -327,15 +327,17 @@ class TextEditorComponent pendingScrollTop = @pendingScrollTop @pendingScrollTop = null @presenter.setScrollTop(pendingScrollTop) + @presenter.commitPendingScrollTopPosition() onHorizontalScroll: (scrollLeft) => - return if @updateRequested or scrollLeft is @editor.getScrollLeft() + return if @updateRequested or scrollLeft is @presenter.getScrollLeft() animationFramePending = @pendingScrollLeft? @pendingScrollLeft = scrollLeft unless animationFramePending @requestAnimationFrame => @presenter.setScrollLeft(@pendingScrollLeft) + @presenter.commitPendingScrollLeftPosition() @pendingScrollLeft = null onMouseWheel: (event) => @@ -354,14 +356,18 @@ class TextEditorComponent if Math.abs(wheelDeltaX) > Math.abs(wheelDeltaY) # Scrolling horizontally previousScrollLeft = @presenter.getScrollLeft() - @presenter.setScrollLeft(previousScrollLeft - Math.round(wheelDeltaX * @scrollSensitivity)) - event.preventDefault() unless previousScrollLeft is @presenter.getScrollLeft() + updatedScrollLeft = previousScrollLeft - Math.round(wheelDeltaX * @scrollSensitivity) + + event.preventDefault() if @presenter.canScrollLeftTo(updatedScrollLeft) + @presenter.setScrollLeft(updatedScrollLeft) else # Scrolling vertically @presenter.setMouseWheelScreenRow(@screenRowForNode(event.target)) previousScrollTop = @presenter.getScrollTop() - @presenter.setScrollTop(previousScrollTop - Math.round(wheelDeltaY * @scrollSensitivity)) - event.preventDefault() unless previousScrollTop is @presenter.getScrollTop() + updatedScrollTop = previousScrollTop - Math.round(wheelDeltaY * @scrollSensitivity) + + event.preventDefault() if @presenter.canScrollTopTo(updatedScrollTop) + @presenter.setScrollTop(updatedScrollTop) onScrollViewScroll: => if @mounted @@ -369,6 +375,77 @@ class TextEditorComponent @scrollViewNode.scrollTop = 0 @scrollViewNode.scrollLeft = 0 + onDidChangeScrollTop: (callback) -> + @presenter.onDidChangeScrollTop(callback) + + onDidChangeScrollLeft: (callback) -> + @presenter.onDidChangeScrollLeft(callback) + + setScrollLeft: (scrollLeft) -> + @presenter.setScrollLeft(scrollLeft) + + setScrollRight: (scrollRight) -> + @presenter.setScrollRight(scrollRight) + + setScrollTop: (scrollTop) -> + @presenter.setScrollTop(scrollTop) + + setScrollBottom: (scrollBottom) -> + @presenter.setScrollBottom(scrollBottom) + + getScrollTop: -> + @presenter.getScrollTop() + + getScrollLeft: -> + @presenter.getScrollLeft() + + getScrollRight: -> + @presenter.getScrollRight() + + getScrollBottom: -> + @presenter.getScrollBottom() + + getScrollHeight: -> + @presenter.getScrollHeight() + + getScrollWidth: -> + @presenter.getScrollWidth() + + getVerticalScrollbarWidth: -> + @presenter.getVerticalScrollbarWidth() + + getHorizontalScrollbarHeight: -> + @presenter.getHorizontalScrollbarHeight() + + getVisibleRowRange: -> + @presenter.getVisibleRowRange() + + pixelPositionForScreenPosition: (screenPosition) -> + position = @presenter.pixelPositionForScreenPosition(screenPosition) + position.top += @presenter.getScrollTop() + position.left += @presenter.getScrollLeft() + position + + screenPositionForPixelPosition: (pixelPosition) -> + @presenter.screenPositionForPixelPosition(pixelPosition) + + pixelRectForScreenRange: (screenRange) -> + rect = @presenter.pixelRectForScreenRange(screenRange) + rect.top += @presenter.getScrollTop() + rect.bottom += @presenter.getScrollTop() + rect.left += @presenter.getScrollLeft() + rect.right += @presenter.getScrollLeft() + rect + + pixelRangeForScreenRange: (screenRange, clip=true) -> + {start, end} = Range.fromObject(screenRange) + {start: @pixelPositionForScreenPosition(start, clip), end: @pixelPositionForScreenPosition(end, clip)} + + pixelPositionForBufferPosition: (bufferPosition) -> + @pixelPositionForScreenPosition( + @editor.screenPositionForBufferPosition(bufferPosition) + ) + onMouseDown: (event) => unless event.button is 0 or (event.button is 1 and process.platform is 'linux') # Only handle mouse down events for left mouse button on all platforms @@ -488,29 +565,6 @@ class TextEditorComponent @sampleBackgroundColors() @remeasureCharacterWidths() - onSelectionAdded: (selection) => - selectionDisposables = new CompositeDisposable - selectionDisposables.add selection.onDidChangeRange => @onSelectionChanged(selection) - selectionDisposables.add selection.onDidDestroy => - @onSelectionChanged(selection) - selectionDisposables.dispose() - @disposables.remove(selectionDisposables) - - @disposables.add(selectionDisposables) - - if @editor.selectionIntersectsVisibleRowRange(selection) - @selectionChanged = true - - onSelectionChanged: (selection) => - if @editor.selectionIntersectsVisibleRowRange(selection) - @selectionChanged = true - - onCursorAdded: (cursor) => - @disposables.add cursor.onDidChangePosition @onCursorMoved - - onCursorMoved: => - @cursorMoved = true - handleDragUntilMouseUp: (dragHandler) => dragging = false lastMousePosition = {} @@ -573,9 +627,11 @@ class TextEditorComponent if mouseYDelta? @presenter.setScrollTop(@presenter.getScrollTop() + yDirection * scaleScrollDelta(mouseYDelta)) + @presenter.commitPendingScrollTopPosition() if mouseXDelta? @presenter.setScrollLeft(@presenter.getScrollLeft() + xDirection * scaleScrollDelta(mouseXDelta)) + @presenter.commitPendingScrollLeftPosition() scaleScrollDelta = (scrollDelta) -> Math.pow(scrollDelta / 2, 3) / 280 @@ -592,7 +648,11 @@ class TextEditorComponent disposables.add(@editor.onDidDestroy(stopDragging)) isVisible: -> - @domNode.offsetHeight > 0 or @domNode.offsetWidth > 0 + # Investigating an exception that occurs here due to ::domNode being null. + atom.assert @domNode?, "TextEditorComponent::domNode was null.", (error) => + error.metadata = {@initialized} + + @domNode? and (@domNode.offsetHeight > 0 or @domNode.offsetWidth > 0) pollDOM: => unless @checkForVisibilityChange() @@ -798,19 +858,22 @@ class TextEditorComponent screenPositionForMouseEvent: (event, linesClientRect) -> pixelPosition = @pixelPositionForMouseEvent(event, linesClientRect) - @editor.screenPositionForPixelPosition(pixelPosition) + @presenter.screenPositionForPixelPosition(pixelPosition) pixelPositionForMouseEvent: (event, linesClientRect) -> {clientX, clientY} = event linesClientRect ?= @linesComponent.getDomNode().getBoundingClientRect() - top = clientY - linesClientRect.top + @presenter.scrollTop - left = clientX - linesClientRect.left + @presenter.scrollLeft - bottom = linesClientRect.top + @presenter.scrollTop + linesClientRect.height - clientY - right = linesClientRect.left + @presenter.scrollLeft + linesClientRect.width - clientX + top = clientY - linesClientRect.top + @presenter.getRealScrollTop() + left = clientX - linesClientRect.left + @presenter.getRealScrollLeft() + bottom = linesClientRect.top + @presenter.getRealScrollTop() + linesClientRect.height - clientY + right = linesClientRect.left + @presenter.getRealScrollLeft() + linesClientRect.width - clientX {top, left, bottom, right} + getGutterWidth: -> + @presenter.getGutterWidth() + getModel: -> @editor @@ -830,12 +893,3 @@ class TextEditorComponent updateParentViewMiniClass: -> @hostElement.classList.toggle('mini', @editor.isMini()) @rootElement.classList.toggle('mini', @editor.isMini()) - -if grim.includeDeprecatedAPIs - TextEditorComponent::setInvisibles = (invisibles={}) -> - grim.deprecate "Use config.set('editor.invisibles', invisibles) instead" - atom.config.set('editor.invisibles', invisibles) - - TextEditorComponent::setShowInvisibles = (showInvisibles) -> - grim.deprecate "Use config.set('editor.showInvisibles', showInvisibles) instead" - atom.config.set('editor.showInvisibles', showInvisibles) diff --git a/src/text-editor-element.coffee b/src/text-editor-element.coffee index e5023e821..685877a2e 100644 --- a/src/text-editor-element.coffee +++ b/src/text-editor-element.coffee @@ -2,7 +2,6 @@ Path = require 'path' {defaults} = require 'underscore-plus' TextBuffer = require 'text-buffer' -Grim = require 'grim' TextEditor = require './text-editor' TextEditorComponent = require './text-editor-component' @@ -182,7 +181,7 @@ class TextEditorElement extends HTMLElement # # Returns an {Object} with two values: `top` and `left`, representing the pixel position. pixelPositionForBufferPosition: (bufferPosition) -> - @getModel().pixelPositionForBufferPosition(bufferPosition, true) + @component.pixelPositionForBufferPosition(bufferPosition) # Extended: Converts a screen position to a pixel position. # @@ -191,21 +190,21 @@ class TextEditorElement extends HTMLElement # # Returns an {Object} with two values: `top` and `left`, representing the pixel positions. pixelPositionForScreenPosition: (screenPosition) -> - @getModel().pixelPositionForScreenPosition(screenPosition, true) + @component.pixelPositionForScreenPosition(screenPosition) # Extended: Retrieves the number of the row that is visible and currently at the # top of the editor. # # Returns a {Number}. getFirstVisibleScreenRow: -> - @getModel().getFirstVisibleScreenRow(true) + @getVisibleRowRange()[0] # Extended: Retrieves the number of the row that is visible and currently at the # bottom of the editor. # # Returns a {Number}. getLastVisibleScreenRow: -> - @getModel().getLastVisibleScreenRow(true) + @getVisibleRowRange()[1] # Extended: call the given `callback` when the editor is attached to the DOM. # @@ -219,6 +218,90 @@ class TextEditorElement extends HTMLElement onDidDetach: (callback) -> @emitter.on("did-detach", callback) + onDidChangeScrollTop: (callback) -> + @component.onDidChangeScrollTop(callback) + + onDidChangeScrollLeft: (callback) -> + @component.onDidChangeScrollLeft(callback) + + setScrollLeft: (scrollLeft) -> + @component.setScrollLeft(scrollLeft) + + setScrollRight: (scrollRight) -> + @component.setScrollRight(scrollRight) + + setScrollTop: (scrollTop) -> + @component.setScrollTop(scrollTop) + + setScrollBottom: (scrollBottom) -> + @component.setScrollBottom(scrollBottom) + + # Essential: Scrolls the editor to the top + scrollToTop: -> + @setScrollTop(0) + + # Essential: Scrolls the editor to the bottom + scrollToBottom: -> + @setScrollBottom(Infinity) + + getScrollTop: -> + @component.getScrollTop() + + getScrollLeft: -> + @component.getScrollLeft() + + getScrollRight: -> + @component.getScrollRight() + + getScrollBottom: -> + @component.getScrollBottom() + + getScrollHeight: -> + @component.getScrollHeight() + + getScrollWidth: -> + @component.getScrollWidth() + + getVerticalScrollbarWidth: -> + @component.getVerticalScrollbarWidth() + + getHorizontalScrollbarHeight: -> + @component.getHorizontalScrollbarHeight() + + getVisibleRowRange: -> + @component.getVisibleRowRange() + + intersectsVisibleRowRange: (startRow, endRow) -> + [visibleStart, visibleEnd] = @getVisibleRowRange() + not (endRow <= visibleStart or visibleEnd <= startRow) + + selectionIntersectsVisibleRowRange: (selection) -> + {start, end} = selection.getScreenRange() + @intersectsVisibleRowRange(start.row, end.row + 1) + + screenPositionForPixelPosition: (pixelPosition) -> + @component.screenPositionForPixelPosition(pixelPosition) + + pixelRectForScreenRange: (screenRange) -> + @component.pixelRectForScreenRange(screenRange) + + pixelRangeForScreenRange: (screenRange) -> + @component.pixelRangeForScreenRange(screenRange) + + setWidth: (width) -> + @style.width = (@component.getGutterWidth() + width) + "px" + @component.measureDimensions() + + getWidth: -> + @style.offsetWidth - @component.getGutterWidth() + + setHeight: (height) -> + @style.height = height + "px" + @component.measureDimensions() + + getHeight: -> + @style.offsetHeight + stopEventPropagation = (commandListeners) -> newCommandListeners = {} for commandName, commandListener of commandListeners diff --git a/src/text-editor-presenter.coffee b/src/text-editor-presenter.coffee index 62e1da167..7554487ad 100644 --- a/src/text-editor-presenter.coffee +++ b/src/text-editor-presenter.coffee @@ -14,7 +14,7 @@ class TextEditorPresenter minimumReflowInterval: 200 constructor: (params) -> - {@model, @autoHeight, @explicitHeight, @contentFrameWidth, @scrollTop, @scrollLeft, @boundingClientRect, @windowWidth, @windowHeight, @gutterWidth} = params + {@model, @autoHeight, @explicitHeight, @contentFrameWidth, @scrollTop, @scrollLeft, @scrollColumn, @scrollRow, @boundingClientRect, @windowWidth, @windowHeight, @gutterWidth} = params {horizontalScrollbarHeight, verticalScrollbarWidth} = params {@lineHeight, @baseCharacterWidth, @backgroundColor, @gutterBackgroundColor, @tileSize} = params {@cursorBlinkPeriod, @cursorBlinkResumeDelay, @stoppedScrollingDelay, @focused} = params @@ -32,6 +32,7 @@ class TextEditorPresenter @lineNumberDecorationsByScreenRow = {} @customGutterDecorationsByGutterNameAndScreenRow = {} @transferMeasurementsToModel() + @transferMeasurementsFromModel() @observeModel() @observeConfig() @buildState() @@ -53,14 +54,11 @@ class TextEditorPresenter @emitter.emit "did-update-state" if @isBatching() transferMeasurementsToModel: -> - @model.setHeight(@explicitHeight) if @explicitHeight? - @model.setWidth(@contentFrameWidth) if @contentFrameWidth? @model.setLineHeightInPixels(@lineHeight) if @lineHeight? @model.setDefaultCharWidth(@baseCharacterWidth) if @baseCharacterWidth? - @model.setScrollTop(@scrollTop) if @scrollTop? - @model.setScrollLeft(@scrollLeft) if @scrollLeft? - @model.setVerticalScrollbarWidth(@measuredVerticalScrollbarWidth) if @measuredVerticalScrollbarWidth? - @model.setHorizontalScrollbarHeight(@measuredHorizontalScrollbarHeight) if @measuredHorizontalScrollbarHeight? + + transferMeasurementsFromModel: -> + @editorWidthInChars = @model.getEditorWidthInChars() # Private: Determines whether {TextEditorPresenter} is currently batching changes. # Returns a {Boolean}, `true` if is collecting changes, `false` if is applying them. @@ -70,9 +68,12 @@ class TextEditorPresenter getStateForMeasurements: -> @updateVerticalDimensions() @updateScrollbarDimensions() + @updateScrollPosition() @updateStartRow() @updateEndRow() + @fetchVisibleDecorations() if @shouldUpdateDecorations + @updateLineDecorations() if @shouldUpdateDecorations @updateTilesState() if @shouldUpdateLineNumbersState or @shouldUpdateLinesState @@ -148,10 +149,6 @@ class TextEditorPresenter observeModel: -> @disposables.add @model.onDidChange => - @linesYardstick.measure [@model.getLongestScreenRow()], => - @updateVerticalDimensions() - @updateHorizontalDimensions() - @shouldUpdateHeightState = true @shouldUpdateVerticalScrollState = true @shouldUpdateHorizontalScrollState = true @@ -197,8 +194,7 @@ class TextEditorPresenter @disposables.add @model.onDidAddDecoration(@didAddDecoration.bind(this)) @disposables.add @model.onDidAddCursor(@didAddCursor.bind(this)) - @disposables.add @model.onDidChangeScrollTop(@setScrollTop.bind(this)) - @disposables.add @model.onDidChangeScrollLeft(@setScrollLeft.bind(this)) + @disposables.add @model.onDidRequestAutoscroll(@requestAutoscroll.bind(this)) @observeDecoration(decoration) for decoration in @model.getDecorations() @observeCursor(cursor) for cursor in @model.getCursors() @disposables.add @model.onDidAddGutter(@didAddGutter.bind(this)) @@ -346,7 +342,7 @@ class TextEditorPresenter row - (row % @tileSize) constrainRow: (row) -> - Math.min(0, Math.max(0, @model.getScreenLineCount())) + Math.max(0, Math.min(row, @model.getScreenLineCount())) getStartTileRow: -> @constrainRow(@tileForRow(@startRow)) @@ -724,6 +720,8 @@ class TextEditorPresenter return unless @height? and @horizontalScrollbarHeight? clientHeight = @height - @horizontalScrollbarHeight + @model.setHeight(clientHeight, true) + unless @clientHeight is clientHeight @clientHeight = clientHeight @updateScrollHeight() @@ -733,6 +731,8 @@ class TextEditorPresenter return unless @contentFrameWidth? and @verticalScrollbarWidth? clientWidth = @contentFrameWidth - @verticalScrollbarWidth + @model.setWidth(clientWidth, true) unless @editorWidthInChars + unless @clientWidth is clientWidth @clientWidth = clientWidth @updateScrollWidth() @@ -742,6 +742,7 @@ class TextEditorPresenter scrollTop = @constrainScrollTop(@scrollTop) unless @scrollTop is scrollTop @scrollTop = scrollTop + @realScrollTop = @scrollTop @updateStartRow() @updateEndRow() @@ -751,6 +752,7 @@ class TextEditorPresenter updateScrollLeft: -> @scrollLeft = @constrainScrollLeft(@scrollLeft) + @realScrollLeft = @scrollLeft constrainScrollLeft: (scrollLeft) -> return scrollLeft unless scrollLeft? and @scrollWidth? and @clientWidth? @@ -843,26 +845,28 @@ class TextEditorPresenter @emitDidUpdateState() setScrollTop: (scrollTop) -> - scrollTop = @constrainScrollTop(scrollTop) + return unless scrollTop? - unless @scrollTop is scrollTop or Number.isNaN(scrollTop) - @scrollTop = scrollTop - @model.setScrollTop(scrollTop) - @didStartScrolling() - @shouldUpdateVerticalScrollState = true - @shouldUpdateHiddenInputState = true - @shouldUpdateDecorations = true - @shouldUpdateLinesState = true - @shouldUpdateCursorsState = true - @shouldUpdateLineNumbersState = true - @shouldUpdateCustomGutterDecorationState = true - @shouldUpdateOverlaysState = true + @pendingScrollLogicalPosition = null + @pendingScrollTop = scrollTop - @emitDidUpdateState() + @shouldUpdateVerticalScrollState = true + @shouldUpdateHiddenInputState = true + @shouldUpdateDecorations = true + @shouldUpdateLinesState = true + @shouldUpdateCursorsState = true + @shouldUpdateLineNumbersState = true + @shouldUpdateCustomGutterDecorationState = true + @shouldUpdateOverlaysState = true + + @emitDidUpdateState() getScrollTop: -> @scrollTop + getRealScrollTop: -> + @realScrollTop ? @scrollTop + didStartScrolling: -> if @stoppedScrollingTimeoutId? clearTimeout(@stoppedScrollingTimeoutId) @@ -882,28 +886,58 @@ class TextEditorPresenter @emitDidUpdateState() setScrollLeft: (scrollLeft) -> - scrollLeft = @constrainScrollLeft(scrollLeft) - unless @scrollLeft is scrollLeft or Number.isNaN(scrollLeft) - oldScrollLeft = @scrollLeft - @scrollLeft = scrollLeft - @model.setScrollLeft(scrollLeft) - @shouldUpdateHorizontalScrollState = true - @shouldUpdateHiddenInputState = true - @shouldUpdateCursorsState = true - @shouldUpdateOverlaysState = true - @shouldUpdateDecorations = true - @shouldUpdateLinesState = true + return unless scrollLeft? - @emitDidUpdateState() + @pendingScrollLogicalPosition = null + @pendingScrollLeft = scrollLeft + + @shouldUpdateHorizontalScrollState = true + @shouldUpdateHiddenInputState = true + @shouldUpdateCursorsState = true + @shouldUpdateOverlaysState = true + @shouldUpdateDecorations = true + @shouldUpdateLinesState = true + + @emitDidUpdateState() getScrollLeft: -> @scrollLeft + getRealScrollLeft: -> + @realScrollLeft ? @scrollLeft + + getClientHeight: -> + if @clientHeight + @clientHeight + else + @explicitHeight - @horizontalScrollbarHeight + + getClientWidth: -> + if @clientWidth + @clientWidth + else + @contentFrameWidth - @verticalScrollbarWidth + + getScrollBottom: -> @getScrollTop() + @getClientHeight() + setScrollBottom: (scrollBottom) -> + @setScrollTop(scrollBottom - @getClientHeight()) + @getScrollBottom() + + getScrollRight: -> @getScrollLeft() + @getClientWidth() + setScrollRight: (scrollRight) -> + @setScrollLeft(scrollRight - @getClientWidth()) + @getScrollRight() + + getScrollHeight: -> + @scrollHeight + + getScrollWidth: -> + @scrollWidth + setHorizontalScrollbarHeight: (horizontalScrollbarHeight) -> unless @measuredHorizontalScrollbarHeight is horizontalScrollbarHeight oldHorizontalScrollbarHeight = @measuredHorizontalScrollbarHeight @measuredHorizontalScrollbarHeight = horizontalScrollbarHeight - @model.setHorizontalScrollbarHeight(horizontalScrollbarHeight) @shouldUpdateScrollbarsState = true @shouldUpdateVerticalScrollState = true @shouldUpdateHorizontalScrollState = true @@ -915,7 +949,6 @@ class TextEditorPresenter unless @measuredVerticalScrollbarWidth is verticalScrollbarWidth oldVerticalScrollbarWidth = @measuredVerticalScrollbarWidth @measuredVerticalScrollbarWidth = verticalScrollbarWidth - @model.setVerticalScrollbarWidth(verticalScrollbarWidth) @shouldUpdateScrollbarsState = true @shouldUpdateVerticalScrollState = true @shouldUpdateHorizontalScrollState = true @@ -933,7 +966,6 @@ class TextEditorPresenter setExplicitHeight: (explicitHeight) -> unless @explicitHeight is explicitHeight @explicitHeight = explicitHeight - @model.setHeight(explicitHeight) @updateHeight() @shouldUpdateVerticalScrollState = true @shouldUpdateScrollbarsState = true @@ -955,10 +987,10 @@ class TextEditorPresenter @updateEndRow() setContentFrameWidth: (contentFrameWidth) -> - unless @contentFrameWidth is contentFrameWidth + if @contentFrameWidth isnt contentFrameWidth or @editorWidthInChars? oldContentFrameWidth = @contentFrameWidth @contentFrameWidth = contentFrameWidth - @model.setWidth(contentFrameWidth) + @editorWidthInChars = null @updateScrollbarDimensions() @updateClientWidth() @shouldUpdateVerticalScrollState = true @@ -1016,6 +1048,9 @@ class TextEditorPresenter @gutterWidth = gutterWidth @updateOverlaysState() + getGutterWidth: -> + @gutterWidth + setLineHeight: (lineHeight) -> unless @lineHeight is lineHeight @lineHeight = lineHeight @@ -1176,29 +1211,30 @@ class TextEditorPresenter @emitDidUpdateState() + fetchVisibleDecorations: -> + @visibleDecorations = [] + + for row in @getScreenRows() + for markerId, decorations of @model.decorationsForScreenRowRange(row, row) + range = @model.getMarker(markerId).getScreenRange() + for decoration in decorations + @visibleDecorations.push({decoration, range}) + updateLineDecorations: -> @rangesByDecorationId = {} @lineDecorationsByScreenRow = {} @lineNumberDecorationsByScreenRow = {} @customGutterDecorationsByGutterNameAndScreenRow = {} - return unless 0 <= @startRow <= @endRow <= Infinity - - for row in @getScreenRows() - for markerId, decorations of @model.decorationsForScreenRowRange(row, row) - range = @model.getMarker(markerId).getScreenRange() - for decoration in decorations - if decoration.isType('line') or decoration.isType('gutter') - @addToLineDecorationCaches(decoration, range) + for {decoration, range} in @visibleDecorations + if decoration.isType('line') or decoration.isType('gutter') + @addToLineDecorationCaches(decoration, range) updateHighlightDecorations: -> @visibleHighlights = {} - return unless 0 <= @startRow <= @endRow <= Infinity - - for markerId, decorations of @model.decorationsForScreenRowRange(@startRow, @endRow - 1) - range = @model.getMarker(markerId).getScreenRange() - for decoration in decorations when decoration.isType('highlight') + for {decoration, range} in @visibleDecorations + if decoration.isType('highlight') @updateHighlightState(decoration, range) for tileId, tileState of @state.content.tiles @@ -1452,3 +1488,179 @@ class TextEditorPresenter @startBlinkingCursorsAfterDelay ?= _.debounce(@startBlinkingCursors, @getCursorBlinkResumeDelay()) @startBlinkingCursorsAfterDelay() @emitDidUpdateState() + + requestAutoscroll: (position) -> + @pendingScrollLogicalPosition = position + @pendingScrollTop = null + @pendingScrollLeft = null + + @shouldUpdateCursorsState = true + @shouldUpdateCustomGutterDecorationState = true + @shouldUpdateDecorations = true + @shouldUpdateHiddenInputState = true + @shouldUpdateHorizontalScrollState = true + @shouldUpdateLinesState = true + @shouldUpdateLineNumbersState = true + @shouldUpdateOverlaysState = true + @shouldUpdateScrollPosition = true + @shouldUpdateVerticalScrollState = true + + @emitDidUpdateState() + + getVerticalScrollMarginInPixels: -> + @model.getVerticalScrollMargin() * @lineHeight + + getHorizontalScrollMarginInPixels: -> + @model.getHorizontalScrollMargin() * @baseCharacterWidth + + getVerticalScrollbarWidth: -> + @verticalScrollbarWidth + + getHorizontalScrollbarHeight: -> + @horizontalScrollbarHeight + + commitPendingLogicalScrollPosition: -> + return unless @pendingScrollLogicalPosition? + + {screenRange, options} = @pendingScrollLogicalPosition + + verticalScrollMarginInPixels = @getVerticalScrollMarginInPixels() + horizontalScrollMarginInPixels = @getHorizontalScrollMarginInPixels() + + {top, left} = @pixelRectForScreenRange(new Range(screenRange.start, screenRange.start)) + {top: endTop, left: endLeft, height: endHeight} = @pixelRectForScreenRange(new Range(screenRange.end, screenRange.end)) + bottom = endTop + endHeight + right = endLeft + + top += @scrollTop + bottom += @scrollTop + left += @scrollLeft + right += @scrollLeft + + if options?.center + desiredScrollCenter = (top + bottom) / 2 + unless @getScrollTop() < desiredScrollCenter < @getScrollBottom() + desiredScrollTop = desiredScrollCenter - @getClientHeight() / 2 + desiredScrollBottom = desiredScrollCenter + @getClientHeight() / 2 + else + desiredScrollTop = top - verticalScrollMarginInPixels + desiredScrollBottom = bottom + verticalScrollMarginInPixels + + desiredScrollLeft = left - horizontalScrollMarginInPixels + desiredScrollRight = right + horizontalScrollMarginInPixels + + if options?.reversed ? true + if desiredScrollBottom > @getScrollBottom() + @setScrollBottom(desiredScrollBottom) + if desiredScrollTop < @getScrollTop() + @setScrollTop(desiredScrollTop) + + if desiredScrollRight > @getScrollRight() + @setScrollRight(desiredScrollRight) + if desiredScrollLeft < @getScrollLeft() + @setScrollLeft(desiredScrollLeft) + else + if desiredScrollTop < @getScrollTop() + @setScrollTop(desiredScrollTop) + if desiredScrollBottom > @getScrollBottom() + @setScrollBottom(desiredScrollBottom) + + if desiredScrollLeft < @getScrollLeft() + @setScrollLeft(desiredScrollLeft) + if desiredScrollRight > @getScrollRight() + @setScrollRight(desiredScrollRight) + + @pendingScrollLogicalPosition = null + + commitPendingScrollLeftPosition: -> + return unless @pendingScrollLeft? + + scrollLeft = @constrainScrollLeft(@pendingScrollLeft) + if scrollLeft isnt @scrollLeft and not Number.isNaN(scrollLeft) + @realScrollLeft = scrollLeft + @scrollLeft = Math.round(scrollLeft) + @scrollColumn = Math.round(@scrollLeft / @baseCharacterWidth) + @model.setScrollColumn(@scrollColumn) + + @emitter.emit 'did-change-scroll-left', @scrollLeft + + @pendingScrollLeft = null + + commitPendingScrollTopPosition: -> + return unless @pendingScrollTop? + + scrollTop = @constrainScrollTop(@pendingScrollTop) + if scrollTop isnt @scrollTop and not Number.isNaN(scrollTop) + @realScrollTop = scrollTop + @scrollTop = Math.round(scrollTop) + @scrollRow = Math.round(@scrollTop / @lineHeight) + @model.setScrollRow(@scrollRow) + + @didStartScrolling() + @emitter.emit 'did-change-scroll-top', @scrollTop + + @pendingScrollTop = null + + restoreScrollPosition: -> + return if @hasRestoredScrollPosition or not @hasPixelPositionRequirements() + + @setScrollTop(@scrollRow * @lineHeight) if @scrollRow? + @setScrollLeft(@scrollColumn * @baseCharacterWidth) if @scrollColumn? + + @hasRestoredScrollPosition = true + + updateScrollPosition: -> + @restoreScrollPosition() + @commitPendingLogicalScrollPosition() + @commitPendingScrollLeftPosition() + @commitPendingScrollTopPosition() + + canScrollLeftTo: (scrollLeft) -> + @scrollLeft isnt @constrainScrollLeft(scrollLeft) + + canScrollTopTo: (scrollTop) -> + @scrollTop isnt @constrainScrollTop(scrollTop) + + onDidChangeScrollTop: (callback) -> + @emitter.on 'did-change-scroll-top', callback + + onDidChangeScrollLeft: (callback) -> + @emitter.on 'did-change-scroll-left', callback + + getVisibleRowRange: -> + [@startRow, @endRow] + + screenPositionForPixelPosition: (pixelPosition) -> + targetTop = pixelPosition.top + targetLeft = pixelPosition.left + defaultCharWidth = @baseCharacterWidth + row = Math.floor(targetTop / @lineHeight) + targetLeft = 0 if row < 0 + targetLeft = Infinity if row > @model.getLastScreenRow() + row = Math.min(row, @model.getLastScreenRow()) + row = Math.max(0, row) + + left = 0 + column = 0 + + iterator = @model.tokenizedLineForScreenRow(row).getTokenIterator() + while iterator.next() + charWidths = @getScopedCharacterWidths(iterator.getScopes()) + value = iterator.getText() + valueIndex = 0 + while valueIndex < value.length + if iterator.isPairedCharacter() + char = value + charLength = 2 + valueIndex += 2 + else + char = value[valueIndex] + charLength = 1 + valueIndex++ + + charWidth = charWidths[char] ? defaultCharWidth + break if targetLeft <= left + (charWidth / 2) + left += charWidth + column += charLength + + new Point(row, column) diff --git a/src/text-editor.coffee b/src/text-editor.coffee index edd35ae93..f8f147862 100644 --- a/src/text-editor.coffee +++ b/src/text-editor.coffee @@ -1,8 +1,6 @@ _ = require 'underscore-plus' path = require 'path' -Serializable = require 'serializable' -Delegator = require 'delegato' -{includeDeprecatedAPIs, deprecate} = require 'grim' +Grim = require 'grim' {CompositeDisposable, Emitter} = require 'event-kit' {Point, Range} = TextBuffer = require 'text-buffer' LanguageMode = require './language-mode' @@ -56,11 +54,8 @@ GutterContainer = require './gutter-container' # soft wraps and folds to ensure your code interacts with them correctly. module.exports = class TextEditor extends Model - Serializable.includeInto(this) atom.deserializers.add(this) - Delegator.includeInto(this) - deserializing: false callDisplayBufferCreatedHook: false registerEditor: false buffer: null @@ -72,11 +67,20 @@ class TextEditor extends Model selectionFlashDuration: 500 gutterContainer: null - @delegatesMethods 'suggestedIndentForBufferRow', 'autoIndentBufferRow', 'autoIndentBufferRows', - 'autoDecreaseIndentForBufferRow', 'toggleLineCommentForBufferRow', 'toggleLineCommentsForBufferRows', - toProperty: 'languageMode' + @deserialize: (state) -> + try + displayBuffer = DisplayBuffer.deserialize(state.displayBuffer) + catch error + if error.syscall is 'read' + return # Error reading the file, don't deserialize an editor for it + else + throw error - constructor: ({@softTabs, initialLine, initialColumn, tabLength, softWrapped, @displayBuffer, buffer, registerEditor, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode}={}) -> + state.displayBuffer = displayBuffer + state.registerEditor = true + new this(state) + + constructor: ({@softTabs, @scrollRow, @scrollColumn, initialLine, initialColumn, tabLength, softWrapped, @displayBuffer, buffer, registerEditor, suppressCursorCreation, @mini, @placeholderText, lineNumberGutterVisible, largeFileMode}={}) -> super @emitter = new Emitter @@ -105,14 +109,6 @@ class TextEditor extends Model @setEncoding(atom.config.get('core.fileEncoding', scope: @getRootScopeDescriptor())) - @disposables.add @displayBuffer.onDidChangeScrollTop (scrollTop) => - @emit 'scroll-top-changed', scrollTop if includeDeprecatedAPIs - @emitter.emit 'did-change-scroll-top', scrollTop - - @disposables.add @displayBuffer.onDidChangeScrollLeft (scrollLeft) => - @emit 'scroll-left-changed', scrollLeft if includeDeprecatedAPIs - @emitter.emit 'did-change-scroll-left', scrollLeft - @gutterContainer = new GutterContainer(this) @lineNumberGutter = @gutterContainer.addGutter name: 'line-number' @@ -121,45 +117,25 @@ class TextEditor extends Model atom.workspace?.editorAdded(this) if registerEditor - serializeParams: -> + serialize: -> + deserializer: 'TextEditor' id: @id softTabs: @softTabs - scrollTop: @scrollTop - scrollLeft: @scrollLeft + scrollRow: @getScrollRow() + scrollColumn: @getScrollColumn() displayBuffer: @displayBuffer.serialize() - deserializeParams: (params) -> - try - displayBuffer = DisplayBuffer.deserialize(params.displayBuffer) - catch error - if error.syscall is 'read' - return # Error reading the file, don't deserialize an editor for it - else - throw error - - params.displayBuffer = displayBuffer - params.registerEditor = true - params - subscribeToBuffer: -> @buffer.retain() @disposables.add @buffer.onDidChangePath => unless atom.project.getPaths().length > 0 atom.project.setPaths([path.dirname(@getPath())]) - @emit "title-changed" if includeDeprecatedAPIs @emitter.emit 'did-change-title', @getTitle() - @emit "path-changed" if includeDeprecatedAPIs @emitter.emit 'did-change-path', @getPath() @disposables.add @buffer.onDidChangeEncoding => @emitter.emit 'did-change-encoding', @getEncoding() @disposables.add @buffer.onDidDestroy => @destroy() - # TODO: remove these when we remove the deprecations. They are old events. - if includeDeprecatedAPIs - @subscribe @buffer.onDidStopChanging => @emit "contents-modified" - @subscribe @buffer.onDidConflict => @emit "contents-conflicted" - @subscribe @buffer.onDidChangeModified => @emit "modified-status-changed" - @preserveCursorPositionOnBufferReload() subscribeToDisplayBuffer: -> @@ -168,22 +144,14 @@ class TextEditor extends Model @disposables.add @displayBuffer.onDidTokenize => @handleTokenization() @disposables.add @displayBuffer.onDidChange (e) => @mergeIntersectingSelections() - @emit 'screen-lines-changed', e if includeDeprecatedAPIs @emitter.emit 'did-change', e - # TODO: remove these when we remove the deprecations. Though, no one is likely using them - if includeDeprecatedAPIs - @subscribe @displayBuffer.onDidChangeSoftWrapped (softWrapped) => @emit 'soft-wrap-changed', softWrapped - @subscribe @displayBuffer.onDidAddDecoration (decoration) => @emit 'decoration-added', decoration - @subscribe @displayBuffer.onDidRemoveDecoration (decoration) => @emit 'decoration-removed', decoration - subscribeToTabTypeConfig: -> @tabTypeSubscription?.dispose() @tabTypeSubscription = atom.config.observe 'editor.tabType', scope: @getRootScopeDescriptor(), => @softTabs = @shouldUseSoftTabs(defaultValue: @softTabs) destroyed: -> - @unsubscribe() if includeDeprecatedAPIs @disposables.dispose() @tabTypeSubscription.dispose() selection.destroy() for selection in @selections.slice() @@ -459,10 +427,17 @@ class TextEditor extends Model @displayBuffer.onDidChangeCharacterWidths(callback) onDidChangeScrollTop: (callback) -> - @emitter.on 'did-change-scroll-top', callback + Grim.deprecate("This is now a view method. Call TextEditorElement::onDidChangeScrollTop instead.") + + atom.views.getView(this).onDidChangeScrollTop(callback) onDidChangeScrollLeft: (callback) -> - @emitter.on 'did-change-scroll-left', callback + Grim.deprecate("This is now a view method. Call TextEditorElement::onDidChangeScrollLeft instead.") + + atom.views.getView(this).onDidChangeScrollLeft(callback) + + onDidRequestAutoscroll: (callback) -> + @displayBuffer.onDidRequestAutoscroll(callback) # TODO Remove once the tabs package no longer uses .on subscriptions onDidChangeIcon: (callback) -> @@ -551,6 +526,10 @@ class TextEditor extends Model setEditorWidthInChars: (editorWidthInChars) -> @displayBuffer.setEditorWidthInChars(editorWidthInChars) + # Returns the editor width in characters. + getEditorWidthInChars: -> + @displayBuffer.getEditorWidthInChars() + ### Section: File Details ### @@ -779,7 +758,6 @@ class TextEditor extends Model (selection) => range = selection.insertText(text, options) didInsertEvent = {text, range} - @emit('did-insert-text', didInsertEvent) if includeDeprecatedAPIs @emitter.emit 'did-insert-text', didInsertEvent range , groupingInterval @@ -1131,10 +1109,14 @@ class TextEditor extends Model @buffer.transact(groupingInterval, fn) # Deprecated: Start an open-ended transaction. - beginTransaction: (groupingInterval) -> @buffer.beginTransaction(groupingInterval) + beginTransaction: (groupingInterval) -> + Grim.deprecate('Transactions should be performed via TextEditor::transact only') + @buffer.beginTransaction(groupingInterval) # Deprecated: Commit an open-ended transaction started with {::beginTransaction}. - commitTransaction: -> @buffer.commitTransaction() + commitTransaction: -> + Grim.deprecate('Transactions should be performed via TextEditor::transact only') + @buffer.commitTransaction() # Extended: Abort an open transaction, undoing any operations performed so far # within the transaction. @@ -1326,9 +1308,6 @@ class TextEditor extends Model # # Returns a {Decoration} object decorateMarker: (marker, decorationParams) -> - if includeDeprecatedAPIs and decorationParams.type is 'gutter' and not decorationParams.gutterName - deprecate("Decorations of `type: 'gutter'` have been renamed to `type: 'line-number'`.") - decorationParams.type = 'line-number' @displayBuffer.decorateMarker(marker, decorationParams) # Essential: Get all the decorations within a screen row range. @@ -1758,7 +1737,6 @@ class TextEditor extends Model @decorateMarker(marker, type: 'line-number', class: 'cursor-line') @decorateMarker(marker, type: 'line-number', class: 'cursor-line-no-selection', onlyHead: true, onlyEmpty: true) @decorateMarker(marker, type: 'line', class: 'cursor-line', onlyEmpty: true) - @emit 'cursor-added', cursor if includeDeprecatedAPIs @emitter.emit 'did-add-cursor', cursor cursor @@ -2247,7 +2225,6 @@ class TextEditor extends Model if selection.intersectsBufferRange(selectionBufferRange) return selection else - @emit 'selection-added', selection if includeDeprecatedAPIs @emitter.emit 'did-add-selection', selection selection @@ -2264,11 +2241,11 @@ class TextEditor extends Model @consolidateSelections() @getLastSelection().clear(options) - # Reduce multiple selections to the most recently added selection. + # Reduce multiple selections to the least recently added selection. consolidateSelections: -> selections = @getSelections() if selections.length > 1 - selection.destroy() for selection in selections[0...-1] + selection.destroy() for selection in selections[1...(selections.length)] true else false @@ -2367,10 +2344,6 @@ class TextEditor extends Model # Returns a {Boolean} or undefined if no non-comment lines had leading # whitespace. usesSoftTabs: -> - # FIXME Remove once this can be specified as a scoped setting in the - # language-make package - return false if @getGrammar()?.scopeName is 'source.makefile' - for bufferRow in [0..@buffer.getLastRow()] continue if @displayBuffer.tokenizedBuffer.tokenizedLineForRow(bufferRow).isComment() @@ -2661,7 +2634,6 @@ class TextEditor extends Model range = selection.insertText(text, options) didInsertEvent = {text, range} - @emit('did-insert-text', didInsertEvent) if includeDeprecatedAPIs @emitter.emit 'did-insert-text', didInsertEvent # Essential: For each selection, if the selection is empty, cut all characters @@ -2881,25 +2853,27 @@ class TextEditor extends Model scrollToScreenPosition: (screenPosition, options) -> @displayBuffer.scrollToScreenPosition(screenPosition, options) - # Essential: Scrolls the editor to the top scrollToTop: -> - @setScrollTop(0) + Grim.deprecate("This is now a view method. Call TextEditorElement::scrollToTop instead.") + + atom.views.getView(this).scrollToTop() - # Essential: Scrolls the editor to the bottom scrollToBottom: -> - @setScrollBottom(Infinity) + Grim.deprecate("This is now a view method. Call TextEditorElement::scrollToTop instead.") + + atom.views.getView(this).scrollToBottom() scrollToScreenRange: (screenRange, options) -> @displayBuffer.scrollToScreenRange(screenRange, options) - horizontallyScrollable: -> @displayBuffer.horizontallyScrollable() + getHorizontalScrollbarHeight: -> + Grim.deprecate("This is now a view method. Call TextEditorElement::getHorizontalScrollbarHeight instead.") - verticallyScrollable: -> @displayBuffer.verticallyScrollable() + atom.views.getView(this).getHorizontalScrollbarHeight() - getHorizontalScrollbarHeight: -> @displayBuffer.getHorizontalScrollbarHeight() - setHorizontalScrollbarHeight: (height) -> @displayBuffer.setHorizontalScrollbarHeight(height) + getVerticalScrollbarWidth: -> + Grim.deprecate("This is now a view method. Call TextEditorElement::getVerticalScrollbarWidth instead.") - getVerticalScrollbarWidth: -> @displayBuffer.getVerticalScrollbarWidth() - setVerticalScrollbarWidth: (width) -> @displayBuffer.setVerticalScrollbarWidth(width) + atom.views.getView(this).getVerticalScrollbarWidth() pageUp: -> @moveUp(@getRowsPerPage()) @@ -2962,25 +2936,21 @@ class TextEditor extends Model @placeholderText = placeholderText @emitter.emit 'did-change-placeholder-text', @placeholderText - getFirstVisibleScreenRow: (suppressDeprecation) -> - unless suppressDeprecation - deprecate("This is now a view method. Call TextEditorElement::getFirstVisibleScreenRow instead.") - @getVisibleRowRange()[0] + getFirstVisibleScreenRow: -> + deprecate("This is now a view method. Call TextEditorElement::getFirstVisibleScreenRow instead.") + atom.views.getView(this).getVisibleRowRange()[0] - getLastVisibleScreenRow: (suppressDeprecation) -> - unless suppressDeprecation - deprecate("This is now a view method. Call TextEditorElement::getLastVisibleScreenRow instead.") - @getVisibleRowRange()[1] + getLastVisibleScreenRow: -> + Grim.deprecate("This is now a view method. Call TextEditorElement::getLastVisibleScreenRow instead.") + atom.views.getView(this).getVisibleRowRange()[1] - pixelPositionForBufferPosition: (bufferPosition, suppressDeprecation) -> - unless suppressDeprecation - deprecate("This method is deprecated on the model layer. Use `TextEditorElement::pixelPositionForBufferPosition` instead") - @displayBuffer.pixelPositionForBufferPosition(bufferPosition) + pixelPositionForBufferPosition: (bufferPosition) -> + Grim.deprecate("This method is deprecated on the model layer. Use `TextEditorElement::pixelPositionForBufferPosition` instead") + atom.views.getView(this).pixelPositionForBufferPosition(bufferPosition) - pixelPositionForScreenPosition: (screenPosition, suppressDeprecation) -> - unless suppressDeprecation - deprecate("This method is deprecated on the model layer. Use `TextEditorElement::pixelPositionForScreenPosition` instead") - @displayBuffer.pixelPositionForScreenPosition(screenPosition) + pixelPositionForScreenPosition: (screenPosition) -> + Grim.deprecate("This method is deprecated on the model layer. Use `TextEditorElement::pixelPositionForScreenPosition` instead") + atom.views.getView(this).pixelPositionForScreenPosition(screenPosition) getSelectionMarkerAttributes: -> {type: 'selection', editorId: @id, invalidate: 'never', maintainHistory: true} @@ -3006,38 +2976,110 @@ class TextEditor extends Model getDefaultCharWidth: -> @displayBuffer.getDefaultCharWidth() setDefaultCharWidth: (defaultCharWidth) -> @displayBuffer.setDefaultCharWidth(defaultCharWidth) - setHeight: (height) -> @displayBuffer.setHeight(height) - getHeight: -> @displayBuffer.getHeight() + setHeight: (height, reentrant=false) -> + if reentrant + @displayBuffer.setHeight(height) + else + Grim.deprecate("This is now a view method. Call TextEditorElement::setHeight instead.") + atom.views.getView(this).setHeight(height) + + getHeight: -> + Grim.deprecate("This is now a view method. Call TextEditorElement::getHeight instead.") + @displayBuffer.getHeight() getClientHeight: -> @displayBuffer.getClientHeight() - setWidth: (width) -> @displayBuffer.setWidth(width) - getWidth: -> @displayBuffer.getWidth() + setWidth: (width, reentrant=false) -> + if reentrant + @displayBuffer.setWidth(width) + else + Grim.deprecate("This is now a view method. Call TextEditorElement::setWidth instead.") + atom.views.getView(this).setWidth(width) - getScrollTop: -> @displayBuffer.getScrollTop() - setScrollTop: (scrollTop) -> @displayBuffer.setScrollTop(scrollTop) + getWidth: -> + Grim.deprecate("This is now a view method. Call TextEditorElement::getWidth instead.") + @displayBuffer.getWidth() - getScrollBottom: -> @displayBuffer.getScrollBottom() - setScrollBottom: (scrollBottom) -> @displayBuffer.setScrollBottom(scrollBottom) + getScrollRow: -> @scrollRow + setScrollRow: (@scrollRow) -> - getScrollLeft: -> @displayBuffer.getScrollLeft() - setScrollLeft: (scrollLeft) -> @displayBuffer.setScrollLeft(scrollLeft) + getScrollColumn: -> @scrollColumn + setScrollColumn: (@scrollColumn) -> - getScrollRight: -> @displayBuffer.getScrollRight() - setScrollRight: (scrollRight) -> @displayBuffer.setScrollRight(scrollRight) + getScrollTop: -> + Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollTop instead.") - getScrollHeight: -> @displayBuffer.getScrollHeight() - getScrollWidth: -> @displayBuffer.getScrollWidth() + atom.views.getView(this).getScrollTop() - getVisibleRowRange: -> @displayBuffer.getVisibleRowRange() + setScrollTop: (scrollTop) -> + Grim.deprecate("This is now a view method. Call TextEditorElement::setScrollTop instead.") - intersectsVisibleRowRange: (startRow, endRow) -> @displayBuffer.intersectsVisibleRowRange(startRow, endRow) + atom.views.getView(this).setScrollTop(scrollTop) - selectionIntersectsVisibleRowRange: (selection) -> @displayBuffer.selectionIntersectsVisibleRowRange(selection) + getScrollBottom: -> + Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollBottom instead.") - screenPositionForPixelPosition: (pixelPosition) -> @displayBuffer.screenPositionForPixelPosition(pixelPosition) + atom.views.getView(this).getScrollBottom() - pixelRectForScreenRange: (screenRange) -> @displayBuffer.pixelRectForScreenRange(screenRange) + setScrollBottom: (scrollBottom) -> + Grim.deprecate("This is now a view method. Call TextEditorElement::setScrollBottom instead.") + + atom.views.getView(this).setScrollBottom(scrollBottom) + + getScrollLeft: -> + Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollLeft instead.") + + atom.views.getView(this).getScrollLeft() + + setScrollLeft: (scrollLeft) -> + Grim.deprecate("This is now a view method. Call TextEditorElement::setScrollLeft instead.") + + atom.views.getView(this).setScrollLeft(scrollLeft) + + getScrollRight: -> + Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollRight instead.") + + atom.views.getView(this).getScrollRight() + + setScrollRight: (scrollRight) -> + Grim.deprecate("This is now a view method. Call TextEditorElement::setScrollRight instead.") + + atom.views.getView(this).setScrollRight(scrollRight) + + getScrollHeight: -> + Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollHeight instead.") + + atom.views.getView(this).getScrollHeight() + + getScrollWidth: -> + Grim.deprecate("This is now a view method. Call TextEditorElement::getScrollWidth instead.") + + atom.views.getView(this).getScrollWidth() + + getVisibleRowRange: -> + Grim.deprecate("This is now a view method. Call TextEditorElement::getVisibleRowRange instead.") + + atom.views.getView(this).getVisibleRowRange() + + intersectsVisibleRowRange: (startRow, endRow) -> + Grim.deprecate("This is now a view method. Call TextEditorElement::intersectsVisibleRowRange instead.") + + atom.views.getView(this).intersectsVisibleRowRange(startRow, endRow) + + selectionIntersectsVisibleRowRange: (selection) -> + Grim.deprecate("This is now a view method. Call TextEditorElement::selectionIntersectsVisibleRowRange instead.") + + atom.views.getView(this).selectionIntersectsVisibleRowRange(selection) + + screenPositionForPixelPosition: (pixelPosition) -> + Grim.deprecate("This is now a view method. Call TextEditorElement::screenPositionForPixelPosition instead.") + + atom.views.getView(this).screenPositionForPixelPosition(pixelPosition) + + pixelRectForScreenRange: (screenRange) -> + Grim.deprecate("This is now a view method. Call TextEditorElement::pixelRectForScreenRange instead.") + + atom.views.getView(this).pixelRectForScreenRange(screenRange) ### Section: Utility @@ -3052,232 +3094,21 @@ class TextEditor extends Model result = true cancel = -> result = false willInsertEvent = {cancel, text} - @emit('will-insert-text', willInsertEvent) if includeDeprecatedAPIs @emitter.emit 'will-insert-text', willInsertEvent result -if includeDeprecatedAPIs - TextEditor.delegatesProperties '$lineHeightInPixels', '$defaultCharWidth', '$height', '$width', - '$verticalScrollbarWidth', '$horizontalScrollbarHeight', '$scrollTop', '$scrollLeft', - toProperty: 'displayBuffer' + ### + Section: Language Mode Delegated Methods + ### - TextEditor::joinLine = -> - deprecate("Use TextEditor::joinLines() instead") - @joinLines() + suggestedIndentForBufferRow: (bufferRow, options) -> @languageMode.suggestedIndentForBufferRow(bufferRow, options) - TextEditor::scopesAtCursor = -> - deprecate 'Use editor.getLastCursor().getScopeDescriptor() instead' - @getLastCursor().getScopeDescriptor().getScopesArray() + autoIndentBufferRow: (bufferRow, options) -> @languageMode.autoIndentBufferRow(bufferRow, options) - TextEditor::getCursorScopes = -> - deprecate 'Use editor.getLastCursor().getScopeDescriptor() instead' - @scopesAtCursor() + autoIndentBufferRows: (startRow, endRow) -> @languageMode.autoIndentBufferRows(startRow, endRow) - TextEditor::getUri = -> - deprecate("Use `::getURI` instead") - @getURI() + autoDecreaseIndentForBufferRow: (bufferRow) -> @languageMode.autoDecreaseIndentForBufferRow(bufferRow) - TextEditor::lineForBufferRow = (bufferRow) -> - deprecate 'Use TextEditor::lineTextForBufferRow(bufferRow) instead' - @lineTextForBufferRow(bufferRow) + toggleLineCommentForBufferRow: (row) -> @languageMode.toggleLineCommentsForBufferRow(row) - TextEditor::lineForScreenRow = (screenRow) -> - deprecate "TextEditor::tokenizedLineForScreenRow(bufferRow) is the new name. But it's private. Try to use TextEditor::lineTextForScreenRow instead" - @tokenizedLineForScreenRow(screenRow) - - TextEditor::linesForScreenRows = (start, end) -> - deprecate "Use TextEditor::tokenizedLinesForScreenRows instead" - @tokenizedLinesForScreenRows(start, end) - - TextEditor::lineLengthForBufferRow = (row) -> - deprecate "Use editor.lineTextForBufferRow(row).length instead" - @lineTextForBufferRow(row).length - - TextEditor::duplicateLine = -> - deprecate("Use TextEditor::duplicateLines() instead") - @duplicateLines() - - TextEditor::scopesForBufferPosition = (bufferPosition) -> - deprecate 'Use ::scopeDescriptorForBufferPosition instead. The return value has changed! It now returns a `ScopeDescriptor`' - @scopeDescriptorForBufferPosition(bufferPosition).getScopesArray() - - TextEditor::toggleSoftWrap = -> - deprecate("Use TextEditor::toggleSoftWrapped instead") - @toggleSoftWrapped() - - TextEditor::setSoftWrap = (softWrapped) -> - deprecate("Use TextEditor::setSoftWrapped instead") - @setSoftWrapped(softWrapped) - - TextEditor::backspaceToBeginningOfWord = -> - deprecate("Use TextEditor::deleteToBeginningOfWord() instead") - @deleteToBeginningOfWord() - - TextEditor::backspaceToBeginningOfLine = -> - deprecate("Use TextEditor::deleteToBeginningOfLine() instead") - @deleteToBeginningOfLine() - - TextEditor::getGutterDecorations = (propertyFilter) -> - deprecate("Use ::getLineNumberDecorations instead") - @getLineNumberDecorations(propertyFilter) - - TextEditor::getCursorScreenRow = -> - deprecate('Use `editor.getCursorScreenPosition().row` instead') - @getCursorScreenPosition().row - - TextEditor::moveCursorUp = (lineCount) -> - deprecate("Use TextEditor::moveUp() instead") - @moveUp(lineCount) - - TextEditor::moveCursorDown = (lineCount) -> - deprecate("Use TextEditor::moveDown() instead") - @moveDown(lineCount) - - TextEditor::moveCursorLeft = -> - deprecate("Use TextEditor::moveLeft() instead") - @moveLeft() - - TextEditor::moveCursorRight = -> - deprecate("Use TextEditor::moveRight() instead") - @moveRight() - - TextEditor::moveCursorToBeginningOfLine = -> - deprecate("Use TextEditor::moveToBeginningOfLine() instead") - @moveToBeginningOfLine() - - TextEditor::moveCursorToBeginningOfScreenLine = -> - deprecate("Use TextEditor::moveToBeginningOfScreenLine() instead") - @moveToBeginningOfScreenLine() - - TextEditor::moveCursorToFirstCharacterOfLine = -> - deprecate("Use TextEditor::moveToFirstCharacterOfLine() instead") - @moveToFirstCharacterOfLine() - - TextEditor::moveCursorToEndOfLine = -> - deprecate("Use TextEditor::moveToEndOfLine() instead") - @moveToEndOfLine() - - TextEditor::moveCursorToEndOfScreenLine = -> - deprecate("Use TextEditor::moveToEndOfScreenLine() instead") - @moveToEndOfScreenLine() - - TextEditor::moveCursorToBeginningOfWord = -> - deprecate("Use TextEditor::moveToBeginningOfWord() instead") - @moveToBeginningOfWord() - - TextEditor::moveCursorToEndOfWord = -> - deprecate("Use TextEditor::moveToEndOfWord() instead") - @moveToEndOfWord() - - TextEditor::moveCursorToTop = -> - deprecate("Use TextEditor::moveToTop() instead") - @moveToTop() - - TextEditor::moveCursorToBottom = -> - deprecate("Use TextEditor::moveToBottom() instead") - @moveToBottom() - - TextEditor::moveCursorToBeginningOfNextWord = -> - deprecate("Use TextEditor::moveToBeginningOfNextWord() instead") - @moveToBeginningOfNextWord() - - TextEditor::moveCursorToPreviousWordBoundary = -> - deprecate("Use TextEditor::moveToPreviousWordBoundary() instead") - @moveToPreviousWordBoundary() - - TextEditor::moveCursorToNextWordBoundary = -> - deprecate("Use TextEditor::moveToNextWordBoundary() instead") - @moveToNextWordBoundary() - - TextEditor::moveCursorToBeginningOfNextParagraph = -> - deprecate("Use TextEditor::moveToBeginningOfNextParagraph() instead") - @moveToBeginningOfNextParagraph() - - TextEditor::moveCursorToBeginningOfPreviousParagraph = -> - deprecate("Use TextEditor::moveToBeginningOfPreviousParagraph() instead") - @moveToBeginningOfPreviousParagraph() - - TextEditor::getCursor = -> - deprecate("Use TextEditor::getLastCursor() instead") - @getLastCursor() - - TextEditor::selectLine = -> - deprecate('Use TextEditor::selectLinesContainingCursors instead') - @selectLinesContainingCursors() - - TextEditor::selectWord = -> - deprecate('Use TextEditor::selectWordsContainingCursors instead') - @selectWordsContainingCursors() - - TextEditor::getSelection = (index) -> - if index? - deprecate("Use TextEditor::getSelections()[index] instead when getting a specific selection") - @getSelections()[index] - else - deprecate("Use TextEditor::getLastSelection() instead") - @getLastSelection() - - TextEditor::getSoftWrapped = -> - deprecate("Use TextEditor::isSoftWrapped instead") - @displayBuffer.isSoftWrapped() - - EmitterMixin = require('emissary').Emitter - TextEditor::on = (eventName) -> - switch eventName - when 'title-changed' - deprecate("Use TextEditor::onDidChangeTitle instead") - when 'path-changed' - deprecate("Use TextEditor::onDidChangePath instead") - when 'modified-status-changed' - deprecate("Use TextEditor::onDidChangeModified instead") - when 'soft-wrap-changed' - deprecate("Use TextEditor::onDidChangeSoftWrapped instead") - when 'grammar-changed' - deprecate("Use TextEditor::onDidChangeGrammar instead") - when 'character-widths-changed' - deprecate("Use TextEditor::onDidChangeCharacterWidths instead") - when 'contents-modified' - deprecate("Use TextEditor::onDidStopChanging instead") - when 'contents-conflicted' - deprecate("Use TextEditor::onDidConflict instead") - - when 'will-insert-text' - deprecate("Use TextEditor::onWillInsertText instead") - when 'did-insert-text' - deprecate("Use TextEditor::onDidInsertText instead") - - when 'cursor-added' - deprecate("Use TextEditor::onDidAddCursor instead") - when 'cursor-removed' - deprecate("Use TextEditor::onDidRemoveCursor instead") - when 'cursor-moved' - deprecate("Use TextEditor::onDidChangeCursorPosition instead") - - when 'selection-added' - deprecate("Use TextEditor::onDidAddSelection instead") - when 'selection-removed' - deprecate("Use TextEditor::onDidRemoveSelection instead") - when 'selection-screen-range-changed' - deprecate("Use TextEditor::onDidChangeSelectionRange instead") - - when 'decoration-added' - deprecate("Use TextEditor::onDidAddDecoration instead") - when 'decoration-removed' - deprecate("Use TextEditor::onDidRemoveDecoration instead") - when 'decoration-updated' - deprecate("Use Decoration::onDidChangeProperties instead. You will get the decoration back from `TextEditor::decorateMarker()`") - when 'decoration-changed' - deprecate("Use Marker::onDidChange instead. e.g. `editor::decorateMarker(...).getMarker().onDidChange()`") - - when 'screen-lines-changed' - deprecate("Use TextEditor::onDidChange instead") - - when 'scroll-top-changed' - deprecate("Use TextEditor::onDidChangeScrollTop instead") - when 'scroll-left-changed' - deprecate("Use TextEditor::onDidChangeScrollLeft instead") - - else - deprecate("TextEditor::on is deprecated. Use documented event subscription methods instead.") - - EmitterMixin::on.apply(this, arguments) + toggleLineCommentsForBufferRows: (start, end) -> @languageMode.toggleLineCommentsForBufferRows(start, end) diff --git a/src/theme-manager.coffee b/src/theme-manager.coffee index 5ac28b003..0d329cea7 100644 --- a/src/theme-manager.coffee +++ b/src/theme-manager.coffee @@ -3,7 +3,6 @@ _ = require 'underscore-plus' {Emitter, Disposable, CompositeDisposable} = require 'event-kit' {File} = require 'pathwatcher' fs = require 'fs-plus' -Grim = require 'grim' # Extended: Handles loading and activating available themes. # @@ -16,35 +15,6 @@ class ThemeManager @lessCache = null @initialLoadComplete = false @packageManager.registerPackageActivator(this, ['theme']) - @sheetsByStyleElement = new WeakMap - - stylesElement = document.head.querySelector('atom-styles') - stylesElement.onDidAddStyleElement @styleElementAdded.bind(this) - stylesElement.onDidRemoveStyleElement @styleElementRemoved.bind(this) - stylesElement.onDidUpdateStyleElement @styleElementUpdated.bind(this) - - styleElementAdded: (styleElement) -> - {sheet} = styleElement - @sheetsByStyleElement.set(styleElement, sheet) - @emit 'stylesheet-added', sheet if Grim.includeDeprecatedAPIs - @emitter.emit 'did-add-stylesheet', sheet - @emit 'stylesheets-changed' if Grim.includeDeprecatedAPIs - @emitter.emit 'did-change-stylesheets' - - styleElementRemoved: (styleElement) -> - sheet = @sheetsByStyleElement.get(styleElement) - @emit 'stylesheet-removed', sheet if Grim.includeDeprecatedAPIs - @emitter.emit 'did-remove-stylesheet', sheet - @emit 'stylesheets-changed' if Grim.includeDeprecatedAPIs - @emitter.emit 'did-change-stylesheets' - - styleElementUpdated: ({sheet}) -> - @emit 'stylesheet-removed', sheet if Grim.includeDeprecatedAPIs - @emitter.emit 'did-remove-stylesheet', sheet - @emit 'stylesheet-added', sheet if Grim.includeDeprecatedAPIs - @emitter.emit 'did-add-stylesheet', sheet - @emit 'stylesheets-changed' if Grim.includeDeprecatedAPIs - @emitter.emit 'did-change-stylesheets' ### Section: Event Subscription @@ -56,7 +26,6 @@ class ThemeManager # * `callback` {Function} onDidChangeActiveThemes: (callback) -> @emitter.on 'did-change-active-themes', callback - @emitter.on 'did-reload-all', callback # TODO: Remove once deprecated pre-1.0 APIs are gone ### Section: Accessing Available Themes @@ -96,6 +65,13 @@ class ThemeManager Section: Managing Enabled Themes ### + warnForNonExistentThemes: -> + themeNames = atom.config.get('core.themes') ? [] + themeNames = [themeNames] unless _.isArray(themeNames) + for themeName in themeNames + unless themeName and typeof themeName is 'string' and atom.packages.resolvePackagePath(themeName) + console.warn("Enabled theme '#{themeName}' is not installed.") + # Public: Get the enabled theme names from the config. # # Returns an array of theme names in the order that they should be activated. @@ -105,7 +81,6 @@ class ThemeManager themeNames = themeNames.filter (themeName) -> if themeName and typeof themeName is 'string' return true if atom.packages.resolvePackagePath(themeName) - console.warn("Enabled theme '#{themeName}' is not installed.") false # Use a built-in syntax and UI theme any time the configured themes are not @@ -264,6 +239,8 @@ class ThemeManager atom.config.observe 'core.themes', => @deactivateThemes() + @warnForNonExistentThemes() + @refreshLessCache() # Update cache for packages in core.themes config promises = [] @@ -279,7 +256,6 @@ class ThemeManager @loadUserStylesheet() @reloadBaseStylesheets() @initialLoadComplete = true - @emit 'reloaded' if Grim.includeDeprecatedAPIs @emitter.emit 'did-change-active-themes' resolve() @@ -292,10 +268,10 @@ class ThemeManager isInitialLoadComplete: -> @initialLoadComplete addActiveThemeClasses: -> - workspaceElement = atom.views.getView(atom.workspace) - for pack in @getActiveThemes() - workspaceElement.classList.add("theme-#{pack.name}") - return + if workspaceElement = atom.views.getView(atom.workspace) + for pack in @getActiveThemes() + workspaceElement.classList.add("theme-#{pack.name}") + return removeActiveThemeClasses: -> workspaceElement = atom.views.getView(atom.workspace) @@ -321,59 +297,3 @@ class ThemeManager themePaths.push(path.join(themePath, 'styles')) themePaths.filter (themePath) -> fs.isDirectorySync(themePath) - -if Grim.includeDeprecatedAPIs - EmitterMixin = require('emissary').Emitter - EmitterMixin.includeInto(ThemeManager) - - ThemeManager::on = (eventName) -> - switch eventName - when 'reloaded' - Grim.deprecate 'Use ThemeManager::onDidChangeActiveThemes instead' - when 'stylesheet-added' - Grim.deprecate 'Use ThemeManager::onDidAddStylesheet instead' - when 'stylesheet-removed' - Grim.deprecate 'Use ThemeManager::onDidRemoveStylesheet instead' - when 'stylesheet-updated' - Grim.deprecate 'Use ThemeManager::onDidUpdateStylesheet instead' - when 'stylesheets-changed' - Grim.deprecate 'Use ThemeManager::onDidChangeStylesheets instead' - else - Grim.deprecate 'ThemeManager::on is deprecated. Use event subscription methods instead.' - EmitterMixin::on.apply(this, arguments) - - ThemeManager::onDidReloadAll = (callback) -> - Grim.deprecate("Use `::onDidChangeActiveThemes` instead.") - @onDidChangeActiveThemes(callback) - - ThemeManager::onDidAddStylesheet = (callback) -> - Grim.deprecate("Use atom.styles.onDidAddStyleElement instead") - @emitter.on 'did-add-stylesheet', callback - - ThemeManager::onDidRemoveStylesheet = (callback) -> - Grim.deprecate("Use atom.styles.onDidRemoveStyleElement instead") - @emitter.on 'did-remove-stylesheet', callback - - ThemeManager::onDidUpdateStylesheet = (callback) -> - Grim.deprecate("Use atom.styles.onDidUpdateStyleElement instead") - @emitter.on 'did-update-stylesheet', callback - - ThemeManager::onDidChangeStylesheets = (callback) -> - Grim.deprecate("Use atom.styles.onDidAdd/RemoveStyleElement instead") - @emitter.on 'did-change-stylesheets', callback - - ThemeManager::getUserStylesheetPath = -> - Grim.deprecate("Call atom.styles.getUserStyleSheetPath() instead") - atom.styles.getUserStyleSheetPath() - - ThemeManager::getLoadedNames = -> - Grim.deprecate("Use `::getLoadedThemeNames` instead.") - @getLoadedThemeNames() - - ThemeManager::getActiveNames = -> - Grim.deprecate("Use `::getActiveThemeNames` instead.") - @getActiveThemeNames() - - ThemeManager::setEnabledThemes = (enabledThemeNames) -> - Grim.deprecate("Use `atom.config.set('core.themes', arrayOfThemeNames)` instead") - atom.config.set('core.themes', enabledThemeNames) diff --git a/src/tokenized-buffer.coffee b/src/tokenized-buffer.coffee index 4b260ebf5..8721b0547 100644 --- a/src/tokenized-buffer.coffee +++ b/src/tokenized-buffer.coffee @@ -2,18 +2,14 @@ _ = require 'underscore-plus' {CompositeDisposable, Emitter} = require 'event-kit' {Point, Range} = require 'text-buffer' {ScopeSelector} = require 'first-mate' -Serializable = require 'serializable' Model = require './model' TokenizedLine = require './tokenized-line' TokenIterator = require './token-iterator' Token = require './token' ScopeDescriptor = require './scope-descriptor' -Grim = require 'grim' module.exports = class TokenizedBuffer extends Model - Serializable.includeInto(this) - grammar: null currentGrammarScore: null buffer: null @@ -25,6 +21,10 @@ class TokenizedBuffer extends Model configSettings: null changeCount: 0 + @deserialize: (state) -> + state.buffer = atom.project.bufferForPathSync(state.bufferPath) + new this(state) + constructor: ({@buffer, @tabLength, @ignoreInvisibles, @largeFileMode}) -> @emitter = new Emitter @disposables = new CompositeDisposable @@ -41,16 +41,13 @@ class TokenizedBuffer extends Model destroyed: -> @disposables.dispose() - serializeParams: -> + serialize: -> + deserializer: 'TokenizedBuffer' bufferPath: @buffer.getPath() tabLength: @tabLength ignoreInvisibles: @ignoreInvisibles largeFileMode: @largeFileMode - deserializeParams: (params) -> - params.buffer = atom.project.bufferForPathSync(params.bufferPath) - params - observeGrammar: (callback) -> callback(@grammar) @onDidChangeGrammar(callback) @@ -128,7 +125,6 @@ class TokenizedBuffer extends Model @invalidateRow(0) @fullyTokenized = false event = {start: 0, end: lastRow, delta: 0} - @emit 'changed', event if Grim.includeDeprecatedAPIs @emitter.emit 'did-change', event setVisible: (@visible) -> @@ -191,7 +187,6 @@ class TokenizedBuffer extends Model [startRow, endRow] = @updateFoldableStatus(startRow, endRow) event = {start: startRow, end: endRow, delta: 0} - @emit 'changed', event if Grim.includeDeprecatedAPIs @emitter.emit 'did-change', event if @firstInvalidRow()? @@ -201,7 +196,6 @@ class TokenizedBuffer extends Model markTokenizationComplete: -> unless @fullyTokenized - @emit 'tokenized' if Grim.includeDeprecatedAPIs @emitter.emit 'did-tokenize' @fullyTokenized = true @@ -255,7 +249,6 @@ class TokenizedBuffer extends Model end -= delta event = {start, end, delta, bufferChange: e} - @emit 'changed', event if Grim.includeDeprecatedAPIs @emitter.emit 'did-change', event retokenizeWhitespaceRowsIfIndentLevelChanged: (row, increment) -> @@ -539,22 +532,6 @@ class TokenizedBuffer extends Model console.log row, line, line.length return -if Grim.includeDeprecatedAPIs - EmitterMixin = require('emissary').Emitter - - TokenizedBuffer::on = (eventName) -> - switch eventName - when 'changed' - Grim.deprecate("Use TokenizedBuffer::onDidChange instead") - when 'grammar-changed' - Grim.deprecate("Use TokenizedBuffer::onDidChangeGrammar instead") - when 'tokenized' - Grim.deprecate("Use TokenizedBuffer::onDidTokenize instead") - else - Grim.deprecate("TokenizedBuffer::on is deprecated. Use event subscription methods instead.") - - EmitterMixin::on.apply(this, arguments) - selectorMatchesAnyScope = (selector, scopes) -> targetClasses = selector.replace(/^\./, '').split('.') _.any scopes, (scope) -> diff --git a/src/workspace.coffee b/src/workspace.coffee index 3f53ae029..bda6e9b45 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -1,10 +1,7 @@ -{includeDeprecatedAPIs, deprecate} = require 'grim' _ = require 'underscore-plus' path = require 'path' {join} = path -Serializable = require 'serializable' {Emitter, Disposable, CompositeDisposable} = require 'event-kit' -Grim = require 'grim' fs = require 'fs-plus' DefaultDirectorySearcher = require './default-directory-searcher' Model = require './model' @@ -30,15 +27,22 @@ Task = require './task' module.exports = class Workspace extends Model atom.deserializers.add(this) - Serializable.includeInto(this) + + @deserialize: (state) -> + return unless state? + + for packageName in state.packagesWithActiveGrammars ? [] + atom.packages.getLoadedPackage(packageName)?.loadGrammarsSync() + + state.paneContainer = PaneContainer.deserialize(state.paneContainer) + new this(state) constructor: (params) -> super - unless Grim.includeDeprecatedAPIs - @paneContainer = params?.paneContainer - @fullScreen = params?.fullScreen ? false - @destroyedItemURIs = params?.destroyedItemURIs ? [] + @paneContainer = params?.paneContainer + @fullScreen = params?.fullScreen ? false + @destroyedItemURIs = params?.destroyedItemURIs ? [] @emitter = new Emitter @openers = [] @@ -84,16 +88,9 @@ class Workspace extends Model @subscribeToFontSize() - # Called by the Serializable mixin during deserialization - deserializeParams: (params) -> - for packageName in params.packagesWithActiveGrammars ? [] - atom.packages.getLoadedPackage(packageName)?.loadGrammarsSync() - - params.paneContainer = PaneContainer.deserialize(params.paneContainer) - params - # Called by the Serializable mixin during serialization. - serializeParams: -> + serialize: -> + deserializer: 'Workspace' paneContainer: @paneContainer.serialize() fullScreen: atom.isFullScreen() packagesWithActiveGrammars: @getPackageNamesWithActiveGrammars() @@ -120,10 +117,11 @@ class Workspace extends Model _.uniq(packageNames) editorAdded: (editor) -> - @emit 'editor-created', editor if includeDeprecatedAPIs installShellCommands: -> - require('./command-installer').installShellCommandsInteractively() + CommandInstaller = require('./command-installer') + commandInstaller = new CommandInstaller(atom.getVersion()) + commandInstaller.installShellCommandsInteractively() subscribeToActiveItem: -> @updateWindowTitle() @@ -409,12 +407,6 @@ class Workspace extends Model # * `activatePane` A {Boolean} indicating whether to call {Pane::activate} on # the containing pane. Defaults to `true`. openSync: (uri='', options={}) -> - # TODO: Remove deprecated changeFocus option - if includeDeprecatedAPIs and options.changeFocus? - deprecate("The `changeFocus` option has been renamed to `activatePane`") - options.activatePane = options.changeFocus - delete options.changeFocus - {initialLine, initialColumn} = options activatePane = options.activatePane ? true @@ -430,12 +422,6 @@ class Workspace extends Model item openURIInPane: (uri, pane, options={}) -> - # TODO: Remove deprecated changeFocus option - if includeDeprecatedAPIs and options.changeFocus? - deprecate("The `changeFocus` option has been renamed to `activatePane`") - options.activatePane = options.changeFocus - delete options.changeFocus - activatePane = options.activatePane ? true if uri? @@ -475,7 +461,6 @@ class Workspace extends Model item.setCursorBufferPosition?([initialLine, initialColumn]) index = pane.getActiveItemIndex() - @emit "uri-opened" if includeDeprecatedAPIs @emitter.emit 'did-open', {uri, pane, item, index} item @@ -515,24 +500,8 @@ class Workspace extends Model # {Workspace::open} on the URI `quux-preview://foo/bar/baz.quux`. Then your opener # can check the protocol for quux-preview and only handle those URIs that match. addOpener: (opener) -> - if includeDeprecatedAPIs - packageName = @getCallingPackageName() - - wrappedOpener = (uri, options) -> - item = opener(uri, options) - if item? and typeof item.getUri is 'function' and typeof item.getURI isnt 'function' - Grim.deprecate("Pane item with class `#{item.constructor.name}` should implement `::getURI` instead of `::getUri`.", {packageName}) - if item? and typeof item.on is 'function' and typeof item.onDidChangeTitle isnt 'function' - Grim.deprecate("If you would like your pane item with class `#{item.constructor.name}` to support title change behavior, please implement a `::onDidChangeTitle()` method. `::on` methods for items are no longer supported. If not, ignore this message.", {packageName}) - if item? and typeof item.on is 'function' and typeof item.onDidChangeModified isnt 'function' - Grim.deprecate("If you would like your pane item with class `#{item.constructor.name}` to support modified behavior, please implement a `::onDidChangeModified()` method. If not, ignore this message. `::on` methods for items are no longer supported.", {packageName}) - item - - @openers.push(wrappedOpener) - new Disposable => _.remove(@openers, wrappedOpener) - else - @openers.push(opener) - new Disposable => _.remove(@openers, opener) + @openers.push(opener) + new Disposable => _.remove(@openers, opener) getOpeners: -> @openers @@ -933,7 +902,7 @@ class Workspace extends Model # # Returns a `Promise`. replace: (regex, replacementText, filePaths, iterator) -> - new Promise (resolve, reject) => + new Promise (resolve, reject) -> openPaths = (buffer.getPath() for buffer in atom.project.getBuffers()) outOfProcessPaths = _.difference(filePaths, openPaths) @@ -960,96 +929,3 @@ class Workspace extends Model inProcessFinished = true checkFinished() - -if includeDeprecatedAPIs - Workspace.properties - paneContainer: null - fullScreen: false - destroyedItemURIs: -> [] - - Object.defineProperty Workspace::, 'activePaneItem', - get: -> - Grim.deprecate "Use ::getActivePaneItem() instead of the ::activePaneItem property" - @getActivePaneItem() - - Object.defineProperty Workspace::, 'activePane', - get: -> - Grim.deprecate "Use ::getActivePane() instead of the ::activePane property" - @getActivePane() - - StackTraceParser = require 'stacktrace-parser' - - Workspace::getCallingPackageName = -> - error = new Error - Error.captureStackTrace(error) - stack = StackTraceParser.parse(error.stack) - - packagePaths = @getPackagePathsByPackageName() - - for i in [0...stack.length] - stackFramePath = stack[i].file - - # Empty when it was run from the dev console - return unless stackFramePath - - for packageName, packagePath of packagePaths - continue if stackFramePath is 'node.js' - relativePath = path.relative(packagePath, stackFramePath) - return packageName unless /^\.\./.test(relativePath) - return - - Workspace::getPackagePathsByPackageName = -> - packagePathsByPackageName = {} - for pack in atom.packages.getLoadedPackages() - packagePath = pack.path - if packagePath.indexOf('.atom/dev/packages') > -1 or packagePath.indexOf('.atom/packages') > -1 - packagePath = fs.realpathSync(packagePath) - packagePathsByPackageName[pack.name] = packagePath - packagePathsByPackageName - - Workspace::eachEditor = (callback) -> - deprecate("Use Workspace::observeTextEditors instead") - - callback(editor) for editor in @getEditors() - @subscribe this, 'editor-created', (editor) -> callback(editor) - - Workspace::getEditors = -> - deprecate("Use Workspace::getTextEditors instead") - - editors = [] - for pane in @paneContainer.getPanes() - editors.push(item) for item in pane.getItems() when item instanceof TextEditor - - editors - - Workspace::on = (eventName) -> - switch eventName - when 'editor-created' - deprecate("Use Workspace::onDidAddTextEditor or Workspace::observeTextEditors instead.") - when 'uri-opened' - deprecate("Use Workspace::onDidOpen or Workspace::onDidAddPaneItem instead. https://atom.io/docs/api/latest/Workspace#instance-onDidOpen") - else - deprecate("Subscribing via ::on is deprecated. Use documented event subscription methods instead.") - - super - - Workspace::reopenItemSync = -> - deprecate("Use Workspace::reopenItem instead") - if uri = @destroyedItemURIs.pop() - @openSync(uri) - - Workspace::registerOpener = (opener) -> - Grim.deprecate("Call Workspace::addOpener instead") - @addOpener(opener) - - Workspace::unregisterOpener = (opener) -> - Grim.deprecate("Call .dispose() on the Disposable returned from ::addOpener instead") - _.remove(@openers, opener) - - Workspace::getActiveEditor = -> - Grim.deprecate "Call ::getActiveTextEditor instead" - @getActivePane()?.getActiveEditor() - - Workspace::paneForUri = (uri) -> - deprecate("Use ::paneForURI instead.") - @paneForURI(uri) diff --git a/static/index.js b/static/index.js index 914290321..11527e963 100644 --- a/static/index.js +++ b/static/index.js @@ -64,9 +64,6 @@ ModuleCache.register(loadSettings) ModuleCache.add(loadSettings.resourcePath) - // Only include deprecated APIs when running core spec - require('grim').includeDeprecatedAPIs = isRunningCoreSpecs(loadSettings) - // Start the crash reporter before anything else. require('crash-reporter').start({ productName: 'Atom', @@ -184,14 +181,6 @@ }, false) } - function isRunningCoreSpecs (loadSettings) { - return !!(loadSettings && - loadSettings.isSpec && - loadSettings.specDirectory && - loadSettings.resourcePath && - path.dirname(loadSettings.specDirectory) === loadSettings.resourcePath) - } - parseLoadSettings() setupWindowBackground() })()