Merge branch 'master' into as-double-reflow-measurements

# Conflicts:
#	src/text-editor-presenter.coffee
This commit is contained in:
Antonio Scandurra
2015-09-28 11:19:56 +02:00
75 changed files with 1681 additions and 2965 deletions

View File

@@ -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

View File

@@ -6,6 +6,6 @@
"url": "https://github.com/atom/atom.git"
},
"dependencies": {
"atom-package-manager": "1.0.5"
"atom-package-manager": "1.1.1"
}
}

21
atom.sh
View File

@@ -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}

View File

@@ -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:

View File

@@ -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",

View File

@@ -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...)

View File

@@ -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))

View File

@@ -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')

View File

@@ -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 <atom@github.com>'
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)

View File

@@ -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)

View File

@@ -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: """

View File

@@ -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)

View File

@@ -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/)

View File

@@ -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

View File

@@ -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",

View File

@@ -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;

View File

@@ -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 <atom@github.com>
Description: <%= description %>
Atom is a free and open source text editor that is modern, approachable, and hackable to the core.

View File

@@ -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/

View File

@@ -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'],

View File

@@ -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"

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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'}
]
}
]

View File

@@ -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", ->

View File

@@ -1,3 +1,4 @@
{Git} = require 'atom'
{deprecate} = require 'grim'
deprecate('Fake task deprecation')
module.exports = ->

View File

@@ -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 ->

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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]

View File

@@ -156,8 +156,6 @@ beforeEach ->
spyOn(clipboard, 'writeText').andCallFake (text) -> clipboardContent = text
spyOn(clipboard, 'readText').andCallFake -> clipboardContent
spyOn(atom.packages, 'uninstallAutocompletePlus')
addCustomMatchers(this)
afterEach ->

View File

@@ -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) ->

View File

@@ -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)

View File

@@ -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'}

View File

@@ -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')

View File

@@ -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()

View File

@@ -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")

View File

@@ -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()

View File

@@ -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)

View File

@@ -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

View File

@@ -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()

View File

@@ -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)

View File

@@ -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)?

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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()

View File

@@ -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)

View File

@@ -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)

View File

@@ -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

View File

@@ -1,5 +1,4 @@
{CompositeDisposable} = require 'event-kit'
Grim = require 'grim'
_ = require 'underscore-plus'
module.exports =

View File

@@ -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()

View File

@@ -1,6 +1,5 @@
path = require 'path'
{CompositeDisposable} = require 'event-kit'
Grim = require 'grim'
class PaneElement extends HTMLElement
attached: false

View File

@@ -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

View File

@@ -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)

View File

@@ -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()

View File

@@ -1,4 +0,0 @@
{Subscriber} = require 'emissary'
SubscriberMixin = componentDidUnmount: -> @unsubscribe()
Subscriber.extend(SubscriberMixin)
module.exports = SubscriberMixin

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)

View File

@@ -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) ->

View File

@@ -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)

View File

@@ -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()
})()